rsa非对称加密签名原理知识
一、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)