关于C#的RSA加密

一般对接第三方的接口时,接口提供方如果要求以RSA方式进行加密传输,并且给了你一串字符串说是RSA加密公钥,那么该公钥一般是PEM格式文件的base64字符串表现形式。

完整PEM格式示例:

1:示例证书:

-----BEGIN RSA PRIVATE KEY-----

base64字符串

-----END RSA PRIVATE KEY-----

注意其格式,接口提供方有可能只给你中间的base64字符串,也有可能将完整的格式内容都给你,那么你要留意去除除了base64字符串内容之外的头尾以及空行。

那么问题来了,在C#的RSA加密工具类RSACryptoServiceProvider中,并没有支持PEM格式公钥为基准的加密方法,只能用一种.net平台认可的xml字符串的公钥才能够进行加密。

RSACryptoServiceProvider _rsa = new RSACryptoServiceProvider();
_rsa.FromXmlString(publickey);//这里的公钥指定只能是平台认可的xml字符串格式,直接传入pem的base64是不行的
byte[] cipherbytes = _rsa.Encrypt(_b, false);//进行加密

想要从pem公钥转为xml公钥,只能依赖于一个第三方库,叫做BouncyCastle

然后用以下方法进行转换:

///     
/// RSA公钥pem的base64字符串-->XML格式字符串转换, 
///     
/// pem公钥base64字符串    
///     
public static string RSAPublicKey(string publicKey)
{
    RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
    string XML = string.Format("{0}{1}",
    Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
    Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
    return XML;
}

另外如果在之后加密时报长度超长之类的错误,说明需要分段加密,因为RSA的加密机制要求:待加密的字节数不能超过密钥的长度值除以 8 再减去 11。

可以用如下方法进行分段加密:

/// 
/// RSA加密并且返回加密后内容的base64字符串
/// 
/// 公钥xml格式字符串
/// 加密内容
/// 
public static string RSAEncrypt(string publickey, string content)
{
    RSACryptoServiceProvider _rsa = new RSACryptoServiceProvider();
    _rsa.FromXmlString(publickey);
    var _b = Encoding.Default.GetBytes(content);
    //将字符编码转为utf8
    _b = Encoding.Convert(Encoding.Default, new UTF8Encoding(), _b);
    int _maxSize = _rsa.KeySize/8-11;
    if (_b.Length <= _maxSize)
    {
        byte[] cipherbytes = _rsa.Encrypt(_b, false);
        return Convert.ToBase64String(cipherbytes);
    }
    else//分块加密
    {
        using (MemoryStream PlaiStream = new MemoryStream(_b))
        using (MemoryStream CrypStream = new MemoryStream())
        {
            Byte[] Buffer = new Byte[_maxSize];
            int BlockSize = PlaiStream.Read(Buffer, 0, _maxSize);

            while (BlockSize > 0)
            {
                Byte[] ToEncrypt = new Byte[BlockSize];
                Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);

                Byte[] Cryptograph = _rsa.Encrypt(ToEncrypt, false);
                CrypStream.Write(Cryptograph, 0, Cryptograph.Length);

                BlockSize = PlaiStream.Read(Buffer, 0, _maxSize);
            }

            return Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
        }
    }
}

但是上面的加密方式有一个问题,就是同样的待加密内容和公钥,每次加密之后的结果都不相同,虽然都能用私钥进行解密。如果是那种涉及到MD5验证之类的摘要算法进行验证,那么这种“加密结果不固定”的加密方式就不可取了。

之所以出现这种问题是与RSA加密算法的“填充(Padding)”有关,这里的东西我也不懂,所以略过,有兴趣的同学可以深入研究一下。

所以基于这种情况,下面的方法就是一种无填充的公钥加密方式,可以让每一次的加密结果都一致:

/// 
        /// RSA公钥加密(无填充,结果不变)
        /// 
        /// 待加密内容的字节数组
        /// 公钥(无pem格式头尾,仅base64部分)
        /// 加密结果的字节数组
        public byte[] RsaEncryptWithPublic(byte[] srcData, string publicKey)
        {
            var encryptEngine = new RsaEngine(); // new Pkcs1Encoding (new RsaEngine());
            using (var txtreader = new StringReader("-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----"))
            {
                var keyParameter = (AsymmetricKeyParameter)new Org.BouncyCastle.OpenSsl.PemReader(txtreader).ReadObject();
                encryptEngine.Init(true, keyParameter);
            }
            byte[] _resultBytes;
            var _maxSize = encryptEngine.GetInputBlockSize();
            if (srcData.Length <= _maxSize)
            {
                _resultBytes = encryptEngine.ProcessBlock(srcData, 0, srcData.Length);
            }
            else//分块加密
            {
                using (MemoryStream PlaiStream = new MemoryStream(srcData))
                using (MemoryStream CrypStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[_maxSize];
                    int BlockSize = PlaiStream.Read(Buffer, 0, _maxSize);

                    while (BlockSize > 0)
                    {
                        Byte[] ToEncrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);

                        Byte[] Cryptograph = encryptEngine.ProcessBlock(ToEncrypt, 0, ToEncrypt.Length);
                        CrypStream.Write(Cryptograph, 0, Cryptograph.Length);

                        BlockSize = PlaiStream.Read(Buffer, 0, _maxSize);
                    }

                    _resultBytes = CrypStream.ToArray();
                }
            }
            return _resultBytes;
        }

你可能感兴趣的:(.net,c#,RSA加密,c#RSA加密,公钥加密)