C#与Java的RSA中的X509EncodedKeySpec、PKCS8EncodedKeySpec
1、JAVA - RSA使用X509EncodedKeySpec、PKCS8EncodedKeySpec生成公钥和私钥
private static final String KEY_ALGORITHM = "RSA"; private static final String PUBLIC_KEY ="publicKey"; private static final String PRIVATE_KEY ="privateKey"; public static void main(String[] args) throws Exception{ Map<String,String> keyMap = genKey(); RSAPublicKey publicKey = getPublicKey(keyMap.get(PUBLIC_KEY)); RSAPrivateKey privateKey = getPrivateKey(keyMap.get(PRIVATE_KEY)); String info ="明文123456"; //加密 byte[] bytes = encrypt(info.getBytes("utf-8"),publicKey); //解密 bytes = decrypt(bytes, privateKey); System.out.println(new String(bytes,"utf-8")); } public static Map<String,String> genKey() throws NoSuchAlgorithmException{ Map<String,String> keyMap = new HashMap<String,String>(); KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM); SecureRandom random = new SecureRandom(); // random.setSeed(keyInfo.getBytes()); // 初始加密,512位已被破解,用1024位,最好用2048位 keygen.initialize(1024, random); // 取得密钥对 KeyPair kp = keygen.generateKeyPair(); RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate(); String privateKeyString = Base64.encode(privateKey.getEncoded()); RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic(); String publicKeyString = Base64.encode(publicKey.getEncoded()); keyMap.put(PUBLIC_KEY, publicKeyString); keyMap.put(PRIVATE_KEY, privateKeyString); return keyMap; } public static RSAPublicKey getPublicKey(String publicKey) throws Exception{ byte[] keyBytes = LBase64.decode(publicKey); X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return (RSAPublicKey) keyFactory.generatePublic(spec); } public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception{ byte[] keyBytes = LBase64.decode(privateKey); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return (RSAPrivateKey) keyFactory.generatePrivate(spec); }
2、C# - RSA使用X509EncodedKeySpec、PKCS8EncodedKeySpec生成公钥和私钥
/*********** Java代码 ***********/ //System.out.println(bytes2hex(publicKey.getModulus().toByteArray())); //System.out.println(bytes2hex(publicKey.getPublicExponent().toByteArray())); //设定RSA参数,用于指定modulus和exponent var parameter = new RSAParameters(); parameter.Modulus = modulusBytes; //modulusBytes是转化后的byte[] parameter.Exponent = exponentBytes; //加密 var rsa = RSACryptoServiceProvider.Create("RSA"); rsa.ImportParameters(parameter); byte[] result = rsa.EncryptValue(Encoding.UTF8.GetBytes("PASSWORD")); //把密文转化为HEX形式 string resultHex = BitConverter.ToString(result).Replace("-", ""); #if 0 //把3个变量转化为System.Numerics.BigInteger var modulus = new BigInteger(modulusBytes); var exponent = new BigInteger(exponentBytes); var data = new BigInteger(Encoding.UTF8.GetBytes("PASSWORD")); //做ModPow运算得到密文,也是BigInteger var result = BigInteger.ModPow(data, exponent, modulus); //把密文BigInteger对应的byte[]转化为HEX形式 string resultHex = BitConverter.ToString(result.ToByteArray()).Replace("-", ""); #endif //把3个变量转化为System.Numerics.BigInteger var modulus = new BigInteger(modulusBytes.Reverse().ToArray()); var exponent = new BigInteger(exponentBytes.Reverse().ToArray()); var data = new BigInteger(Encoding.UTF8.GetBytes("PASSWORD").Reverse().ToArray()); //做ModPow运算得到密文,也是BigInteger var result = BigInteger.ModPow(d, e, m); //把密文BigInteger对应的byte[]转化为HEX形式 string resultHex = BitConverter.ToString(result.ToByteArray().Reverse().ToArray()).Replace("-", "");
3、C# - JAVA - RSA使用X509EncodedKeySpec、PKCS8EncodedKeySpec生成公钥和私钥解析
1. Java程序员很少想过getEncoded()是什么算法,它是如何把modulus与publicExponent封装到一个byte[]里去的。
2. 而对于C#程序员来讲,RSA公钥加密必须要用到modulus与publicExponent,无论它们是XML形式还是byte[]形式,因此如何从publicKeyHex中解析这两个参数就成了第一个关键点。
3. JavaDoc:
An Encoded Form This is an external encoded form for the key used when a standard representation of the key is needed outside the Java Virtual Machine, as when transmitting the key to some other party. The key is encoded according to a standard format (such as X.509 SubjectPublicKeyInfo or PKCS#8), and is returned using the getEncoded method. Note: The syntax of the ASN.1 type SubjectPublicKeyInfo is defined as follows: SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } For more information, see RFC 3280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile.
根据ASN.1标准进行保存的,涉及到了2种格式:X.509 SubjectPublicKeyInfo和PKCS#8, 具体是哪种可以从getFormat()的方法说明里可以找到答案:公钥使用的是X.509 SubjectPublicKeyInfo,私钥使用的是PKCS#8。
JavaDoc里已经描述了SubjectPublicKeyInfo的结构如下:
SEQUENCE { SEQUENCE { //algorithm,我们不用关心具体结构 } BIT STRING { //SubjectPublicKey,以BIT STRING存储 SEQUENCE { INTEGER //modulus INTEGER //publicExponent } } }
Java端使用的是2048位的标准RSA加密,给出的公钥HEX字符串如下:
30820122300D06092A864886F70D01010105000382010F003082010A02820101008C214751E6EA33378080F64BF55C0888D3EFA4DF08794318069DDFD14A3AB6468B20CD134819100FA20539785AECF595CF2333F7ADC48366F4ACBC41B1CED728B57417CF3B6CA4E7DDB9DA348F7D38158DD6F2FF3934AEB0A70732E2949505EF893A940404B1B5F4B69243E2877BBA90E5994EBFD61986F412DE4AD3E8331CE1D3D41ADAEF5C79D5B22E05C7F76FC748BC5FA42345D70EC3D1DE3DBD338C300C3750841E2E16E7B907E536FCA1A40D05DC9DFCDE4EB2E8575228309AD146486E6F21C386E90C36DEECB57F955CE68609204AFBD434F8A1BFB5D921C470EED82CCA8BFDA92A8EC668E9E9EB6F959CD535C8BCCFCB08A671983A27E8B03F5BF90D0203010001
对照着ASN.1规范,格式如下:
二进制 解析 -------------------------------------------------------------- 30 0011 0000 TAG:类型00=通用,1=结构体,10000=16=SEQUENCE 82 1000 0010 LEN:定长,长形式,后面2字节是长度 01 长度 0x122=290字节,注意是大字节序 22 30 0011 0000 TAG:类型00=通用,1=结构体,10000=16=SEQUENCE 0D 0000 1101 LEN:定长,短形式,13字节 06 VAL:13字节,我们不关心内容,跳过 09 . . . 05 00 03 0000 0011 TAG:类型00=通用类型,0=简单数据,00011=3=BIT STRING 82 1000 0010 LEN:定长,长形式,后面2字节是长度,注意是大字节序 01 长度 0x10f=271字节 0F 00 保留字 30 0011 0000 TAG:类型00=通用类型,1=结构体,10000=16=SEQUENCE 82 1000 0010 LEN:定长,长形式,后面2字节是长度,注意是大字节序 01 长度 0x10a=266字节 0A 02 0000 0010 TAG:类型00,0=简单数据,00010=2=INTEGER 82 1000 0010 LEN:定长,长形式,后面2字节是长度,注意是大字节序 01 长度=0x101,257字节 01 00 VAL:257字节,是公钥中的modulus 8C . . . F9 0D 02 0000 0010 TAG:类型00,0=简单数据,00010=2=INTEGER 03 0000 0011 LEN:定长,短形式,3字节 01 00 01 VAL:3字节,是公钥中的publicExponent