Java与CSP数据兼容之一:Java兼容CSP导出的RSA公钥数据
来源:互联网 发布:无印良品淘宝旗舰店 编辑:程序博客网 时间:2024/05/16 13:58
下面将讲述Java中这三种生成RSA公钥对象的方法:
一、通过X509证书创建
如果已知公钥所在的证书文件(X509格式),那么可以直接通过证书获取对应的公钥对象。其相关代码如下:
/** * Created by Singler on 2015/10/12. * RSA加解密实现类 * */ public class RSA { /** 算法 */ private static String ALGORITHM = "RSA"; /** RSA bits */ private static int KEYSIZE = 1024; /** 由X509格式的证书内容创建公钥对象,证书内容用Base64编码 **/ public static RSAPublicKey CreatePublicKeyFromCert(String base64X509Cert) throws Exception { /**将证书内容解码成二进制**/ Base64.Decoder decoder = Base64.getDecoder(); byte[] certData = decoder.decode(base64X509Cert); /**构造证书对象**/ CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509"); ByteArrayInputStream bain = new ByteArrayInputStream(certData); X509Certificate Cert = (X509Certificate)certificatefactory.generateCertificate(bain); /**获取公钥对象**/ RSAPublicKey pukKey = (RSAPublicKey)Cert.getPublicKey(); return pukKey; } } public class Main { static final String base64X509EncCert = "MIICfzCCAeygAwIBAgIQQpcVM898DJ9O2voeAMomJTAJBgUrDgMCHQUAMDcxGzAZBgNVBAoeEm1ZbF93AWVwW1eLwU5mTi1fwzEYMBYGA1UEAxMPWkpDQSBUZWNobm9sb2d5MB4XDTExMTIyMjAyNTkxMFoXDTM5MTIzMTIzNTk1OVowNzEbMBkGA1UECh4SbVlsX3cBZXBbV4vBTmZOLV/DMRgwFgYDVQQDEw9aSkNBIFRlY2hub2xvZ3kwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAI7QNZ1IxmiruAh1bDuCxq3SBIgL6g8fJWeteDfDGOmCy2XvUUfi2VuuJgtcFmhTK46zflBP3/rHj1IHY/0ZdMfgRcqMfF32+5oaIuNxw4zeSWydxSJlay9aq4pU0C40HkiaM4PE4JS0lsFdc7JaV0Shk7E4N81JOjKqhQ07SqnjAgMBAAGjgZMwgZAwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUEDDAKBggrBgEFBQcDAjBoBgNVHQEEYTBfgBA3FRkusjCPJCGc33EBFu0zoTkwNzEbMBkGA1UECh4SbVlsX3cBZXBbV4vBTmZOLV/DMRgwFgYDVQQDEw9aSkNBIFRlY2hub2xvZ3mCEEKXFTPPfAyfTtr6HgDKJiUwCQYFKw4DAh0FAAOBgQCHLkBcCbH4OGN9OpinUYmAGsjQWB42wYDjgvX1pG0BQEvI1dPp42/OzlNTKApKyCqNUDvcCIE1bS1g9+K277NOoeYupBdz2voqS4mAZKstGauCfseeu8y8tjedbRgvjHokyATFmX6EK7NcBucm4eNDTqU+oqnzg4ypAnGtJtYFZg=="; public static void main(String[] args) { try { PublicKey pubKey = RSA.CreatePublicKeyFromCert(base64X509EncCert); byte[] pubKeyData = pubKey.getEncoded(); String pubKeyStr = Convert.bytesToHexString(pubKeyData); System.out.println(pubKeyStr); } catch (Exception e) { System.out.println(e.getMessage()); } } }该RSA公钥的X509 Encoded数据以16进制输出结果如下:
/** * Created by Singler on 2015/10/12. * RSA加解密实现类 * */ public class RSA { /** 算法 */ private static String ALGORITHM = "RSA"; /** RSA bits */ private static int KEYSIZE = 1024; /** 由X509 Encoded公钥数据构造公钥对象,公钥数据用Base64编码 */ public static RSAPublicKey CreatePublicKeyFromString(String base64EncodedPubKey) throws Exception { /**将证书内容解码成二进制**/ Base64.Decoder decoder = Base64.getDecoder(); byte[] encodedPubkeyData = decoder.decode(base64EncodedPubKey); /**构造公钥对象**/ X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedPubkeyData); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); return (RSAPublicKey)keyFactory.generatePublic(x509EncodedKeySpec); } } public class Main { static String base64X509PubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw+D4pTERUemPtGIsterieWoTbRTNHgZ0DN9Z4qfrS03eWP339/JBkGyKwlBykCzYDt8L0YMW/1Xg1appYGvfo4mnVvxzIbc1tuanH1Z+wnXbrHqgW5ndlMjZ328mDyDIRHPmzA8FMYSvPGJtnrv4H9Tc+CeKrmiWskbzxBg4T/MUxYsk/+WWg6y5dUU61rwr19HyBZKq93xH8IOctgeRxsf6oItQJ1y1Nd55XVEul2Lh5KtTZzZMWAwreIq/EhZJH0XdbOeHhy78/fMmQtkKpJqLpRMTas1ZBWmVnqHxgBQcvheOu6cNGJYbadisJDk7Yo6MW3M7kRbybeU7C9yf7QIDAQAB"; public static void main(String[] args) { try { PublicKey pubKey = RSA.CreatePublicKeyFromString(base64X509PubKey); byte[] pubKeyData = pubKey.getEncoded(); String pubKeyStr = Convert.bytesToHexString(pubKeyData); System.out.println(pubKeyStr); } catch (Exception e) { System.out.println(e.getMessage()); } } }
三、通过Windows CryptoAPI导出的RSA公钥数据创建
有时RSA公钥数据来源于Windows客户端,Windows下常用的RSA函数来源于微软的CryptoAPI,其导出的RSA公钥数据为PUBLICKEYBLOB类型的数据块,数据块包含结构体BLOBHEADER和RSAPUBKEY。此时如果想把该RSA公钥应用于Java,就需要通过最原始的模和指数数据创建RSA公钥对象了。同时要注意,由于CrytoAPI的数据是大端模式,而Java里是小端模式,所以从CryptoAPI得到的模和指数都要经过转化才能在Java中使用。
具体过程如下:
1、使用CryptoAPI导出RSA公钥数据
2、得到公钥模和指数
3、大端到小端的转化
4、在Java中构造公钥对象
下面是C++调用CryptoAPI导出公钥数据的代码。
#include "stdafx.h" #include <windows.h> #include <atlconv.h> DWORD ExportPubKey(HCRYPTPROV hProv, DWORD dwKeyUsage, LPBYTE lpbtPubKey, LPDWORD lpdwLen); void Reverse(LPBYTE lpData, DWORD dwLen); void PrintDataInHex(LPBYTE lpData, DWORD dwLen); int _tmain(int argc, _TCHAR* argv[]) { DWORD dwErr = 0; DWORD dwFlag = CRYPT_FIRST; DWORD dwParamLen = 2048; BYTE btParamData[2048] = {0}; HCRYPTPROV hDefaultProv = NULL; HCRYPTPROV hProv = NULL; TCHAR *pContainer = NULL; const TCHAR tcCSPName[] = {_T("ZJCA CSP v1.0 for EnterSafe ePass3000gm")}; // DWORD dwPubKeyLen = 0; DWORD dwModulusLen = 0; LPBYTE lpbtPubKey = NULL; LPBYTE lpbtModulus = NULL; BYTE btPubexp[4] = {0}; BLOBHEADER *pBlobHeader = NULL; RSAPUBKEY *pRSAPubKey = NULL; USES_CONVERSION; if (!CryptAcquireContext(&hDefaultProv, NULL, tcCSPName, PROV_RSA_FULL, 0)) { DWORD dwErr = GetLastError(); printf("函数CryptAcquireContext失败! dwError = 0x%x\n", dwErr); goto END; } //获取CSP所有的容器名称,找到加密密钥所在容器 while (CryptGetProvParam(hDefaultProv, PP_ENUMCONTAINERS, btParamData, &dwParamLen, dwFlag)) { dwFlag = CRYPT_NEXT; #ifdef UNICODE pContainer = A2W((char*)btParamData); #else pContainer = btParamData; #endif if (CryptAcquireContext(&hProv, pContainer, tcCSPName, PROV_RSA_FULL, 0)) { HCRYPTKEY hKeyPair = NULL; if (CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKeyPair) && hKeyPair) { CryptDestroyKey(hKeyPair); break; } } } if (!hProv) { printf("Not found keyset!\n"); goto END; } // 获取公钥数据长度 dwErr = ExportPubKey(hProv, AT_KEYEXCHANGE, NULL, &dwPubKeyLen); if (0 != dwErr) { printf("ExportPubKey() failed! dwErr = 0x%x\n", dwErr); goto END; } // 获取公钥数据 lpbtPubKey = new BYTE[dwPubKeyLen]; dwErr = ExportPubKey(hProv, AT_KEYEXCHANGE, lpbtPubKey, &dwPubKeyLen); if (0 != dwErr) { printf("ExportPubKey() failed! dwErr = 0x%x\n", dwErr); goto END; } // 输出公钥数据 printf("\nRSA public key:\n"); PrintDataInHex(lpbtPubKey, dwPubKeyLen); // 获取公钥模数据,并换成小端模式 pBlobHeader = (BLOBHEADER*)lpbtPubKey; pRSAPubKey = (RSAPUBKEY*)(lpbtPubKey + sizeof(BLOBHEADER)); dwModulusLen = pRSAPubKey->bitlen / 8; lpbtModulus = new BYTE[dwModulusLen]; memcpy(lpbtModulus, lpbtPubKey + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), dwModulusLen); Reverse(lpbtModulus, dwModulusLen); printf("\nRSA public key modulus:\n"); PrintDataInHex(lpbtModulus, dwModulusLen); // 获取公钥指数数据 memcpy(btPubexp, &pRSAPubKey->pubexp, 4); printf("\nRSA public key exp:\n"); PrintDataInHex(btPubexp, 4); END: if (lpbtModulus) { delete []lpbtModulus; lpbtModulus = NULL; } if (lpbtPubKey) { delete []lpbtPubKey; lpbtPubKey = NULL; } if (hDefaultProv) { CryptReleaseContext(hDefaultProv, 0); hDefaultProv = NULL; } if (hProv) { CryptReleaseContext(hProv, 0); hProv = NULL; } getchar(); return 0; } DWORD ExportPubKey(HCRYPTPROV hProv, DWORD dwKeyUsage, LPBYTE lpbtPubKey, 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, PUBLICKEYBLOB, 0, NULL, &dwDataLen)) { dwError = GetLastError(); goto FREE_MEMORY; } // 返回数据长度 if (!lpbtPubKey) { dwError = 0; *lpdwLen = dwDataLen; goto FREE_MEMORY; } // 导出密钥对中的公钥数据 if (!CryptExportKey(hKeyPair, 0, PUBLICKEYBLOB, 0, lpbtPubKey, &dwDataLen)) { dwError = GetLastError(); goto FREE_MEMORY; } *lpdwLen = dwDataLen; FREE_MEMORY: if (hKeyPair) { CryptDestroyKey(hKeyPair); hKeyPair = NULL; } return dwError; } void Reverse(LPBYTE lpData, DWORD dwLen) { BYTE temp = 0; for (ULONG i = 0; i < dwLen / 2; i++) { temp = lpData[i]; lpData[i] = lpData[dwLen - (i + 1)]; lpData[dwLen - ( i + 1)] = temp; } } void PrintDataInHex(LPBYTE lpData, DWORD dwLen) { for (DWORD dwIndex = 0; dwIndex < dwLen; dwIndex++) { printf("%02X ", lpData[dwIndex]); if ((dwIndex + 1) % 16 == 0) { printf("\n"); } } }程序输入结果如下:
下一步,需要把C++得到的模和指数数据,按16进制的字符串形式组织好,然后构造成RSA公钥对象。具体代码如下:
/** * 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 RSAPublicKey CreatePublicKeyFromModulus(String modulusIn16Radix, String exponentIn16Radix) throws Exception { BigInteger m = new BigInteger(modulusIn16Radix, 16); BigInteger e = new BigInteger(exponentIn16Radix, 16); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e); RSAPublicKey rsaPubKey = (RSAPublicKey)keyFactory.generatePublic(keySpec); return rsaPubKey; } } public class Main { static final String pubKeyModulusInHex = "C3E0F8A5311151E98FB4622CB5EAE2796A136D14CD1E06740CDF59E2A7EB4B4DDE58FDF7F7F241906C8AC25072902CD80EDF0BD18316FF55E0D5AA69606BDFA389A756FC7321B735B6E6A71F567EC275DBAC7AA05B99DD94C8D9DF6F260F20C84473E6CC0F053184AF3C626D9EBBF81FD4DCF8278AAE6896B246F3C418384FF314C58B24FFE59683ACB975453AD6BC2BD7D1F20592AAF77C47F0839CB60791C6C7FAA08B50275CB535DE795D512E9762E1E4AB5367364C580C2B788ABF1216491F45DD6CE787872EFCFDF32642D90AA49A8BA513136ACD590569959EA1F180141CBE178EBBA70D18961B69D8AC24393B628E8C5B733B9116F26DE53B0BDC9FED"; static final String pubKeyExpInHex = "01000100"; public static void main(String[] args) { try { PublicKey pubKey = RSA.CreatePublicKeyFromModulus(pubKeyModulusInHex, pubKeyExpInHex); byte[] pubKeyData = pubKey.getEncoded(); String pubKeyStr = Base64.getEncoder().encodeToString(pubKeyData); //Convert.bytesToHexString(pubKeyData); System.out.println(pubKeyStr); } catch (Exception e) { System.out.println(e.getMessage()); } } }
该段Java代码输入的X509 Encoded公钥数据如下:
MIIBIzANBgkqhkiG9w0BAQEFAAOCARAAMIIBCwKCAQEAw+D4pTERUemPtGIsterieWoTbRTNHgZ0DN9Z4qfrS03eWP339/JBkGyKwlBykCzYDt8L0YMW/1Xg1appYGvfo4mnVvxzIbc1tuanH1Z+wnXbrHqgW5ndlMjZ328mDyDIRHPmzA8FMYSvPGJtnrv4H9Tc+CeKrmiWskbzxBg4T/MUxYsk/+WWg6y5dUU61rwr19HyBZKq93xH8IOctgeRxsf6oItQJ1y1Nd55XVEul2Lh5KtTZzZMWAwreIq/EhZJH0XdbOeHhy78/fMmQtkKpJqLpRMTas1ZBWmVnqHxgBQcvheOu6cNGJYbadisJDk7Yo6MW3M7kRbybeU7C9yf7QIEAQABAA==
- 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
- CSP
- java-csp-股票波动问题
- java-csp-折点计数
- CSP 之 权限查询 java
- CSP的今世与未来
- CSP应用开发-数据加解密
- 适用于 Java 程序员的 CSP,第 1 部分
- 适用于 Java 程序员的 CSP ,第 2 部分
- CCF-CSP 最大的矩形 201312-3 JAVA
- 用java以正确的姿势刷CSP
- 子类继承父类—构造函数用法详解
- 有关分布式 MySQL 数据库中间件 MySQLDA,一文为你深入介绍~
- 你到底能有多聪明(智力)
- android 4.0.3 设置时间无法更新到RTC
- vue+ssr+axios
- Java与CSP数据兼容之一:Java兼容CSP导出的RSA公钥数据
- 【BZOJ2244】【SDOI2011】拦截导弹
- 【Scikit-Learn 中文文档】特征选择
- ubuntu下的caffe安装
- 学术论文地址总结
- React学习笔记_按需加载
- Spark算子:RDD基本转换操作(1)–map、flagMap、distinct
- 二叉树的遍历方法及递归实现
- 服务端开发之MySql数据库问题记录