C#平安对接之SHA256withRSA,以及AES128加密(平安仅java文档,有需要使用c#对接的可以参考下)

这边有个需求需要对接平安积分,但是操蛋的是对接文档中给的示例是java的,并且!!!关于加密的说明只是很笼统的说了下,具体怎么实现还要要看java示例。坑的雅痞。 

下面记录几个接口用到的加密及验签方式:

首先说明几个必要的参数,是加密、加签、包括解密解签需要使用到的,

其次只会贴和业务无关的代码。

接口方提供:

商户密钥:merchantKey
私钥-加密用:privateKey
公钥-解密用:publicKey
测试商户号

1、签名用商户侧RSA私钥进行SHA-256,生成签名:

使用的加密方式为:SHA1WithRSA,先来看文档:

这里需要对公共请求参数进行处理,

参数格式很好理解,这里只展示部分业务参数,仅用于加密说明

{
   sign:签名,
   bizData:业务请求参数
}

提供的java示例有兴趣的可以看下:

有道云笔记http://note.youdao.com/noteshare?id=29bb960b01c1db6417462ab86ed81504&sub=BD9CFD6FB3AE4FBD8E2F6EFDA04672CB

加签规则如下:

bizData字段设置为未加密原始json字符串;
去掉sign参数字段;
将报文json字符串 按照key字典序升序排序(bizData中json字符串内容无需排序)

1、bizData字段设置为未加密原始json字符串;

 req.bizData = bizreq.ToJson();//将bizdata转json

2、去掉sign参数字段;因为实例化业务参数的时候,没有对sign参数进行赋值,所以可以使用下面方法去除sign参数字段

调用方式为:

  string sign_ = common.signfilter(req);
/// 
        /// 签名过滤[过滤对象为空/null]-这里用来去除对象中的签名
        /// 
        public string signfilter(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>(json);
            SortedDictionary keyValues = new SortedDictionary(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; } } }

2、bizData加密:

对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(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;
    }
}

你可能感兴趣的:(c#,java,开发语言,c#)