Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据

来源:互联网 发布:火星哥 知乎 编辑:程序博客网 时间:2024/06/05 16:54
在Java中,如果想创建一个RSA私钥对象,常见的办法有三种:

1、由PKCS8格式的Encoded私钥数据创建

2、由pfx12格式的证书数据创建

3、直接用私钥模和指数数据创建

对于第一种方法,常用于Java语言内部、或者是OpenSSL库之间。

对于第二种方法,是比较直接的,直接从含有私钥的证书中获取私钥。

对于第三种方法,是由最原始的数据构造私钥对象,那么该方法也适用和CSP之间交换私钥数据。

      下面针对这三种不同的方法,介绍实现过程:

一、由PKCS8格式的Encoded数据创建

      如果知道私钥的PKCS8格式的编码数据(Java本身导出的私钥数据就是该格式,OpenSSL库也支持该格式的私钥输出),那么可以使用下面的代码创建私钥对象。

   
  1. /** 由Encoded私钥数据(PKCS8格式)构造私钥对象,私钥数据用Base64编码 */  
  2. public static RSAPrivateKey CreatePrivateKeyFromString(String base64EncodedPriKey)  throws Exception {  
  3.     /**将证书内容解码成二进制**/  
  4.     Base64.Decoder decoder = Base64.getDecoder();  
  5.     byte[] encodedPrikeyData = decoder.decode(base64EncodedPriKey);  
  6.   
  7.   
  8.     /**构造私钥对象**/  
  9.     PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(encodedPrikeyData);  
  10.     KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);  
  11.   
  12.     return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8KeySpec);  
  13.  } 

二、由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=