Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据
来源:互联网 发布:2017年海关数据 编辑:程序博客网 时间:2024/06/05 02:09
在Java中,如果想创建一个RSA私钥对象,常见的办法有三种:
1、由PKCS8格式的Encoded私钥数据创建
2、由pfx12格式的证书数据创建
3、直接用私钥模和指数数据创建
对于第一种方法,常用于Java语言内部、或者是OpenSSL库之间。
对于第二种方法,是比较直接的,直接从含有私钥的证书中获取私钥。
对于第三种方法,是由最原始的数据构造私钥对象,那么该方法也适用和CSP之间交换私钥数据。
下面针对这三种不同的方法,介绍实现过程:
一、由PKCS8格式的Encoded数据创建
如果知道私钥的PKCS8格式的编码数据(Java本身导出的私钥数据就是该格式,OpenSSL库也支持该格式的私钥输出),那么可以使用下面的代码创建私钥对象。
/** 由Encoded私钥数据(PKCS8格式)构造私钥对象,私钥数据用Base64编码 */ public static RSAPrivateKey CreatePrivateKeyFromString(String base64EncodedPriKey) throws Exception { /**将证书内容解码成二进制**/ Base64.Decoder decoder = Base64.getDecoder(); byte[] encodedPrikeyData = decoder.decode(base64EncodedPriKey); /**构造私钥对象**/ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(encodedPrikeyData); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8KeySpec); }
二、由Pfx12证书数据创建
如果已知私钥所在的Pfx12证书,那么可以直接通过该证书获取私钥对象。当然,同时需要知道该Pfx12证书的私钥保护密码。相关代码如下:
/** 由Pfx证书创建私钥对象,Pfx证书内容用Base64编码 */ public static RSAPrivateKey CreatePrivateKeyFromCert(String base64PfxCert, String alias, String pin) throws Exception{ /**将证书内容解码成二进制**/ Base64.Decoder decoder = Base64.getDecoder(); byte[] certData = decoder.decode(base64PfxCert); ByteArrayInputStream in = new ByteArrayInputStream(certData); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(in, pin.toCharArray()); RSAPrivateKey priKey = (RSAPrivateKey)ks.getKey(alias, pin.toCharArray()); return priKey; }注意:其中参数alias为证书的别名,也就是证书“使用者”中的“CN”字段。
三、由模和指数数据创建
如果想要把CSP中导出的私钥数据,传入Java代码使用,那么就只能使用这种方法。需要注意的是:Windows CryptoAPI导出的数据为大端模式,而Java的数据是以小端存放,故需要转化之后才能传入Java。具体做法如下:
1、通过CryptoAPI函数CryptExportKey()导出私钥数据,前提是该密钥对生成(或导入)时使用了CRYPT_EXPORTABLE标记,否则私钥是不能导出;
2、解读私钥数据快,得到模和私钥指数数据;
3、将数据由大端模式转化为小端模式;
4、构造Java的RSA私钥对象。
CryptoAPI导出私钥数据的代码如下:
DWORD ExportPriKey(HCRYPTPROV hProv, DWORD dwKeyUsage, LPBYTE lpbtPriKey, LPDWORD lpdwLen){DWORD dwError = 0;DWORD dwDataLen = 0;HCRYPTKEY hKeyPair = NULL;if (!hProv || !lpdwLen){return 1;}//获取密钥对句柄if (!CryptGetUserKey(hProv, dwKeyUsage, &hKeyPair) || !hKeyPair){dwError = GetLastError();return dwError;}//导出密钥对中的私钥数据长度if (!CryptExportKey(hKeyPair, 0, PRIVATEKEYBLOB, 0, NULL, &dwDataLen)){dwError = GetLastError();goto FREE_MEMORY;}//返回数据长度if (!lpbtPriKey){dwError = 0;*lpdwLen = dwDataLen;goto FREE_MEMORY;}//导出密钥对中的私钥数据if (!CryptExportKey(hKeyPair, 0, PRIVATEKEYBLOB, 0, lpbtPriKey, &dwDataLen)){dwError = GetLastError();goto FREE_MEMORY;}*lpdwLen = dwDataLen;FREE_MEMORY:if (hKeyPair){CryptDestroyKey(hKeyPair);hKeyPair = NULL;}return dwError;}
CryptoAPI到处的私钥数据块有BLOBHEADER+RSAPUBKEY+私钥数据组成。下图为一个私钥数据块的内容解析:
基于这个私钥数据块的结构,我们可以使用下面的代码来解读各个项的具体数据:
/* CSP导出的私钥数据 */static BYTE pbPvk[] = {0x07, 0x02, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x52, 0x53, 0x41, 0x32, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xe3, 0xa9, 0x4a, 0x3b, 0x0d, 0x85, 0xaa, 0x32, 0x3a, 0x49, 0xcd, 0x37, 0x38, 0xb1, 0x93, 0xa1, 0x44, 0x57, 0x5a, 0xb2, 0x73, 0x5d, 0xc1, 0x96, 0xb4, 0x94, 0xe0, 0xc4, 0x83, 0x33, 0x9a, 0x48, 0x1e, 0x34, 0x2e, 0xd0, 0x54, 0x8a, 0xab, 0x5a, 0x2f, 0x6b, 0x65, 0x22, 0xc5, 0x9d, 0x6c, 0x49, 0xde, 0x8c, 0xc3, 0x71, 0xe3, 0x22, 0x1a, 0x9a, 0xfb, 0xf6, 0x5d, 0x7c, 0x8c, 0xca, 0x45, 0xe0, 0xc7, 0x74, 0x19, 0xfd, 0x63, 0x07, 0x52, 0x8f, 0xc7, 0xfa, 0xdf, 0x4f, 0x50, 0x7e, 0xb3, 0x8e, 0x2b, 0x53, 0x68, 0x16, 0x5c, 0x0b, 0x26, 0xae, 0x5b, 0xd9, 0xe2, 0x47, 0x51, 0xef, 0x65, 0xcb, 0x82, 0xe9, 0x18, 0xc3, 0x37, 0x78, 0xad, 0x67, 0x25, 0x1f, 0x0f, 0xea, 0x0b, 0x88, 0x04, 0xd2, 0xad, 0xc6, 0x82, 0x3b, 0x6c, 0x75, 0x08, 0xb8, 0xab, 0x68, 0xc6, 0x48, 0x9d, 0x35, 0xd0, 0x8e, 0xc1, 0x69, 0xeb, 0xf9, 0xec, 0xb3, 0x29, 0xf8, 0x9a, 0x30, 0x02, 0x4f, 0x99, 0xb1, 0xd1, 0x24, 0xe7, 0x77, 0xfa, 0xb5, 0x12, 0xbc, 0x8b, 0xab, 0xa6, 0xfc, 0x23, 0x49, 0x08, 0x0f, 0x76, 0x0b, 0xb0, 0x24, 0x4e, 0x6c, 0x78, 0x2b, 0xc9, 0xe1, 0x26, 0x7c, 0x21, 0x95, 0xa7, 0xd4, 0x7a, 0x7d, 0x36, 0x39, 0x1b, 0x75, 0x3c, 0xc7, 0x2a, 0xd8, 0x18, 0xea, 0x26, 0x25, 0x3a, 0xf7, 0x50, 0xc9, 0xa3, 0x54, 0x73, 0xda, 0xda, 0x68, 0xde, 0x36, 0x64, 0x50, 0x6f, 0x10, 0x8e, 0x47, 0xfe, 0xe6, 0x04, 0x07, 0x00, 0x42, 0x6e, 0xc9, 0x10, 0xce, 0x31, 0x5f, 0x19, 0x6b, 0xf0, 0xea, 0x17, 0xc7, 0xf6, 0x3e, 0xa7, 0x3c, 0x12, 0x4f, 0x47, 0x91, 0x59, 0xd8, 0x44, 0x56, 0x81, 0x60, 0x9d, 0x40, 0x15, 0x8a, 0x63, 0xf1, 0x83, 0xab, 0x51, 0x8c, 0x93, 0x03, 0x13, 0x6f, 0xe6, 0x17, 0x9b, 0xb5, 0xc1, 0xeb, 0x33, 0xba, 0xa1, 0x79, 0x3b, 0x29, 0x10, 0x8f, 0xd5, 0x20, 0x5c, 0x9a, 0x36, 0x5c, 0x93, 0x3a, 0x8b, 0x42, 0x32, 0x3f, 0x6e, 0x3f, 0xa7, 0x6d, 0xeb, 0x92, 0x35, 0x4f, 0x07, 0xc0, 0x84, 0xab, 0x9b, 0x5f, 0x1b, 0x89, 0xda, 0xca, 0x4a, 0x2d, 0x1c, 0xec, 0x58, 0x83, 0xec, 0x68, 0x9f, 0x86, 0x9e, 0xe1, 0x27, 0x06, 0x84, 0x45, 0x7f, 0xf3, 0xb5, 0x2b, 0x4d, 0xbc, 0xd5, 0x92, 0xab, 0x21, 0x28, 0x82, 0x00, 0x44, 0xd3, 0x0c, 0xb2, 0x90, 0x73, 0x38, 0xc5, 0x7c, 0x6b, 0x50, 0xb4, 0xbb, 0x40, 0x14, 0x69, 0xac, 0xdb, 0x18, 0x3d, 0xc7, 0x4a, 0x98, 0x4a, 0x85, 0x4d, 0x6a, 0xf9, 0xbc, 0x79, 0x60, 0xda, 0x64, 0x64, 0x35, 0xe5, 0x06, 0x0d, 0x95, 0xef, 0x0f, 0x49, 0xc1, 0x36, 0x7a, 0xa9, 0xf5, 0x83, 0x1c, 0xe2, 0xef, 0xd3, 0x18, 0x54, 0xf5, 0xee, 0xcf, 0x20, 0x9a, 0x17, 0x45, 0x32, 0xa0, 0x7c, 0x34, 0x30, 0xd5, 0x44, 0x98, 0x86, 0x5a, 0xf9, 0x2a, 0x31, 0x33, 0xfc, 0x18, 0xac, 0x2c, 0xda, 0x40, 0x0a, 0x9a, 0x98, 0x87, 0xe8, 0xbc, 0x1e, 0xaa, 0x6e, 0xb3, 0x01, 0x5d, 0xd9, 0x5e, 0x0c, 0x37, 0x99, 0x19, 0xde, 0x06, 0x68, 0xe6, 0xba, 0x7d, 0x3b, 0x19, 0x52, 0x7e, 0x5b, 0x7a, 0x23, 0x27, 0x76, 0x4e, 0x25, 0x13, 0xf7, 0x74, 0x26, 0xd6, 0x49, 0x48, 0x01, 0x02, 0x12, 0x38, 0xde, 0x1f, 0x62, 0xda, 0x4b, 0x0e, 0x46, 0x2b, 0x41, 0xf5, 0x02, 0xd0, 0xc1, 0xe4, 0xe7, 0xfa, 0x18, 0x57, 0x6c, 0xd5, 0x7b, 0xda, 0xda, 0x6e, 0xc0, 0x5c, 0xe5, 0x59, 0xb2, 0x25, 0x75, 0x9b, 0x44, 0x90, 0xaf, 0xdc, 0x5d, 0xa1, 0xcc, 0x73, 0x63, 0x21, 0x7e, 0xab, 0xa9, 0x6d, 0x87, 0xf3, 0x26, 0xf2, 0x30, 0x42, 0x6b, 0xda, 0xf0, 0xa3, 0x89, 0x92, 0xba, 0x29, 0x2c, 0xc3, 0x88, 0x80, 0x06, 0x43, 0x74, 0xd8, 0xe4, 0x4b, 0x20, 0x3e, 0x91, 0xb0, 0x83, 0x1b, 0x7a, 0x29, 0x68, 0xcd, 0x4c, 0xa5, 0x40, 0x75, 0xed, 0x53, 0x0b, 0xb3, 0x76, 0x8d, 0xc4, 0x92, 0x12, 0x8b, 0x7a, 0x1e, 0xb7, 0x7e, 0xd0, 0xac, 0xfb, 0xfb, 0xf3, 0xca, 0xef, 0xbd, 0x20, 0x61, 0x0f, 0xd9, 0x89, 0x3a, 0x8b, 0x7b, 0x1c, 0xa9, 0x83, 0x49, 0xdd, 0xd6, 0x60, 0x2c, 0xd7, 0x43, };int _tmain(int argc, _TCHAR* argv[]){ULONG ulIndex = 0;BLOBHEADER blobheader = {0};RSAPUBKEY rsapubkey = {0};BYTE modulus[128] = {0};BYTE prime1[64] = {0};BYTE prime2[64] = {0};BYTE exponent1[64] = {0};BYTE exponent2[64] = {0};BYTE coefficient[64] = {0};BYTE privateExponent[128] = {0};//块头数据memcpy(&blobheader, pbPvk, sizeof(blobheader));ulIndex += sizeof(blobheader);//公钥数据memcpy(&rsapubkey, pbPvk + ulIndex, sizeof(RSAPUBKEY));ulIndex += sizeof(RSAPUBKEY);//模memcpy(modulus, pbPvk + ulIndex, rsapubkey.bitlen/8);ulIndex += rsapubkey.bitlen/8;//memcpy(prime1, pbPvk + ulIndex, rsapubkey.bitlen/16);ulIndex += rsapubkey.bitlen/16;//memcpy(prime2, pbPvk + ulIndex, rsapubkey.bitlen/16);ulIndex += rsapubkey.bitlen/16;//memcpy(exponent1, pbPvk + ulIndex, rsapubkey.bitlen/16);ulIndex += rsapubkey.bitlen/16;//memcpy(exponent2, pbPvk + ulIndex, rsapubkey.bitlen/16);ulIndex += rsapubkey.bitlen/16;//memcpy(coefficient, pbPvk + ulIndex, rsapubkey.bitlen/16);ulIndex += rsapubkey.bitlen/16;//私钥指数memcpy(privateExponent, pbPvk + ulIndex, rsapubkey.bitlen/8);ulIndex += rsapubkey.bitlen/8;return 0;}
记住:要将模和私钥指数数据转化为小端模式:
BYTE btTemp = 0;for (ULONG i = 0; i < rsapubkey.bitlen/8 / 2; i++){btTemp = modulus[i];modulus[i] = modulus[rsapubkey.bitlen/8 - (i + 1)];modulus[rsapubkey.bitlen/8 - ( i + 1)] = btTemp;//btTemp = privateExponent[i];privateExponent[i] = privateExponent[rsapubkey.bitlen/8 - (i + 1)];privateExponent[rsapubkey.bitlen/8 - ( i + 1)] = btTemp;}然后为了方便Java代码生成私钥对象,将模和私钥指数都按16进制字符串打印,结果如下:
模数据:
8ED0359D48C668ABB808756C3B82C6ADD204880BEA0F1F2567AD7837C318E982CB65EF5147E2D95BAE260B5C1668532B8EB37E504FDFFAC
78F520763FD1974C7E045CA8C7C5DF6FB9A1A22E371C38CDE496C9DC522656B2F5AAB8A54D02E341E489A3383C4E094B496C15D73B25A57
44A193B13837CD493A32AA850D3B4AA9E3
私钥指数数据:
43D72C60D6DD4983A91C7B8B3A89D90F6120BDEFCAF3FBFBACD07EB71E7A8B1292C48D76B30B53ED7540A54CCD68297A1B83B0913E204B
E4D87443068088C32C29BA9289A3F0DA6B4230F226F3876DA9AB7E216373CCA15DDCAF90449B7525B259E55CC06EDADA7BD56C5718FAE7
E4C1D002F5412B460E4BDA621FDE38120201
好了,现在在Java代码中,就可以由模和私钥指数数据创建私钥对象了。具体代码如下:
/** * Created by Singler on 2015/10/12. * RSA加解密实现类 * */public class RSA { /** 算法 */ private static String ALGORITHM = "RSA"; /** RSA bits */ private static int KEYSIZE = 1024; /** 由模和指数构造私钥对象,模和指数由16进制字符串表示 */ public static RSAPrivateKey CreatePrivateKeyFromModulus(String modulusIn16Radix, String exponentIn16Radix) throws Exception { BigInteger m = new BigInteger(modulusIn16Radix, 16); BigInteger e = new BigInteger(exponentIn16Radix, 16); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e); RSAPrivateKey rsaPriKey = (RSAPrivateKey)keyFactory.generatePrivate(keySpec); return rsaPriKey; }}public class Main { static final String priKeyModulusInHex = "8ED0359D48C668ABB808756C3B82C6ADD204880BEA0F1F2567AD7837C318E982CB65EF5147E2D95BAE260B5C1668532B8EB37E504FDFFAC78F520763FD1974C7E045CA8C7C5DF6FB9A1A22E371C38CDE496C9DC522656B2F5AAB8A54D02E341E489A3383C4E094B496C15D73B25A5744A193B13837CD493A32AA850D3B4AA9E3"; static final String prikeyExpInHex = "43D72C60D6DD4983A91C7B8B3A89D90F6120BDEFCAF3FBFBACD07EB71E7A8B1292C48D76B30B53ED7540A54CCD68297A1B83B0913E204BE4D87443068088C32C29BA9289A3F0DA6B4230F226F3876DA9AB7E216373CCA15DDCAF90449B7525B259E55CC06EDADA7BD56C5718FAE7E4C1D002F5412B460E4BDA621FDE38120201"; public static void main(String[] args) { try { PrivateKey priKey = RSA.CreatePrivateKeyFromModulus(priKeyModulusInHex, prikeyExpInHex); byte[] priKeyData = priKey.getEncoded(); String priKeyStr = Base64.getEncoder().encodeToString(priKeyData); System.out.println(priKeyStr); } catch (Exception e) { System.out.println(e.getMessage()); }}}上面Java代码创建私钥对象成功后,将私钥数据的PKCS8编码内容以Base64格式输出如下:
MIIBNgIBADANBgkqhkiG9w0BAQEFAASCASAwggEcAgEAAoGBAI7QNZ1IxmiruAh1bDuCxq3SBIgL6g8fJWeteDfDGOmCy2XvUUfi2VuuJgtcFmhTK46zf
lBP3/rHj1IHY/0ZdMfgRcqMfF32+5oaIuNxw4zeSWydxSJlay9aq4pU0C40HkiaM4PE4JS0lsFdc7JaV0Shk7E4N81JOjKqhQ07SqnjAgEAAoGAQ9csYNbdS
YOpHHuLOonZD2Egve/K8/v7rNB+tx56ixKSxI12swtT7XVApUzNaCl6G4OwkT4gS+TYdEMGgIjDLCm6komj8NprQjDyJvOHbamrfiFjc8yhXdyvkESbdSWy
WeVcwG7a2nvVbFcY+ufkwdAC9UErRg5L2mIf3jgSAgECAQACAQACAQACAQACAQA=
- Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据
- Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据
- Java与CSP数据兼容之一:Java兼容CSP导出的RSA公钥数据
- Java与CSP数据兼容之一:Java兼容CSP导出的RSA公钥数据
- Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
- Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
- CSP之 Markdown java
- CSP 之 权限查询 java
- Java之excel导入数据同时兼容.xls和.xlsx
- CSP
- CSP
- java-csp-股票波动问题
- java-csp-折点计数
- CSP的今世与未来
- CSP应用开发-数据加解密
- 适用于 Java 程序员的 CSP,第 1 部分
- 适用于 Java 程序员的 CSP ,第 2 部分
- CCF-CSP 最大的矩形 201312-3 JAVA
- python在linux(anaconda)的图形界面(snack)
- TCP/IP学习笔记-面试小结
- 在Eclipse添加Android兼容包(v4/v7appcompat)
- 图像处理之快速均值模糊(Box Blur)
- 开速开发:UIView与XIB关联
- Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据
- eclipse快捷键
- VS2005编译问题LINK : E:/aaa/Debug/aaa.exe not found or not built by the last incremental link; performin
- MAC更改原有用户账户密码
- java基础学习笔记——vector
- F5网络虚拟化Windows Server的应用交付
- python下sched实现延时或者循环的用法
- Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
- 移动互联网实时视频通讯之视频采集