C#使用SHA256withRSA加密对接口进行访问
需求:接口改成SHA256withRSA加密访问
材料:自己电脑生成的RSA公钥和私钥(这里公钥提供给接口方配置,私钥我们留着自己用来加密。)
公钥私钥生成方式:
在自己电脑上使用openssl(类似cmd的界面,然后运行以下命令生成,生成后,你会在你【C:\Users\用户名】的路径下找到private_key.pem和public_key.pem这两个文件。
-public_key.pem提供给接口方,我们用private_key.pem进行加密(自留,不外传)。)

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048 openssl rsa -pubout -in private_key.pem -out public_key.pem
代码:
这里引用Portable.BouncyCastle

private void button1_Click(object sender, EventArgs e) { #region string publicKeyPath = @"C:\Users\T709\1\public_key.pem"; string privateKeyPath = @"C:\Users\T709\1\private_key.pem"; //生成32位随机字符串 var requestNonceId = ""; var requestDate = ""; Guid newG = Guid.NewGuid(); requestNonceId = newG.ToString().Replace("-", ""); // 获取当前时间的Unix时间戳 DateTime now = DateTime.UtcNow; TimeSpan span = now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); long unixTimestamp = (long)span.TotalMilliseconds; requestDate = unixTimestamp.ToString(); string requestUrl = "/api/auth/sign/v1"; string signStr = requestNonceId + requestDate + requestUrl; #endregion string privateKey = LoadPrivateContent(privateKeyPath); //SHA256withRSA //SHA-256withRSA var s = RSASignJavaBouncyCastle(signStr, privateKey, "SHA256withRSA"); var s2 = EncryptPrivateKeyJava(privateKey, signStr); } #region /// <summary> /// 基于BouncyCastle的RSA私钥加密 /// </summary> /// <param name="privateKeyJava"></param> /// <param name="data"></param> /// <returns></returns> public static string EncryptPrivateKeyJava(string privateKeyJava, string data, string encoding = "UTF-8") { RsaKeyParameters privateKeyParam = (RsaKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKeyJava)); byte[] cipherbytes = Encoding.GetEncoding(encoding).GetBytes(data); RsaEngine rsa = new RsaEngine(); rsa.Init(true, privateKeyParam);//参数true表示加密/false表示解密。 cipherbytes = rsa.ProcessBlock(cipherbytes, 0, cipherbytes.Length); return Convert.ToBase64String(cipherbytes); } #region 公钥解密 /// <summary> /// 基于BouncyCastle的RSA公钥解密 /// </summary> /// <param name="publicKeyJava"></param> /// <param name="data"></param> /// <param name="encoding"></param> /// <returns></returns> public static string DecryptPublicKeyJava(string publicKeyJava, string data, string encoding = "UTF-8") { RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKeyJava)); byte[] cipherbytes = Convert.FromBase64String(data); RsaEngine rsa = new RsaEngine(); rsa.Init(false, publicKeyParam);//参数true表示加密/false表示解密。 cipherbytes = rsa.ProcessBlock(cipherbytes, 0, cipherbytes.Length); return Encoding.GetEncoding(encoding).GetString(cipherbytes); } #endregion #region 加签 /// <summary> /// 基于BouncyCastle的RSA签名 /// </summary> /// <param name="data"></param> /// <param name="privateKeyJava"></param> /// <param name="hashAlgorithm">JAVA的和.NET的不一样,如:MD5(.NET)等同于MD5withRSA(JAVA)</param> /// <param name="encoding"></param> /// <returns></returns> public static string RSASignJavaBouncyCastle(string data, string privateKeyJava, string hashAlgorithm = "MD5withRSA", string encoding = "UTF-8") { RsaKeyParameters privateKeyParam = (RsaKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKeyJava)); ISigner signer = SignerUtilities.GetSigner(hashAlgorithm); signer.Init(true, privateKeyParam);//参数为true验签,参数为false加签 var dataByte = Encoding.GetEncoding(encoding).GetBytes(data); signer.BlockUpdate(dataByte, 0, dataByte.Length); //return Encoding.GetEncoding(encoding).GetString(signer.GenerateSignature()); //签名结果 非Base64String return Convert.ToBase64String(signer.GenerateSignature()); } #endregion #region 验签 /// <summary> /// 基于BouncyCastle的RSA签名 /// </summary> /// <param name="data">源数据</param> /// <param name="publicKeyJava"></param> /// <param name="signature">base64签名</param> /// <param name="hashAlgorithm">JAVA的和.NET的不一样,如:MD5(.NET)等同于MD5withRSA(JAVA)</param> /// <param name="encoding"></param> /// <returns></returns> public static bool VerifyJavaBouncyCastle(string data, string publicKeyJava, string signature, string hashAlgorithm = "MD5withRSA", string encoding = "UTF-8") { RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKeyJava)); ISigner signer = SignerUtilities.GetSigner(hashAlgorithm); signer.Init(false, publicKeyParam); byte[] dataByte = Encoding.GetEncoding(encoding).GetBytes(data); signer.BlockUpdate(dataByte, 0, dataByte.Length); //byte[] signatureByte = Encoding.GetEncoding(encoding).GetBytes(signature);// 非Base64String byte[] signatureByte = Convert.FromBase64String(signature); return signer.VerifySignature(signatureByte); } #endregion #endregion public static string LoadPrivateContent(string pemPath) { string content = File.ReadAllText(pemPath); // 精确移除PEM头尾和所有空白字符 content = content .Replace("-----BEGIN PUBLIC KEY-----", "") .Replace("-----END PUBLIC KEY-----", "") .Replace("-----BEGIN PRIVATE KEY-----", "") .Replace("-----END PRIVATE KEY-----", "") .Replace("-----BEGIN RSA PRIVATE KEY-----", "") .Replace("-----END RSA PRIVATE KEY-----", "") .Replace("\r", "") .Replace("\n", ""); //.Replace("\\s", ""); // 移除所有空白字符,包括换行、空格、制表符 return content; }
注:
这里我代码自行解析正常,但是Java接口方调用老是显示签名验证错误。
可能的原因:对方配置的不是你提供的公钥。(我这个就是碰到)
另一个就是我用的PostMan中,【Content-Type】、【Content-Length】和【Host】没有勾选。(这里默认勾选的,我测试的时候不小心给去掉了。)

感谢:https://www.cnblogs.com/ggll611928/p/18028968
https://blog.csdn.net/zxy13826134783/article/details/126447948
https://www.cnblogs.com/Alex80/p/11526634.html
https://www.cnblogs.com/kevin860/p/9557859.html
https://www.cnblogs.com/dj258/p/18261075

浙公网安备 33010602011771号