前言

前不久移植了支付宝官方的SDK,以适用ASP.NET Core使用支付宝支付,但是最近有好几位用户反应在Linux下使用会出错,调试发现是RSA加密的错误,下面具体讲一讲。

RSA在.NET Core的改动

以前我们使用RSA加密主要是使用RSACryptoServiceProvider这个类,在.NET Core中也有这个类,但是这个类并不支持跨平台,所以如果你是用这个类来进行加/解密在windows上运行是完全没有错误的,但是只要你一放到Linux下就会出现异常。

查阅资料得知,要解决这个问题,需要改用 System.Security.Cryptography.RSA.Create() 工厂方法,使用它之后,在 Windows 上创建的是 System.Security.Cryptography.RSACng 的实例,在 Mac 与 Linux 上创建的是 System.Security.Cryptography.RSAOpenSsl 的实例,它们都继承自 System.Security.Cryptography.RSA 抽象类。

RSACng:

相关资料:https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsacng?view=netcore-2.0

RSAOpenSsl :

相关资料:https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsaopenssl?view=netcore-2.0

在Windows上的调试截图:

在Mac上使用Visual studio For Mac 调试截图:

RSA公钥/私钥说明

这里的RSA加密/解密主要是针对于由OpenSSL生成的公钥/私钥字符串。ssh-keygen -t rsa 命令生成的公钥私钥是不行的。

公钥示例:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7PyjMEuniN6BPn8oqzIZ6AO1N
jSTO9R3adCCIwKfKIEoWXXM+tHDpktdPKSaAsWJPTNAGvEvtxOfzXib/EMXKqD0e
Uy5MatfpRjRdf1hJVimmfrb09Qx2j7CsKLy7nD23m4xubdYBwvkjMwt/L3JxB5D6
qryW1wei/j1c+/OCxQIDAQAB
-----END PUBLIC KEY-----

私钥示例:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC7PyjMEuniN6BPn8oqzIZ6AO1NjSTO9R3adCCIwKfKIEoWXXM+
tHDpktdPKSaAsWJPTNAGvEvtxOfzXib/EMXKqD0eUy5MatfpRjRdf1hJVimmfrb09Qx2j7CsKLy7nD23m4xubdYBwvkjMwt/L3JxB5D6qryW1wei/j1c+/OCxQIDAQAB
AoGAT7vGYJgRNf4f6qgNS4pKHTu10RcwPFyOOM7IZ9M5380+HyXuBB6MEjowKwpH1fcy+LepwaR+5KG7b5uBGY4H2ticMtdysBd9gLwnY4Eh4j7LCWE54HvELpeWXkWp
FQdb/NQhcqMAGwYsTnRPdBqkrUmJBTYqEGkIlqCQ5vUJOCECQQDhe0KGmbq1RWp6
TDvgpA2dUmlt2fdP8oNW8O7MvbDaQRduoZnVRTPYCDKfzFqpNXL1hAYgth1N0vzD
nv3VoLcpAkEA1JcY+rLv5js1g5Luv8LaI5/3uOg0CW7fmh/LfGuz8k/OxASN+cAO
UjPHrxtc5xn1zat4/bnV5GEdlOp/DhquPQJBAIV2Fsdi4M+AueiPjPWHRQO0jvDV
jfwFOFZSn5YSRUa6NmtmPY6tumUJXSWWqKb1GwlVTuc3xBqXYsNLLUWwLhkCQQDJ
UJCiD0LohhdGEqUuSKnj5H9kxddJO4pZXFSI7UEJbJQDwcBkyn+FTm2BH+tZGZdQ
fVnlA89OJr0poOpSg+eNAkAKY85SR9KASaTiDBoPpJ8N805XEhd0Kq+ghzSThxL3
fVtKUQLiCh7Yd8oMd/G5S3xWJHUXSioATT8uPRH2bOb/
-----END RSA PRIVATE KEY-----

公钥/私钥生成

Windows&MAC_OSX可以使用有支付宝开发的RSA密钥生成工具:

使用此工具生成的时候一定要选择,PKCS1

下载地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1

此外还可以使用OpenSSL工具命令来生成:https://doc.open.alipay.com/docs/doc.htm?articleId=106130&docType=1

.NET Core 中的使用

这里要讲一下RSA2算法。

什么是RSA2 ?RSA2 是在原来SHA1WithRSA签名算法的基础上,新增了支持SHA256WithRSA的签名算法。该算法比SHA1WithRSA有更强的安全能力。

算法名称 标准签名算法名称 备注
RSA2 SHA256WithRSA (强烈推荐使用),强制要求RSA密钥的长度至少为2048
RSA SHA1WithRSA 对RSA密钥的长度不限制,推荐使用2048位以上

签名的作用:保证数据完整性,机密性和发送方角色的不可抵赖性

这里来一发干货,我已经封装好的RSA/RSA2算法,支持加密/解密/签名/验证签名。

/// <summary>/// RSA加解密 使用OpenSSL的公钥加密/私钥解密/// 作者:李志强/// 创建时间:2017年10月30日15:50:14/// QQ:501232752/// </summary>

public class RSAHelper{     private readonly RSA _privateKeyRsaProvider;    private readonly RSA _publicKeyRsaProvider;    private readonly HashAlgorithmName _hashAlgorithmName;   

  private readonly Encoding _encoding;    /// <summary>/// 实例化RSAHelper/// </summary>/// <param name="rsaType">加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048</param>/// <param name="encoding">编码类型</param>/// <param name="privateKey">私钥</param>/// <param name="publicKey">公钥</param>public RSAHelper(RSAType rsaType, Encoding encoding, string privateKey, string publicKey = null)    {_encoding = encoding;        if (!string.IsNullOrEmpty(privateKey)){_privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey);}        if (!string.IsNullOrEmpty(publicKey)){_publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey);}_hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;}    #region 使用私钥签名/// <summary>/// 使用私钥签名/// </summary>/// <param name="data">原始数据</param>/// <returns></returns>public string Sign(string data)    {        byte[] dataBytes = _encoding.GetBytes(data);           var signatureBytes = _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);               return Convert.ToBase64String(signatureBytes);}    #endregion#region 使用公钥验证签名/// <summary>/// 使用公钥验证签名/// </summary>/// <param name="data">原始数据</param>/// <param name="sign">签名</param>/// <returns></returns>public bool Verify(string data,string sign)    {        byte[] dataBytes = _encoding.GetBytes(data);            byte[] signBytes = Convert.FromBase64String(sign);        var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);              return verify;}    #endregion#region 解密public string Decrypt(string cipherText)    {        if (_privateKeyRsaProvider == null){            throw new Exception("_privateKeyRsaProvider is null");}           return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1));}    #endregion#region 加密public string Encrypt(string text)    {            if (_publicKeyRsaProvider == null){                  throw new Exception("_publicKeyRsaProvider is null");}               return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.Pkcs1));}    #endregion#region 使用私钥创建RSA实例public RSA CreateRsaProviderFromPrivateKey(string privateKey)    {        var privateKeyBits = Convert.FromBase64String(privateKey);                var rsa = RSA.Create();                  var rsaParameters = new RSAParameters();             using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))){            byte bt = 0;                      ushort twobytes = 0;twobytes = binr.ReadUInt16();                    if (twobytes == 0x8130)binr.ReadByte();                         else if (twobytes == 0x8230)binr.ReadInt16();                           elsethrow new Exception("Unexpected value read binr.ReadUInt16()");twobytes = binr.ReadUInt16();                  if (twobytes != 0x0102)                   throw new Exception("Unexpected version");bt = binr.ReadByte();                    if (bt != 0x00)                  throw new Exception("Unexpected value read binr.ReadByte()");rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr));rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr));}rsa.ImportParameters(rsaParameters);            return rsa;}    #endregion#region 使用公钥创建RSA实例public RSA CreateRsaProviderFromPublicKey(string publicKeyString)    {        // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };            byte[] seq = new byte[15];            var x509Key = Convert.FromBase64String(publicKeyString);        // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------using (MemoryStream mem = new MemoryStream(x509Key)){            using (BinaryReader binr = new BinaryReader(mem))  //wrap Memory Stream with BinaryReader for easy reading{                byte bt = 0;                ushort twobytes = 0;twobytes = binr.ReadUInt16();                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)binr.ReadByte();    //advance 1 byteelse if (twobytes == 0x8230)binr.ReadInt16();   //advance 2 byteselsereturn null;seq = binr.ReadBytes(15);       //read the Sequence OIDif (!CompareBytearrays(seq, seqOid))    //make sure Sequence for OID is correctreturn null;twobytes = binr.ReadUInt16();                if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)binr.ReadByte();    //advance 1 byteelse if (twobytes == 0x8203)binr.ReadInt16();   //advance 2 byteselsereturn null;bt = binr.ReadByte();                if (bt != 0x00)     //expect null byte nextreturn null;twobytes = binr.ReadUInt16();                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)binr.ReadByte();    //advance 1 byteelse if (twobytes == 0x8230)                    binr.ReadInt16();   //advance 2 bytes                else                    return null;                twobytes = binr.ReadUInt16();                byte lowbyte = 0x00;                       byte highbyte = 0x00;                if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)                    lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus                else if (twobytes == 0x8202)                {                    highbyte = binr.ReadByte(); //advance 2 bytes                    lowbyte = binr.ReadByte();                }                else                    return null;                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order                int modsize = BitConverter.ToInt32(modint, 0);                int firstbyte = binr.PeekChar();                     if (firstbyte == 0x00)                {   //if first byte (highest order) of modulus is zero, don't include it                    binr.ReadByte();    //skip this null byte                    modsize -= 1;   //reduce modulus buffer size by 1                }                byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes                if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data                    return null;                  int expbytes = (int)binr.ReadByte();         // should only need one byte for actual exponent data (for all useful values)                byte[] exponent = binr.ReadBytes(expbytes);                // ------- create RSACryptoServiceProvider instance and initialize with public key -----                var rsa = RSA.Create();                RSAParameters rsaKeyInfo = new RSAParameters                {                    Modulus = modulus,                    Exponent = exponent                };                rsa.ImportParameters(rsaKeyInfo);                        return rsa;            }        }    }    #endregion    #region 导入密钥算法    private int GetIntegerSize(BinaryReader binr)    {        byte bt = 0;        int count = 0;        bt = binr.ReadByte();         if (bt != 0x02)                 return 0;        bt = binr.ReadByte();               if (bt == 0x81)            count = binr.ReadByte();            else        if (bt == 0x82)        {                     var highbyte = binr.ReadByte();                 var lowbyte = binr.ReadByte();                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };            count = BitConverter.ToInt32(modint, 0);        }              else        {            count = bt;        }               while (binr.ReadByte() == 0x00)        {            count -= 1;        }        binr.BaseStream.Seek(-1, SeekOrigin.Current);        return count;    }   

      private bool CompareBytearrays(byte[] a, byte[] b)    {             if (a.Length != b.Length)                    return false;             int i = 0;             foreach (byte c in a)        {                    if (c != b[i])                         return false;            i++;        }        return true;    }    #endregion}/// <summary>/// RSA算法类型/// </summary>

public enum RSAType {        /// <summary>    /// SHA1    /// </summary>    RSA = 0,    /// <summary>    /// RSA2 密钥长度至少为2048    /// SHA256    /// </summary>    RSA2 }     

使用:

static void Main(string[] args){    //2048 公钥string publicKey =        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoQh0wEqx/R2H1v00IU12Oc30fosRC/frhH89L6G+fzeaqI19MYQhEPMU13wpeqRONCUta+2iC1sgCNQ9qGGf19yGdZUfueaB1Nu9rdueQKXgVurGHJ+5N71UFm+OP1XcnFUCK4wT5d7ZIifXxuqLehP9Ts6sNjhVfa+yU+VjF5HoIe69OJEPo7OxRZcRTe17khc93Ic+PfyqswQJJlY/bgpcLJQnM+QuHmxNtF7/FpAx9YEQsShsGpVo7JaKgLo+s6AFoJ4QldQKir2vbN9vcKRbG3piElPilWDpjXQkOJZhUloh/jd7QrKFimZFldJ1r6Q59QYUyGKZARUe0KZpMQIDAQAB";    //2048 私钥string privateKey =        "MIIEpAIBAAKCAQEAoQh0wEqx/R2H1v00IU12Oc30fosRC/frhH89L6G+fzeaqI19MYQhEPMU13wpeqRONCUta+2iC1sgCNQ9qGGf19yGdZUfueaB1Nu9rdueQKXgVurGHJ+5N71UFm+OP1XcnFUCK4wT5d7ZIifXxuqLehP9Ts6sNjhVfa+yU+VjF5HoIe69OJEPo7OxRZcRTe17khc93Ic+PfyqswQJJlY/bgpcLJQnM+QuHmxNtF7/FpAx9YEQsShsGpVo7JaKgLo+s6AFoJ4QldQKir2vbN9vcKRbG3piElPilWDpjXQkOJZhUloh/jd7QrKFimZFldJ1r6Q59QYUyGKZARUe0KZpMQIDAQABAoIBAQCRZLUlOUvjIVqYvhznRK1OG6p45s8JY1r+UnPIId2Bt46oSLeUkZvZVeCnfq9k0Bzb8AVGwVPhtPEDh73z3dEYcT/lwjLXAkyPB6gG5ZfI/vvC/k7JYV01+neFmktw2/FIJWjEMMF2dvLNZ/Pm4bX1Dz9SfD/45Hwr8wqrvRzvFZsj5qqOxv9RPAudOYwCwZskKp/GF+L+3Ycod1Wu98imzMZUH+L5dQuDGg3kvf3ljIAegTPoqYBg0imNPYY/EGoFKnbxlK5S5/5uAFb16dGJqAz3XQCz9Is/IWrOTu0etteqV2Ncs8uqPdjed+b0j8CMsr4U1xjwPQ8WwdaJtTkRAoGBANAndgiGZkCVcc9975/AYdgFp35W6D+hGQAZlL6DmnucUFdXbWa/x2rTSEXlkvgk9X/PxOptUYsLJkzysTgfDywZwuIXLm9B3oNmv3bVgPXsgDsvDfaHYCgz0nHK6NSrX2AeX3yO/dFuoZsuk+J+UyRigMqYj0wjmxUlqj183hinAoGBAMYMOBgF77OXRII7GAuEut/nBeh2sBrgyzR7FmJMs5kvRh6Ck8wp3ysgMvX4lxh1ep8iCw1R2cguqNATr1klOdsCTOE9RrhuvOp3JrYzuIAK6MpH/uBICy4w1rW2+gQySsHcH40r+tNaTFQ7dQ1tef//iy/IW8v8i0t+csztE1JnAoGABdtWYt8FOYP688+jUmdjWWSvVcq0NjYeMfaGTOX/DsNTL2HyXhW/Uq4nNnBDNmAz2CjMbZwt0y+5ICkj+2REVQVUinAEinTcAe5+LKXNPx4sbX3hcrJUbk0m+rSu4G0B/f5cyXBsi9wFCAzDdHgBduCepxSr04Sc9Hde1uQQi7kCgYB0U20HP0Vh+TG2RLuE2HtjVDD2L/CUeQEiXEHzjxXWnhvTg+MIAnggvpLwQwmMxkQ2ACr5sd/3YuCpB0bxV5o594nsqq9FWVYBaecFEjAGlWHSnqMoXWijwu/6X/VOTbP3VjH6G6ECT4GR4DKKpokIQrMgZ9DzaezvdOA9WesFdQKBgQCWfeOQTitRJ0NZACFUn3Fs3Rvgc9eN9YSWj4RtqkmGPMPvguWo+SKhlk3IbYjrRBc5WVOdoX8JXb2/+nAGhPCuUZckWVmZe5pMSr4EkNQdYeY8kOXGSjoTOUH34ZdKeS+e399BkBWIiXUejX/Srln0H4KoHnTWgxwNpTsBCgXu8Q==";    var rsa = new RSAHelper(RSAType.RSA2,Encoding.UTF8, privateKey, publicKey);    string str = "博客园 http://www.cnblogs.com/";Console.WriteLine("原始字符串:"+str);    //加密string enStr = rsa.Encrypt(str);Console.WriteLine("加密字符串:"+enStr);    //解密string deStr = rsa.Decrypt(enStr);Console.WriteLine("解密字符串:"+deStr);    //私钥签名string signStr = rsa.Sign(str);Console.WriteLine("字符串签名:" + signStr);    //公钥验证签名bool signVerify = rsa.Verify(str,signStr);Console.WriteLine("验证签名:" + signVerify);Console.ReadKey();
}

运行:

参考

  • http://www.cnblogs.com/dudu/p/dotnet-core-rsa-openssl.html

本文Demo:https://github.com/stulzq/DotnetCore.RSA

.NET Core 交流群:4656606

原文地址:http://www.cnblogs.com/stulzq/p/7757915.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

.NET Core 使用RSA算法 加密/解密/签名/验证签名相关推荐

  1. RSA算法加密解密举例

    RSA算法加密解密举例 使用如下数字字母对照表: 明文M="its all greek to me" ,p=47,q=59,e=17,求出其密文以及给出RSA 算法加/解密过程.( ...

  2. 安全不安全002:C#实现RSA算法加密解密

    通过前面的文章我们学会了如何生成公钥和私钥,详见这篇文章:https://blog.csdn.net/yysyangyangyangshan/article/details/80368397. 那么, ...

  3. openssl 非对称加密 RSA 加密解密以及签名验证签名

    1. 简介 openssl  rsa.h 提供了密码学中公钥加密体系的一些接口, 本文主要讨论利用rsa.h接口开发以下功能 公钥私钥的生成 公钥加密,私钥解密 私钥加密,公钥解密 签名:私钥签名 验 ...

  4. java rsa 117_java实现RSA非对称加密解密

    之前写过一篇java实现AES对称加密解密 在对密码加密传输的场景下 RSA非对称加密解密可能会更加适合. 原理就是后台生成一对公钥和私钥,公钥给前端用来加密,后台用私钥去解密,保证了传输过程中就算被 ...

  5. JAVA RSA算法加密实现 eclipse

    RSA算法加密实现 一.实验目的 学习经典的RSA公钥加密算法的基本原理和特点,能够编写简单的代码实现RSA公钥加密和私钥解密的过程. 二.实验要求 1. 熟悉RSA公私钥加密算法. 2. 掌握如何使 ...

  6. 用RSA算法加密文本文件

    用RSA算法加密文本文件 写文目的 任务说明 代码实现 (1)RSA加密比较小的txt文件 (2)生成1M和1G的txt文件 (3)加密1M的txt文件 调试过程 写文目的 当时参照网上其他文章写的, ...

  7. RSA分段加密/解密 nodejs 和java联调

    RSA分段加密/解密 nodejs 和java联调 文章目录 RSA分段加密/解密 nodejs 和java联调 1 nodejs 环境 1.1 axios配置 1.2 API接口 1.3 业务 1. ...

  8. RSA算法加解密的C语言实现

    RSA算法加解密的C语言实现 一. 实现的功能 二. 源代码 一. 实现的功能 用户输入明文 自动随机生成较大的数p和q,并对它们进行素性检测,检测成功之后,程序继续 计算Φ(n)的值,并求出它的所有 ...

  9. Android-RSA算法加密解密

    被这个小问题困了2天,终于被干掉了. 安卓中利用RSA算法加密和解密 整个流程大致为:生成密钥对->获取公私钥->对明(密)文加(解)密. package com.cc.encryptte ...

最新文章

  1. 局域网共享文件夹设置
  2. linux切换root权限
  3. Jetpack:使用 ActivityResult 处理 Activity 之间的数据通信
  4. 【MySQL】系统命令与基础查询
  5. Linux 目录配置标准:FHS
  6. Pannellum:实例之在部分视角内展示全景图
  7. 求一个序列中,第k个数
  8. 泛泰A870 CWM Recovery En/Cn合集
  9. ATMEGA328P引脚定义图
  10. 如何查看wifi信息并对其进行管理?这些app帮你一键掌控你的wifi信息
  11. MATLAB图例设置为透明
  12. SDM439/SDM429/SDM450 Sensors Overview (80-PF208-11)
  13. Java中 字节转 KB/MB/GB
  14. 联想z5 Android 9.0,联想Z5开启Android 9.0内测,新增人脸识别!
  15. HTTP详解(请求方式)
  16. 基于ssm+vue的师生防疫登记管理系统 elementui
  17. ​不为人知的故事——主流加密背后的男人
  18. 电脑突然没声音了怎么办?
  19. 科技公司融资潮后怎么走?Snap公司40亿美元投石问路
  20. linux内核分析(转自某位大哥网上的笔记)

热门文章

  1. log4net日志插件的使用
  2. 单词计数WordCountApp.class
  3. LAMP攻略: LAMP环境搭建,Linux下Apache,MySQL,PHP安装与配置
  4. C#内建接口:IEnumerable
  5. Win11代言人官宣
  6. IEnumerable.OrderBy().First() 在 .netcore 3.1 中是否做了优化?
  7. 小米和腾讯的.NET笔面试题哪个更难?可自测附答案
  8. 快速排序的性能和名字一样优秀
  9. 如何在 C#9 中使用 static 匿名函数
  10. 一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试