这边有个需求需要对接平安积分,但是操蛋的是对接文档中给的示例是java的,并且!!!关于加密的说明只是很笼统的说了下,具体怎么实现还要要看java示例。坑的雅痞。
下面记录几个接口用到的加密及验签方式:
首先说明几个必要的参数,是加密、加签、包括解密解签需要使用到的,
其次只会贴和业务无关的代码。
接口方提供:
- 商户密钥:merchantKey
- 私钥-加密用:privateKey
- 公钥-解密用:publicKey
- 测试商户号
使用的加密方式为:SHA1WithRSA,先来看文档:

这里需要对公共请求参数进行处理,
参数格式很好理解,这里只展示部分业务参数,仅用于加密说明
- {
- sign:签名,
- bizData:业务请求参数
- }
提供的java示例有兴趣的可以看下:
加签规则如下:
- bizData字段设置为未加密原始json字符串;
- 去掉sign参数字段;
- 将报文json字符串 按照key字典序升序排序(bizData中json字符串内容无需排序)
1、bizData字段设置为未加密原始json字符串;
req.bizData = bizreq.ToJson();//将bizdata转json
2、去掉sign参数字段;因为实例化业务参数的时候,没有对sign参数进行赋值,所以可以使用下面方法去除sign参数字段
调用方式为:
string sign_ = common.signfilter<PinganBaseReq>(req);
- ///
- /// 签名过滤[过滤对象为空/null]-这里用来去除对象中的签名
- ///
- public string signfilter<T>(T signobject)
- {
- JsonSerializerSettings jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
- string jsonOut = JsonConvert.SerializeObject(signobject, Formatting.Indented, jsonSetting);
- return jsonOut;
- }
3、将报文json字符串 按照key字典序升序排序(bizData中json字符串内容无需排序)
调用方式为:
req.sign = common.signmethod(sign_);z//sign_为第二步的变量
- public string signmethod(string strjson)
- {
- //json字符串升序排列
- strjson = StortJson(strjson);
- //去除特殊字符,不然验签不成功
- strjson = System.Text.RegularExpressions.Regex.Replace(strjson, "\\s*|\t|\r|\n", "");
- //SHA256加密-并转base64
- string strjson_ = RSAWithSha256.Sign(strjson, privateKey);一个是签名字符串,一个是私钥
- return strjson_;
- }
- ///
- /// 将报文json字符串根据key进行升序排列
- ///
- ///
- ///
- public string StortJson(string json)
- {
- var dic = JsonConvert.DeserializeObject
string, object>>(json); - SortedDictionary<string, object> keyValues = new SortedDictionary<string, object>(dic);
- keyValues.OrderBy(m => m.Key);//升序 把Key换成Value 就是对Value进行排序
- return JsonConvert.SerializeObject(keyValues);
- }
RSAWithSha256类:
- public class RSAWithSha256
- {
- ///
- /// RSA私钥,从Java格式转.net格式(不依赖第三方包)
- ///
- /// 私钥
- ///
- private static string RSAPrivateKeyJava2DotNet(string privateKey)
- {
- RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));
- return string.Format("
{0} {1} {2}
{3}
{4} {5} {6} {7} ", - Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
- Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
- }
-
- ///
- /// 256私钥签名
- ///
- ///
- ///
- ///
- public static string Sign(string contentForSign, string privateKey)
- {
- var netKey = RSAPrivateKeyJava2DotNet(privateKey); //转换成适用于.net的私钥
-
- //var rsa = FromXmlString(netKey); //.net core2.2及其以下版本使用,重写FromXmlString(string)方法
-
- var rsa = new RSACryptoServiceProvider();
- rsa.FromXmlString(netKey); //.net core3.0直接使用,不需要重写
-
- var rsaClear = new RSACryptoServiceProvider();
- var paras = rsa.ExportParameters(true);
- rsaClear.ImportParameters(paras); //签名返回
- using (var sha256 = new SHA256CryptoServiceProvider())
- {
- var signData = rsa.SignData(Encoding.Default.GetBytes(contentForSign), sha256);
- return Convert.ToBase64String(signData);
- }
- }
- ///
- /// SHA1签名-用于联合登录
- ///
- ///
- ///
- ///
- public static string SignSHA1(string contentForSign, string privateKey)
- {
- var netKey = RSAPrivateKeyJava2DotNet(privateKey); //转换成适用于.net的私钥
-
- //var rsa = FromXmlString(netKey); //.net core2.2及其以下版本使用,重写FromXmlString(string)方法
-
- var rsa = new RSACryptoServiceProvider();
- rsa.FromXmlString(netKey); //.net core3.0直接使用,不需要重写
-
- var rsaClear = new RSACryptoServiceProvider();
- var paras = rsa.ExportParameters(true);
- rsaClear.ImportParameters(paras); //签名返回
- using (var sha1 = new SHA1CryptoServiceProvider())
- {
- var signData = rsa.SignData(Encoding.Default.GetBytes(contentForSign), sha1);
- return Convert.ToBase64String(signData);
- }
- }
-
-
- ///
- /// RSA公钥,从Java格式转.net格式(不依赖第三方包)
- ///
- ///
- ///
- private static string RSAPublicKeyJava2DotNet(string publicKey)
- {
- RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
- return string.Format("
{0} {1} ", - Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
- Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
- }
-
- ///
- /// RSA签名验证
- ///
- /// 签名
- /// 验证的字符串
- /// 公钥
- ///
是否相同,true验证成功,false验证失败。 - public static bool VerifySignature(string encryptSource, string compareString, string publicKey)
- {
- try
- {
- //.net core2.2及其以下版本使用,重写FromXmlString(string)方法
- //using (RSACryptoServiceProvider rsa = FromXmlString(RSAPublicKeyJava2DotNet(publicKey)))
- using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
- {
- rsa.FromXmlString(RSAPublicKeyJava2DotNet(publicKey)); //.net core3.0直接使用,不需要重写
- byte[] signature = Convert.FromBase64String(encryptSource);
- SHA256Managed sha256 = new SHA256Managed();
- RSAPKCS1SignatureDeformatter df = new RSAPKCS1SignatureDeformatter(rsa);
- df.SetHashAlgorithm("SHA256");
- byte[] compareByte = sha256.ComputeHash(Encoding.Default.GetBytes(compareString));
-
- return df.VerifySignature(compareByte, signature);
- }
- }
- catch (Exception)
- {
- return false;
- }
- }
- }
对bizData业务参数原json字符串进行AES128加密(加密数据Base64编码)
模式: AES/CBC/PKCS5Padding , 秘钥由平安付提供。
注意:这里和其他aes接口加密不同的是需要计算iv,客户提供的ivParameter有两种, 这里两种都会介绍
这里加密前需要先对商户密钥转换成字节组
调用方式为:
req.bizData = common.AesEncrypt(req.bizData, common.toAesKey());
- public string toAesKey()
- {
- int aesKeyBit = 128;
- int aesKeySize = aesKeyBit / 8;
- // 16位编码转为字节数组
- byte[] bs = strToToHexByte(merchantKey);
- byte[] aesKeyBytes = new byte[aesKeySize];
- Array.Copy(bs, aesKeyBytes, aesKeySize);
-
- int merchantKeySize = bs.Length;
-
- if (aesKeySize > merchantKeySize)
- {
- // 填充
- for (int index = merchantKeySize; index < aesKeySize; index++)
- {
- aesKeyBytes[index] = (byte)index;
- }
- }
-
- return base64encode(aesKeyBytes);
- }
- private byte[] strToToHexByte(string hexString)
- {
- hexString = hexString.Replace(" ", "");
- if ((hexString.Length % 2) != 0)
- hexString += " ";
- byte[] returnBytes = new byte[hexString.Length / 2];
- for (int i = 0; i < returnBytes.Length; i++)
- returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
- return returnBytes;
- }
//使用客户提供的iv字节组
private static byte[] IV_VALUE = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };
//加密
- public string AesEncrypt(string encryptStr, string key)
- {
- byte[] keyArray = Convert.FromBase64String(key);
- byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(encryptStr);//然后你看走完这部,数据不一样了看到没
- RijndaelManaged rDel = new RijndaelManaged();
- rDel.Key = keyArray;
- rDel.Mode = CipherMode.CBC;
- rDel.Padding = PaddingMode.PKCS7;
- rDel.IV = IV_VALUE;
- ICryptoTransform cTransform = rDel.CreateEncryptor();
- byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
- return Convert.ToBase64String(resultArray, 0, resultArray.Length);
- }
//解密--平安返回的业务参数也是加密的,这里需要通过解密处理
- ///
- /// AES 解密
- ///
- /// 明文(待解密)
- /// 密文
- ///
- public string AESDencrypt(string encryptStr, string key)
- {
- try
- {
- encryptStr = CorrectionCiphertext(encryptStr);
- byte[] keyArray = Convert.FromBase64String(key);
- byte[] toEncryptArray = Convert.FromBase64String(encryptStr);
- RijndaelManaged rDel = new RijndaelManaged();
- rDel.Key = keyArray;
- rDel.Mode = CipherMode.CBC;
- rDel.Padding = PaddingMode.PKCS7;
- rDel.IV = IV_VALUE;
- ICryptoTransform cTransform = rDel.CreateDecryptor();
- byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
- return UTF8Encoding.UTF8.GetString(resultArray);
- }
- catch (Exception)
- {
- return "";
-
- }
- }
-
- ///
- /// 补全密文
- ///
- /// 密文
- /// 秘钥长度
- ///
补全后的密文 - private static string CorrectionCiphertext(string strCiphertext, int keySize = 128)
- {
- int ciphertextLength = keySize / 8;
- byte[] data = Convert.FromBase64String(strCiphertext);
- var newData = new List<byte>(data);
- while (newData.Count < ciphertextLength)
- {
- newData.Insert(0, 0x00);
- }
- return Convert.ToBase64String(newData.ToArray());
- }
下面介绍iv基于32位字符串时,加密解密方法
客户提供的32位iv
private static string ivParameter32 = "0123456789abcdef0123456789abcdef";
- ///
- /// AES 加密--仅适用于平安联合登录
- ///
- /// 明文(待加密)
- /// 密文
- ///
- public string AesEncrypt_(string encryptStr, string key)
- {
- byte[] IV = OpenApiAESKeyCreator.hexStr2Bytes(ivParameter32);
- byte[] keyArray = Convert.FromBase64String(key);
- byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(encryptStr);//
- RijndaelManaged rDel = new RijndaelManaged();
- rDel.Key = keyArray;
- rDel.Mode = CipherMode.CBC;
- rDel.Padding = PaddingMode.PKCS7;
- rDel.IV = IV;
- ICryptoTransform cTransform = rDel.CreateEncryptor();
- byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
- return Convert.ToBase64String(resultArray, 0, resultArray.Length);
- }
- ///
- /// 解密--仅适用于平安联合登录
- ///
- ///
- ///
- ///
- public static string AESDencrypt_(string encryptStr, string key)
- {
- byte[] keyArray = Convert.FromBase64String(key);
- byte[] toEncryptArray = Convert.FromBase64String(encryptStr);
- byte[] IV_VALUE = OpenApiAESKeyCreator.hexStr2Bytes(ivParameter32);
-
- RijndaelManaged rDel = new RijndaelManaged();
- rDel.Key = keyArray;
- rDel.Mode = CipherMode.CBC;
- rDel.Padding = PaddingMode.PKCS7;
- rDel.IV = IV_VALUE;
- ICryptoTransform cTransform = rDel.CreateDecryptor();
- byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
- return UTF8Encoding.UTF8.GetString(resultArray);
- }
OpenApiAESKeyCreator类:
- internal class OpenApiAESKeyCreator
- {
- public static String toAesKey128(String merchantKey)
- {
- return toAesKey(merchantKey, 128);
- }
-
- private static String toAesKey(String merchantKey, int aesKeyBit)
- {
-
- int aesKeySize = aesKeyBit / 8;
- // 16位编码转为字节数组
- //[-123, 62, -64, -29, -41, 101, 71, -92, -71, 123, 93, -41, 118, 54, 111, -113]
- //[133, 62, 192, 227, 215, 101, 71, 164, 185, 123, 93, 215, 118, 54, 111, 143]这是转base64之前的数组数据
- byte[] bs = hexStr2Bytes(merchantKey);
-
- byte[] aesKeyBytes = new byte[aesKeySize];
- Array.Copy(bs, aesKeyBytes, aesKeySize);
-
- int merchantKeySize = bs.Length;
-
- if (aesKeySize > merchantKeySize)
- {
- // 填充
- for (int index = merchantKeySize; index < aesKeySize; index++)
- {
- aesKeyBytes[index] = (byte)index;
- }
- }
-
- return base64encode(aesKeyBytes);
- }
-
- // 将 s 进行 BASE64 编码
- private static String base64encode(byte[] src)
- {
- if (src == null)
- {
- return null;
- }
-
- return Convert.ToBase64String(src);
- }
-
- public static byte[] hexStr2Bytes(string src)
- {
- /* 对输入值进行规范化整理 */
- src = src.Trim().Replace(" ", "").ToUpper();
- // 处理值初始化
- int m = 0, n = 0;
- int iLen = src.Length / 2; // 计算长度
- byte[] ret = new byte[iLen]; // 分配存储空间
-
- for (int i = 0; i < iLen; i++)
- {
- ret[i] = (byte)(Convert.ToInt32(src.Substring(i * 2, 2), 16) & 0xFF);
- }
- return ret;
- }
- }