一、RSA加密簡介
RSA加密是一種非對稱加密。可以在不直接傳遞密鑰的情況下,完成解密。這能夠確保信息的安全性,避免了直接傳遞密鑰所造成的被破解的風險。是由一對密鑰來進行加解密的過程,分別稱為公鑰和私鑰。兩者之間有數學相關,該加密算法的原理就是對一極大整數做因數分解的困難性來保證安全性。通常個人保存私鑰,公鑰是公開的(可能同時多人持有)。
二、RSA加密、簽名區別
加密和簽名都是為了安全性考慮,但略有不同。常有人問加密和簽名是用私鑰還是公鑰?其實都是對加密和簽名的作用有所混淆。簡單的說,加密是為了防止信息被泄露,而簽名是為了防止信息被篡改。這里舉2個例子說明。
第一個場景:戰場上,B要給A傳遞一條消息,內容為某一指令。
RSA的加密過程如下:
(1)A生成一對密鑰(公鑰和私鑰),私鑰不公開,A自己保留。公鑰為公開的,任何人可以獲取。
(2)A傳遞自己的公鑰給B,B用A的公鑰對消息進行加密。
(3)A接收到B加密的消息,利用A自己的私鑰對消息進行解密。
在這個過程中,只有2次傳遞過程,第一次是A傳遞公鑰給B,第二次是B傳遞加密消息給A,即使都被敵方截獲,也沒有危險性,因為只有A的私鑰才能對消息進行解密,防止了消息內容的泄露。
第二個場景:A收到B發的消息后,需要進行回復“收到”。
RSA簽名的過程如下:
(1)A生成一對密鑰(公鑰和私鑰),私鑰不公開,A自己保留。公鑰為公開的,任何人可以獲取。
(2)A用自己的私鑰對消息加簽,形成簽名,並將加簽的消息和消息本身一起傳遞給B。
(3)B收到消息后,在獲取A的公鑰進行驗簽,如果驗簽出來的內容與消息本身一致,證明消息是A回復的。
在這個過程中,只有2次傳遞過程,第一次是A傳遞加簽的消息和消息本身給B,第二次是B獲取A的公鑰,即使都被敵方截獲,也沒有危險性,因為只有A的私鑰才能對消息進行簽名,即使知道了消息內容,也無法偽造帶簽名的回復給B,防止了消息內容的篡改。
但是,綜合兩個場景你會發現,第一個場景雖然被截獲的消息沒有泄露,但是可以利用截獲的公鑰,將假指令進行加密,然后傳遞給A。第二個場景雖然截獲的消息不能被篡改,但是消息的內容可以利用公鑰驗簽來獲得,並不能防止泄露。所以在實際應用中,要根據情況使用,也可以同時使用加密和簽名,比如A和B都有一套自己的公鑰和私鑰,當A要給B發送消息時,先用B的公鑰對消息加密,再對加密的消息使用A的私鑰加簽名,達到既不泄露也不被篡改,更能保證消息的安全性。
總結:公鑰加密、私鑰解密、私鑰簽名、公鑰驗簽。
/// <summary> /// RSA加密 /// </summary> /// <param name="publickey"></param> /// <param name="content"></param> /// <returns></returns> public static string RSAEncrypt(string publickey,string content) { publickey =@"<RSAKeyValue><Modulus>5m9m14XH3oqLJ8bNGw9e4rGpXpcktv9MSkHSVFVMjHbfv+SJ5v0ubqQxa5YjLN4vc49z7SVju8s0X4gZ6AzZTn06jzWOgyPRV54Q4I0DCYadWW4Ze3e+BOtwgVU1Og3qHKn8vygoj40J6U85Z/PTJu3hN1m75Zr195ju7g9v4Hk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"; RSACryptoServiceProvider rsa =new RSACryptoServiceProvider(); byte[] cipherbytes; rsa.FromXmlString(publickey); cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(content),false); return Convert.ToBase64String(cipherbytes); } /// <summary> /// RSA解密 /// </summary> /// <param name="privatekey"></param> /// <param name="content"></param> /// <returns></returns> public static string RSADecrypt(string privatekey,string content) { privatekey =@"<RSAKeyValue><Modulus>5m9m14XH3oqLJ8bNGw9e4rGpXpcktv9MSkHSVFVMjHbfv+SJ5v0ubqQxa5YjLN4vc49z7SVju8s0X4gZ6AzZTn06jzWOgyPRV54Q4I0DCYadWW4Ze3e+BOtwgVU1Og3qHKn8vygoj40J6U85Z/PTJu3hN1m75Zr195ju7g9v4Hk=</Modulus><Exponent>AQAB</Exponent><P>/hf2dnK7rNfl3lbqghWcpFdu778hUpIEBixCDL5WiBtpkZdpSw90aERmHJYaW2RGvGRi6zSftLh00KHsPcNUMw==</P><Q>6Cn/jOLrPapDTEp1Fkq+uz++1Do0eeX7HYqi9rY29CqShzCeI7LEYOoSwYuAJ3xA/DuCdQENPSoJ9KFbO4Wsow==</Q><DP>ga1rHIJro8e/yhxjrKYo/nqc5ICQGhrpMNlPkD9n3CjZVPOISkWF7FzUHEzDANeJfkZhcZa21z24aG3rKo5Qnw==</DP><DQ>MNGsCB8rYlMsRZ2ek2pyQwO7h/sZT8y5ilO9wu08Dwnot/7UMiOEQfDWstY3w5XQQHnvC9WFyCfP4h4QBissyw==</DQ><InverseQ>EG02S7SADhH1EVT9DD0Z62Y0uY7gIYvxX/uq+IzKSCwB8M2G7Qv9xgZQaQlLpCaeKbux3Y59hHM+KpamGL19Kg==</InverseQ><D>vmaYHEbPAgOJvaEXQl+t8DQKFT1fudEysTy31LTyXjGu6XiltXXHUuZaa2IPyHgBz0Nd7znwsW/S44iql0Fen1kzKioEL3svANui63O3o5xdDeExVM6zOf1wUUh/oldovPweChyoAdMtUzgvCbJk1sYDJf++Nr0FeNW1RB1XG30=</D></RSAKeyValue>"; RSACryptoServiceProvider rsa =new RSACryptoServiceProvider(); byte[] cipherbytes; rsa.FromXmlString(privatekey); cipherbytes = rsa.Decrypt(Convert.FromBase64String(content),false); return Encoding.UTF8.GetString(cipherbytes); }
java的公鑰私鑰是一串字符串,.net 公鑰私鑰是一串XML格式字符串,兩者格式不是一致的,需要轉換一下才能使用,具體請參考
1 using System; 2 using System.Xml; 3 using Org.BouncyCastle.Asn1.Pkcs; 4 using Org.BouncyCastle.Asn1.X509; 5 using Org.BouncyCastle.Crypto.Parameters; 6 using Org.BouncyCastle.Math; 7 using Org.BouncyCastle.Pkcs; 8 using Org.BouncyCastle.Security; 9 using Org.BouncyCastle.X509; 10 /// <summary> 11 /// RSA密鑰格式轉換 12 /// </summary> 13 public class RSAKeyConvert 14 { 15 /// <summary> 16 /// RSA私鑰格式轉換,java->.net 17 /// </summary> 18 /// <param name="privateKey">java生成的RSA私鑰</param> 19 /// <returns></returns> 20 public static string RSAPrivateKeyJava2DotNet(string privateKey) 21 { 22 RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey)); 23 return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>", 24 Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()), 25 Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()), 26 Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()), 27 Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()), 28 Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()), 29 Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()), 30 Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()), 31 Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned())); 32 } 33 /// <summary> 34 /// RSA私鑰格式轉換,.net->java 35 /// </summary> 36 /// <param name="privateKey">.net生成的私鑰</param> 37 /// <returns></returns> 38 public static string RSAPrivateKeyDotNet2Java(string privateKey) 39 { 40 XmlDocument doc = new XmlDocument(); 41 doc.LoadXml(privateKey); 42 BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText)); 43 BigInteger exp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText)); 44 BigInteger d = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText)); 45 BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText)); 46 BigInteger q = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText)); 47 BigInteger dp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText)); 48 BigInteger dq = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText)); 49 BigInteger qinv = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText)); 50 RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv); 51 PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam); 52 byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded(); 53 return Convert.ToBase64String(serializedPrivateBytes); 54 } 55 /// <summary> 56 /// RSA公鑰格式轉換,java->.net 57 /// </summary> 58 /// <param name="publicKey">java生成的公鑰</param> 59 /// <returns></returns> 60 public static string RSAPublicKeyJava2DotNet(string publicKey) 61 { 62 RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey)); 63 return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>", 64 Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()), 65 Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned())); 66 } 67 /// <summary> 68 /// RSA公鑰格式轉換,.net->java 69 /// </summary> 70 /// <param name="publicKey">.net生成的公鑰</param> 71 /// <returns></returns> 72 public static string RSAPublicKeyDotNet2Java(string publicKey) 73 { 74 XmlDocument doc = new XmlDocument(); doc.LoadXml(publicKey); 75 BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText)); 76 BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText)); 77 RsaKeyParameters pub = new RsaKeyParameters(false, m, p); 78 SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub); 79 byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded(); 80 return Convert.ToBase64String(serializedPublicBytes); 81 } 82 }
使用的命名空間:
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
//初始化時生成公鑰和私鑰 RSACryptoServiceProvider provider = new RSACryptoServiceProvider(); string privateKey = provider.ToXmlString(true);//生成私鑰 string publicKey = provider.ToXmlString(false);//生成公鑰
加簽算法 和 驗簽算法 必須一致,舉栗加簽使用的是MD5算法那么驗簽也必須是MD5算法,不然就會驗簽失敗。栗子中使用的是SHA256算法,對應JAVA的SHA256withRSA算法。
/// <summary> /// 生成簽名 /// </summary> /// <param name="str">需簽名的數據</param> /// <param name="privateKey">私鑰</param> /// <param name="encoding">編碼格式 默認utf-8</param> /// <returns>簽名后的值</returns> public static string Signature(string str, string privateKey, string encoding) { //SHA256withRSA //根據需要加簽時的哈希算法轉化成對應的hash字符節 //byte[] bt = Encoding.GetEncoding("utf-8").GetBytes(str); byte[] bt = Encoding.GetEncoding(encoding).GetBytes(str); var sha256 = new SHA256CryptoServiceProvider(); byte[] rgbHash = sha256.ComputeHash(bt); RSACryptoServiceProvider key = new RSACryptoServiceProvider(); key.FromXmlString(privateKey); RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key); formatter.SetHashAlgorithm("SHA256");//此處是你需要加簽的hash算法,需要和上邊你計算的hash值的算法一致,不然會報錯。 byte[] inArray = formatter.CreateSignature(rgbHash); return Convert.ToBase64String(inArray); }
/// <summary> /// 簽名驗證 /// </summary> /// <param name="str">待驗證的字符串</param> /// <param name="sign">加簽之后的字符串</param> /// <param name="publicKey">公鑰</param> /// <param name="encoding">編碼格式</param> /// <returns>簽名是否符合</returns> public static bool SignCheck(string str, string sign, string publicKey, string encoding) { try { //byte[] bt = Encoding.GetEncoding("utf-8").GetBytes(str); byte[] bt = Encoding.GetEncoding(encoding).GetBytes(str); var sha256 = new SHA256CryptoServiceProvider(); byte[] rgbHash = sha256.ComputeHash(bt); RSACryptoServiceProvider key = new RSACryptoServiceProvider(); key.FromXmlString(publicKey); RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key); deformatter.SetHashAlgorithm("SHA256"); byte[] rgbSignature = Convert.FromBase64String(sign); if (deformatter.VerifySignature(rgbHash, rgbSignature)) { return true; } return false; } catch { return false; } }
.cer證書信息 Base64編碼 字符串驗簽:
/// <summary> /// 驗簽demo /// <param name="Data">待驗證的字符串</param> /// <param name="SignData">加簽之后的字符串</param> /// </summary> /// <returns></returns> public static void VerifyDataDemo(string Data,string SignData) { //加簽原文 //string Data = "2100040001401529c913701003072941546SwitchFeeInquiry{\"vesselCName\":\"正利洛杉磯\",\"vesselEName\":\"APL LOS ANGELES\",\"voyageNo\":\"0KR44E\",\"billNo\":\"AJKQDAS44E0360\",\"shippingAgencyCode\":\"91370220756929610P\",\"companyCode\":\"913701003072941546\",\"companyName\":\"山東匯貿電子口岸有限公司\",\"remark\":null}"; //.cer證書信息 證書導出編碼Base64格式 讀取證書的Base64編碼 string certInfoStr = "MIIC/TCCAeWgAwIBAgIQfV5s1Unl+rdC/+jXcl1GbzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlTbmxUYW9LQUIwIBcNMTgxMjMxMTYwMDAwWhgPNDAxODEyMzExNjAwMDBaMBQxEjAQBgNVBAMTCVNubFRhb0tBQjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWeiVBDvANYDxNPm9v0HccYA5miib8A/YcF5sUWlHOjipWeEETZYiwgzL64Glzxdkt0IqhpkJ6HjT1hfdCLGgf8MXOGsoJRF/MJdslfDwEXEIcflfsCCbAQ9L1TZN7DAQH2a1a1STmTZI1HABZj2Jz8ZKv+8uByRRvkwkTTFbEY63ssCvgi62RZ2L1IsP8WzQOnydN55BQzaD7FwF1TlCb/SfBV4k4qz8dtWhiXXcuwBZbzcrYDq7uReqSWCO+n3uWGBmGFFR44CDgppI3KJcGLegFJpZihtW7Br3dCJmnH+ayrt9hPCfGr3KI5ysqqThjj7LDMat/7NGLF+siXx7UCAwEAAaNJMEcwRQYDVR0BBD4wPIAQzjjloN6qCKGikaZnXVkiZqEWMBQxEjAQBgNVBAMTCVNubFRhb0tBQoIQfV5s1Unl+rdC/+jXcl1GbzANBgkqhkiG9w0BAQsFAAOCAQEAsvmbM/OpP0qxO9t0ISc/4DGRCwDAoCtvvaDMO6AmFGEbQ3YqepfdDq1Mr1dH+td2QvkypwOG9u0dGKpt8SUPdUAVEzr4ni6EV1UIomBgbkDHKYAIvsxPHrrfGNeuZAtPXr+tY5CjHR2LW+WMcCWc4K9GemXDWTlVPh79DIrGrGn8ZANB2Tf9zLl3iYk+QTrUl2MnVWujWIkAZOunMtNG4DC3aJWizU+OH+UBVgDVsGJFoJYO1qZleWP07FYI+qVqsiiwt/iNuGaR+qYDRhultLz1DaP9UmAXedm1DynbNio18N4A+G8nodVIc4/D5t5VscjBdYE5XpJ/YSPiwQRK+w=="; //簽名字符串 //string SignData = "sgvPVzfjBHEpiMFX2vGg2cr/4B7J6FPcLKVKivICSLUAqDvjanbxLKctHlfzYkIfAEEl2OWC/S3ZF2b/ALgeAxsz16MopV2AGXUmakKqUz8wCPSFtZjgjXQzgx49FU2wcXduh4quLD1xhDiW2gMm0ehN2rJ40Ny4zAlEiXCFlGrmfIHlME91AosS/nI9WG3tNIBr7WNHfnjsS2CplXvl5v8Wp3ZZnPtn5cyyXwV4++z5s3k2T9NVx1CJfgkNB03cajPiO7Cc150eSl/YEkN/iZt1ITp1QLuiI5qjKwWaWzOM7G9fmvasoqKc4QqrabmrOucn6sL2QIcj1+ptTltrOA=="; X509Certificate2 X509 = new X509Certificate2(Encoding.UTF8.GetBytes(certInfoStr)); //string publickey = X509.PublicKey.Key.ToXmlString(false); RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)X509.PublicKey.Key; using (var sha256 = new SHA256CryptoServiceProvider()) { bool result = rsa.VerifyData(Encoding.UTF8.GetBytes(Data), sha256, Convert.FromBase64String(SignData)); Console.WriteLine("驗簽結果" + result); Console.ReadKey(); } }
原文鏈接:RSA加密、解密、簽名、驗簽的原理及方法 - PC君 - 博客園 (cnblogs.com)