非对称密钥RSA算法加解密在C#和Java之间交互的问题,这两天看了很多其他人写的文章,碰到了几个问题,最终解决问题。
参考地址:http://xw-z1985.iteye.com/blog/1837376
需求目的:完成c#请求端RSA加密(签名)问题,客户端采用C#开发,服务器端采用Java开发。服务器端给客户端提供私钥,进行数据加密(签名),客户端加密(签名)后提数据提交给服务器,服务器用公钥对数据解密,进行验证。
解决过程碰到的问题:
1.JAVA 需要的 RSA私钥 格式和 C# 需要的 RSA私钥 不一致。
JAVA 需要是 PKCS8格式私钥:
-----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOwuOHH/OIRE+0if mEPYGuhYRTyKdd6VLFIsNqy/SO5xZitHfA7xEymJKnpEUGgDJKr5zbFbytnWs5Jj gen6TXkUh9LG/fhPGGHdUVB42pAHv5yzoyEaOnJxBAxd6UstoWTaEgbT6GUbzMr/ Az25zuxw7c+skAlnUETVE5GL3tD7AgMBAAECgYEAxdNZODMctb3J9OSo93rV3vPA 2prna87rVtDt4vg+MGsPtwSjZyiKcmoQCGWcK+MmHYgrwHkwihKKSv3KXZ9or3xQ 2wNZGuTHLymWEzqfEfVb0igvxbe85EGwsaN3qSK62CK8vok/Bi+fZVa3UNCn0WFs lUS0qn+K3SECM9I1iwECQQD+2Pl2AJGQs2bRXSsnJk0FIwjpqdpGZFPlAUYaXkuT MqpwefP/bwwiuWqq9QIt2vAAKgy5T16tpPBcGpT6cvxBAkEA7T+i1gVwrXcozTuT 9oCwkF2MGBaXkv3mN9H/Pfy/oIhTsgiDxX8t+0KapAEQogvCuAOq19JvGw5e91H2 g0suOwJAJOMnCIuAhl9RTJCdxGbo0wuFKL0rGPFAq28JxJtNeRrmTK16QcjDCuun ouMf059TCdMMUG5Kl/u9xrcaRT4LgQJAZPiUYOnnzqvMHayhiGO0wXxOx2G2GMUe Wdtx+fu7wqLCnB6rlj5OX4U1M1+QqKbAtHg7Gadhye4P1Mp5U9+HSQJBANLVzcCX yAX2D12UPTPkhcNRaCRXFp3aJGMxI4iluUC+ukAdiapohqZ7vMQyWRq/tDyiwjir qMcg/AJIuQWmPyc= -----END PRIVATE KEY-----
C# 需要的是 PKCS1 格式私钥:
-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDsLjhx/ziERPtIn5hD2BroWEU8inXelSxSLDasv0jucWYrR3wO 8RMpiSp6RFBoAySq+c2xW8rZ1rOSY4Hp+k15FIfSxv34Txhh3VFQeNqQB7+cs6Mh GjpycQQMXelLLaFk2hIG0+hlG8zK/wM9uc7scO3PrJAJZ1BE1RORi97Q+wIDAQAB AoGBAMXTWTgzHLW9yfTkqPd61d7zwNqa52vO61bQ7eL4PjBrD7cEo2coinJqEAhl nCvjJh2IK8B5MIoSikr9yl2faK98UNsDWRrkxy8plhM6nxH1W9IoL8W3vORBsLGj d6kiutgivL6JPwYvn2VWt1DQp9FhbJVEtKp/it0hAjPSNYsBAkEA/tj5dgCRkLNm 0V0rJyZNBSMI6anaRmRT5QFGGl5LkzKqcHnz/28MIrlqqvUCLdrwACoMuU9eraTw XBqU+nL8QQJBAO0/otYFcK13KM07k/aAsJBdjBgWl5L95jfR/z38v6CIU7IIg8V/ LftCmqQBEKILwrgDqtfSbxsOXvdR9oNLLjsCQCTjJwiLgIZfUUyQncRm6NMLhSi9 KxjxQKtvCcSbTXka5kytekHIwwrrp6LjH9OfUwnTDFBuSpf7vca3GkU+C4ECQGT4 lGDp586rzB2soYhjtMF8TsdhthjFHlnbcfn7u8Kiwpweq5Y+Tl+FNTNfkKimwLR4 OxmnYcnuD9TKeVPfh0kCQQDS1c3Al8gF9g9dlD0z5IXDUWgkVxad2iRjMSOIpblA vrpAHYmqaIame7zEMlkav7Q8osI4q6jHIPwCSLkFpj8n -----END RSA PRIVATE KEY-----
2.私钥格式之间的转换问题
转换工具:openssl工具:http://www.slproweb.com/products/Win32OpenSSL.html
转换参考地址: http://blog.csdn.net/hanzengyi/article/details/78029104
java 代码 , 注意这里的私钥:是Pem私钥文件中去除头(-----BEGIN PRIVATE KEY-----)和尾(-----END PRIVATE KEY-----)以及换行符后的字符串
1 /** 2 * @data: 待加密的字符串 3 * @privateKey: 私钥 4 */ 5 public static String sign(byte[] data, String privateKey) throws Exception { 6 7 byte[] keyBytes = new BASE64Decoder().decodeBuffer(privateKey); 8 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); 9 10 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 11 12 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); 13 14 Signature signature = Signature.getInstance("SHA1withRSA"); 15 signature.initSign(priKey); 16 signature.update(data); 17 18 return byte2hex(signature.sign()); 19 }
c# 代码,注意这里的私钥:是Pem私钥文件中去除头(-----BEGIN RSA PRIVATE KEY-----)和尾(-----END RSA PRIVATE KEY-----)以及换行符后的字符串
1 /// <summary> 2 /// 签名 3 /// </summary> 4 /// <param name="data">待加密的字符串</param> 5 /// <param name="privateKey">私钥</param> 6 /// <returns></returns> 7 public static string Sign(string data, string privateKey) 8 { 9 RSACryptoServiceProvider rsaCsp = LoadCertificate(privateKey); 10 byte[] dataBytes = Encoding.UTF8.GetBytes(data); 11 byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1"); 12 return Hex_2To16(signatureBytes); 13 } 14 15 private static RSACryptoServiceProvider LoadCertificate(string privateKey) 16 { 17 byte[] res = res = Convert.FromBase64String(privateKey); 18 try 19 { 20 RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res); 21 return rsa; 22 } 23 catch (Exception ex) 24 { 25 } 26 return null; 27 } 28 29 private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) 30 { 31 byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; 32 33 // --------- Set up stream to decode the asn.1 encoded RSA private key ------ 34 MemoryStream mem = new MemoryStream(privkey); 35 BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading 36 byte bt = 0; 37 ushort twobytes = 0; 38 int elems = 0; 39 try 40 { 41 twobytes = binr.ReadUInt16(); 42 if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 43 binr.ReadByte(); //advance 1 byte 44 else if (twobytes == 0x8230) 45 binr.ReadInt16(); //advance 2 bytes 46 else 47 return null; 48 49 twobytes = binr.ReadUInt16(); 50 if (twobytes != 0x0102) //version number 51 return null; 52 bt = binr.ReadByte(); 53 if (bt != 0x00) 54 return null; 55 56 57 //------ all private key components are Integer sequences ---- 58 elems = GetIntegerSize(binr); 59 MODULUS = binr.ReadBytes(elems); 60 61 elems = GetIntegerSize(binr); 62 E = binr.ReadBytes(elems); 63 64 elems = GetIntegerSize(binr); 65 D = binr.ReadBytes(elems); 66 67 elems = GetIntegerSize(binr); 68 P = binr.ReadBytes(elems); 69 70 elems = GetIntegerSize(binr); 71 Q = binr.ReadBytes(elems); 72 73 elems = GetIntegerSize(binr); 74 DP = binr.ReadBytes(elems); 75 76 elems = GetIntegerSize(binr); 77 DQ = binr.ReadBytes(elems); 78 79 elems = GetIntegerSize(binr); 80 IQ = binr.ReadBytes(elems); 81 82 83 // ------- create RSACryptoServiceProvider instance and initialize with public key ----- 84 CspParameters CspParameters = new CspParameters(); 85 CspParameters.Flags = CspProviderFlags.UseMachineKeyStore; 86 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters); 87 RSAParameters RSAparams = new RSAParameters(); 88 RSAparams.Modulus = MODULUS; 89 RSAparams.Exponent = E; 90 RSAparams.D = D; 91 RSAparams.P = P; 92 RSAparams.Q = Q; 93 RSAparams.DP = DP; 94 RSAparams.DQ = DQ; 95 RSAparams.InverseQ = IQ; 96 RSA.ImportParameters(RSAparams); 97 return RSA; 98 } 99 catch (Exception ex) 100 { 101 return null; 102 } 103 finally 104 { 105 binr.Close(); 106 } 107 } 108 109 private static int GetIntegerSize(BinaryReader binr) 110 { 111 byte bt = 0; 112 byte lowbyte = 0x00; 113 byte highbyte = 0x00; 114 int count = 0; 115 bt = binr.ReadByte(); 116 if (bt != 0x02) //expect integer 117 return 0; 118 bt = binr.ReadByte(); 119 120 if (bt == 0x81) 121 count = binr.ReadByte(); // data size in next byte 122 else 123 if (bt == 0x82) 124 { 125 highbyte = binr.ReadByte(); // data size in next 2 bytes 126 lowbyte = binr.ReadByte(); 127 byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; 128 count = BitConverter.ToInt32(modint, 0); 129 } 130 else 131 { 132 count = bt; // we already have the data size 133 } 134 135 while (binr.ReadByte() == 0x00) 136 { //remove high order zeros in data 137 count -= 1; 138 } 139 binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte 140 return count; 141 } 142 143 144 /// <summary> 145 /// 2进制转16进制 146 /// </summary> 147 public static String Hex_2To16(Byte[] bytes) 148 { 149 String hexString = String.Empty; 150 Int32 iLength = 65535; 151 if (bytes != null) 152 { 153 StringBuilder strB = new StringBuilder(); 154 155 if (bytes.Length < iLength) 156 { 157 iLength = bytes.Length; 158 } 159 160 for (int i = 0; i < iLength; i++) 161 { 162 strB.Append(bytes[i].ToString("X2")); 163 } 164 hexString = strB.ToString(); 165 } 166 return hexString; 167 }