using System; using System.Text; using System.Security.Cryptography; using System.IO; namespace PaySharp.Alipay.Util.Asymmetric { /// /// RSA算法加密器 /// 签名部分采用SHA1算法进行摘要计算 /// public class RSAEncryptor : BaseAsymmetricEncryptor { /// /// RSA算法签名采用SHA1摘要算法 /// /// 摘要算法名称 protected virtual string GetShaType() { return "SHA1"; } protected override string GetAsymmetricType() { return "RSA"; } protected override string DoDecrypt(string cipherTextBase64, string charset, string privateKey) { using (RSACryptoServiceProvider rsaService = BuildRSAServiceProvider(Convert.FromBase64String(privateKey))) { byte[] data = Convert.FromBase64String(cipherTextBase64); //解密块最大长度 int maxBlockSize = rsaService.KeySize / 8; //如果密文长度小于等于单个解密块最大长度,直接单次调用解密接口完成解密 if (data.Length <= maxBlockSize) { byte[] cipherbytes = rsaService.Decrypt(data, false); return Encoding.GetEncoding(charset).GetString(cipherbytes); } //如果密文长度大于单个解密块最大长度,在内存中循环调用解密接口完成解密 using (MemoryStream plainStream = new MemoryStream()) { using (MemoryStream cipherStream = new MemoryStream(data)) { Byte[] buffer = new Byte[maxBlockSize]; int readSize = cipherStream.Read(buffer, 0, maxBlockSize); while (readSize > 0) { Byte[] cipherBlock = new Byte[readSize]; Array.Copy(buffer, 0, cipherBlock, 0, readSize); Byte[] plainBlock = rsaService.Decrypt(cipherBlock, false); plainStream.Write(plainBlock, 0, plainBlock.Length); readSize = cipherStream.Read(buffer, 0, maxBlockSize); } } return Encoding.GetEncoding(charset).GetString(plainStream.ToArray()); } } } protected override string DoEncrypt(string plainText, string charset, string publicKey) { using (RSACryptoServiceProvider rsaService = new RSACryptoServiceProvider()) { rsaService.PersistKeyInCsp = false; rsaService.ImportParameters(ConvertFromPemPublicKey(publicKey)); byte[] data = Encoding.GetEncoding(charset).GetBytes(plainText); //加密块最大长度 int maxBlockSize = rsaService.KeySize / 8 - 11; //如果明文长度小于等于单个加密块最大长度,直接单次调用加密接口完成加密 if (data.Length <= maxBlockSize) { byte[] cipherbytes = rsaService.Encrypt(data, false); return Convert.ToBase64String(cipherbytes); } //如果明文长度大于单个加密块最大长度,在内存中循环调用加密接口完成加密 using (MemoryStream cipherStream = new MemoryStream()) { using (MemoryStream plainStream = new MemoryStream(data)) { Byte[] buffer = new Byte[maxBlockSize]; int readSize = plainStream.Read(buffer, 0, maxBlockSize); while (readSize > 0) { Byte[] plainBlock = new Byte[readSize]; Array.Copy(buffer, 0, plainBlock, 0, readSize); Byte[] cipherBlock = rsaService.Encrypt(plainBlock, false); cipherStream.Write(cipherBlock, 0, cipherBlock.Length); readSize = plainStream.Read(buffer, 0, maxBlockSize); } } return Convert.ToBase64String(cipherStream.ToArray(), Base64FormattingOptions.None); } } } protected override string DoSign(string content, string charset, string privateKey) { using (RSACryptoServiceProvider rsaService = BuildRSAServiceProvider(Convert.FromBase64String(privateKey))) { byte[] data = Encoding.GetEncoding(charset).GetBytes(content); byte[] sign = rsaService.SignData(data, GetShaType()); return Convert.ToBase64String(sign); } } protected override bool DoVerify(string content, string charset, string publicKey, string sign) { using (RSACryptoServiceProvider rsaService = new RSACryptoServiceProvider()) { rsaService.PersistKeyInCsp = false; rsaService.ImportParameters(ConvertFromPemPublicKey(publicKey)); return rsaService.VerifyData(Encoding.GetEncoding(charset).GetBytes(content), GetShaType(), Convert.FromBase64String(sign)); } } private RSAParameters ConvertFromPemPublicKey(string pemPublickKey) { if (string.IsNullOrEmpty(pemPublickKey)) { throw new Exception("PEM格式公钥不可为空。"); } //移除干扰文本 pemPublickKey = pemPublickKey.Replace("-----BEGIN PUBLIC KEY-----", "") .Replace("-----END PUBLIC KEY-----", "").Replace("\n", "").Replace("\r", ""); byte[] keyData = Convert.FromBase64String(pemPublickKey); bool keySize1024 = (keyData.Length == 162); bool keySize2048 = (keyData.Length == 294); if (!(keySize1024 || keySize2048)) { throw new Exception("公钥长度只支持1024和2048。"); } byte[] pemModulus = (keySize1024 ? new byte[128] : new byte[256]); byte[] pemPublicExponent = new byte[3]; Array.Copy(keyData, (keySize1024 ? 29 : 33), pemModulus, 0, (keySize1024 ? 128 : 256)); Array.Copy(keyData, (keySize1024 ? 159 : 291), pemPublicExponent, 0, 3); RSAParameters para = new RSAParameters(); para.Modulus = pemModulus; para.Exponent = pemPublicExponent; return para; } private static RSACryptoServiceProvider BuildRSAServiceProvider(byte[] privateKey) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; byte bt = 0; ushort twobytes = 0; int elems = 0; //set up stream to decode the asn.1 encoded RSA private key //wrap Memory Stream with BinaryReader for easy reading using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(privateKey))) { twobytes = binaryReader.ReadUInt16(); //data read as little endian order (actual data order for Sequence is 30 81) if (twobytes == 0x8130) { //advance 1 byte binaryReader.ReadByte(); } else if (twobytes == 0x8230) { //advance 2 bytes binaryReader.ReadInt16(); } else { return null; } twobytes = binaryReader.ReadUInt16(); //version number if (twobytes != 0x0102) { return null; } bt = binaryReader.ReadByte(); if (bt != 0x00) { return null; } //all private key components are Integer sequences elems = GetIntegerSize(binaryReader); MODULUS = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); E = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); D = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); P = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); Q = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); DP = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); DQ = binaryReader.ReadBytes(elems); elems = GetIntegerSize(binaryReader); IQ = binaryReader.ReadBytes(elems); //create RSACryptoServiceProvider instance and initialize with public key RSACryptoServiceProvider rsaService = new RSACryptoServiceProvider(); RSAParameters rsaParams = new RSAParameters { Modulus = MODULUS, Exponent = E, D = D, P = P, Q = Q, DP = DP, DQ = DQ, InverseQ = IQ }; rsaService.ImportParameters(rsaParams); return rsaService; } } private static int GetIntegerSize(BinaryReader binaryReader) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binaryReader.ReadByte(); //expect integer if (bt != 0x02) { return 0; } bt = binaryReader.ReadByte(); if (bt == 0x81) { //data size in next byte count = binaryReader.ReadByte(); } else if (bt == 0x82) { //data size in next 2 bytes highbyte = binaryReader.ReadByte(); lowbyte = binaryReader.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else { //we already have the data size count = bt; } while (binaryReader.ReadByte() == 0x00) { //remove high order zeros in data count -= 1; } //last ReadByte wasn't a removed zero, so back up a byte binaryReader.BaseStream.Seek(-1, SeekOrigin.Current); return count; } } }