using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; namespace Alipay.AopSdk.Core.Util { public class AlipaySignature { /** 默认编码字符集 */ private static readonly string DEFAULT_CHARSET = "utf-8"; public static string GetSignContent(IDictionary parameters) { // 第一步:把字典按Key的字母顺序排序 IDictionary sortedParams = new SortedDictionary(parameters); var dem = sortedParams.GetEnumerator(); // 第二步:把所有参数名和参数值串在一起 var query = new StringBuilder(""); while (dem.MoveNext()) { var key = dem.Current.Key; var value = dem.Current.Value; if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) query.Append(key).Append("=").Append(value).Append("&"); } var content = query.ToString().Substring(0, query.Length - 1); return content; } public static string RSASign(IDictionary parameters, string privateKeyPem, string charset, string signType) { var signContent = GetSignContent(parameters); return RSASignCharSet(signContent, privateKeyPem, charset, signType); } public static string RSASign(string data, string privateKeyPem, string charset, string signType) { return RSASignCharSet(data, privateKeyPem, charset, signType); } ///* public static string RSASign(IDictionary parameters, string privateKeyPem, string charset, bool keyFromFile, string signType) { var signContent = GetSignContent(parameters); return RSASignCharSet(signContent, privateKeyPem, charset, keyFromFile, signType); } public static string RSASign(string data, string privateKeyPem, string charset, string signType, bool keyFromFile) { return RSASignCharSet(data, privateKeyPem, charset, keyFromFile, signType); } //*/ public static string RSASignCharSet(string data, string privateKeyPem, string charset, string signType) { RSA rsaCsp = LoadCertificateFile(privateKeyPem, signType); byte[] dataBytes = null; if (string.IsNullOrEmpty(charset)) dataBytes = Encoding.UTF8.GetBytes(data); else dataBytes = Encoding.GetEncoding(charset).GetBytes(data); var signatureBytes = rsaCsp.SignData(dataBytes, "RSA2".Equals(signType) ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); return Convert.ToBase64String(signatureBytes); } public static string RSASignCharSet(string data, string privateKeyPem, string charset, bool keyFromFile, string signType) { byte[] signatureBytes = null; try { RSA rsaCsp = null; rsaCsp = keyFromFile ? LoadCertificateFile(privateKeyPem, signType) : LoadCertificateString(privateKeyPem, signType); byte[] dataBytes = null; if (string.IsNullOrEmpty(charset)) dataBytes = Encoding.UTF8.GetBytes(data); else dataBytes = Encoding.GetEncoding(charset).GetBytes(data); if (null == rsaCsp) throw new AopException("您使用的私钥格式错误,请检查RSA私钥配置" + ",charset = " + charset); signatureBytes = rsaCsp.SignData(dataBytes, "RSA2".Equals(signType) ? HashAlgorithmName.SHA256 : HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); } catch (Exception ex) { throw new AopException($"您使用的私钥格式错误,请检查RSA私钥配置,charset = {charset},异常信息:{ex.Message}", ex); } return Convert.ToBase64String(signatureBytes); } public static bool RSACheckV1(IDictionary parameters, string publicKeyPem, string charset) { var sign = parameters["sign"]; parameters.Remove("sign"); parameters.Remove("sign_type"); var signContent = GetSignContent(parameters); return RSACheckContent(signContent, sign, publicKeyPem, charset, "RSA"); } public static bool RSACheckV1(IDictionary parameters, string publicKeyPem) { var sign = parameters["sign"]; parameters.Remove("sign"); parameters.Remove("sign_type"); var signContent = GetSignContent(parameters); return RSACheckContent(signContent, sign, publicKeyPem, DEFAULT_CHARSET, "RSA"); } public static bool RSA2Check(IDictionary parameters, string publicKeyPem) { var sign = parameters["sign"]; parameters.Remove("sign"); parameters.Remove("sign_type"); var signContent = GetSignContent(parameters); return RSACheckContent(signContent, sign, publicKeyPem, DEFAULT_CHARSET, "RSA2"); } public static bool RSACheckV1(IDictionary parameters, string publicKeyPem, string charset, string signType, bool keyFromFile) { var sign = parameters["sign"]; parameters.Remove("sign"); parameters.Remove("sign_type"); var signContent = GetSignContent(parameters); return RSACheckContent(signContent, sign, publicKeyPem, charset, signType, keyFromFile); } public static bool RSACheckV2(IDictionary parameters, string publicKeyPem, string charset) { var sign = parameters["sign"]; parameters.Remove("sign"); parameters.Remove("sign_type"); var signContent = GetSignContent(parameters); return RSACheckContent(signContent, sign, publicKeyPem, charset, "RSA"); } public static bool RSACheckV2(IDictionary parameters, string publicKeyPem, string charset, string signType, bool keyFromFile) { var sign = parameters["sign"]; parameters.Remove("sign"); parameters.Remove("sign_type"); var signContent = GetSignContent(parameters); return RSACheckContent(signContent, sign, publicKeyPem, charset, signType, keyFromFile); } public static RSA CreateRsaProviderFromPublicKey(string publicKeyString,string signType) { // 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 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; seq = binr.ReadBytes(15); //read the Sequence OID if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct return 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 byte else if (twobytes == 0x8203) binr.ReadInt16(); //advance 2 bytes else return null; bt = binr.ReadByte(); if (bt != 0x00) //expect null byte next return 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 byte else 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(); rsa.KeySize = signType == "RSA" ? 1024 : 2048; RSAParameters rsaKeyInfo = new RSAParameters(); rsaKeyInfo.Modulus = modulus; rsaKeyInfo.Exponent = exponent; rsa.ImportParameters(rsaKeyInfo); return rsa; } } } private static 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; } public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, string signType) { try { if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; var sPublicKeyPem = File.ReadAllText(publicKeyPem); var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, signType); if ("RSA2".Equals(signType)) { var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), Convert.FromBase64String(sign),HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return bVerifyResultOriginal; } else { var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), Convert.FromBase64String(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); return bVerifyResultOriginal; } } catch { return false; } } public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, string signType, bool keyFromFile) { try { if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; string sPublicKeyPem= publicKeyPem; if (keyFromFile) { sPublicKeyPem = File.ReadAllText(publicKeyPem); } var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, signType); if ("RSA2".Equals(signType)) { var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), Convert.FromBase64String(sign), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return bVerifyResultOriginal; } else { var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), Convert.FromBase64String(sign), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); return bVerifyResultOriginal; } } catch { return false; } } public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, bool keyFromFile) { try { string sPublicKeyPem= publicKeyPem; if (keyFromFile) { sPublicKeyPem = File.ReadAllText(publicKeyPem); } var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPem, "RSA"); var sha1 = new SHA1CryptoServiceProvider(); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; var bVerifyResultOriginal = rsa.VerifyData(Encoding.GetEncoding(charset).GetBytes(signContent), Convert.FromBase64String(sign),HashAlgorithmName.SHA1,RSASignaturePadding.Pkcs1); return bVerifyResultOriginal; } catch (Exception ex) { var s = ex.Message; return false; } } public static string CheckSignAndDecrypt(IDictionary parameters, string alipayPublicKey, string cusPrivateKey, bool isCheckSign, bool isDecrypt) { var charset = parameters["charset"]; var bizContent = parameters["biz_content"]; if (isCheckSign) if (!RSACheckV2(parameters, alipayPublicKey, charset)) throw new AopException("rsaCheck failure:rsaParams=" + parameters); if (isDecrypt) return RSADecrypt(bizContent, cusPrivateKey, charset, "RSA"); return bizContent; } public static string CheckSignAndDecrypt(IDictionary parameters, string alipayPublicKey, string cusPrivateKey, bool isCheckSign, bool isDecrypt, string signType, bool keyFromFile) { var charset = parameters["charset"]; var bizContent = parameters["biz_content"]; if (isCheckSign) if (!RSACheckV2(parameters, alipayPublicKey, charset, signType, keyFromFile)) throw new AopException("rsaCheck failure:rsaParams=" + parameters); if (isDecrypt) return RSADecrypt(bizContent, cusPrivateKey, charset, signType, keyFromFile); return bizContent; } public static string encryptAndSign(string bizContent, string alipayPublicKey, string cusPrivateKey, string charset, bool isEncrypt, bool isSign, string signType, bool keyFromFile) { var sb = new StringBuilder(); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; sb.Append(""); if (isEncrypt) { // 加密 sb.Append(""); var encrypted = RSAEncrypt(bizContent, alipayPublicKey, charset, keyFromFile); sb.Append("" + encrypted + ""); sb.Append("" + signType + ""); if (isSign) { var sign = RSASign(encrypted, cusPrivateKey, charset, signType, keyFromFile); sb.Append("" + sign + ""); sb.Append("" + signType + ""); } sb.Append(""); } else if (isSign) { // 不加密,但需要签名 sb.Append(""); sb.Append("" + bizContent + ""); var sign = RSASign(bizContent, cusPrivateKey, charset, signType, keyFromFile); sb.Append("" + sign + ""); sb.Append("" + signType + ""); sb.Append(""); } else { // 不加密,不加签 sb.Append(bizContent); } return sb.ToString(); } public static string encryptAndSign(string bizContent, string alipayPublicKey, string cusPrivateKey, string charset, bool isEncrypt, bool isSign) { var sb = new StringBuilder(); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; sb.Append(""); if (isEncrypt) { // 加密 sb.Append(""); var encrypted = RSAEncrypt(bizContent, alipayPublicKey, charset); sb.Append("" + encrypted + ""); sb.Append("RSA"); if (isSign) { var sign = RSASign(encrypted, cusPrivateKey, charset, "RSA"); sb.Append("" + sign + ""); sb.Append("RSA"); } sb.Append(""); } else if (isSign) { // 不加密,但需要签名 sb.Append(""); sb.Append("" + bizContent + ""); var sign = RSASign(bizContent, cusPrivateKey, charset, "RSA"); sb.Append("" + sign + ""); sb.Append("RSA"); sb.Append(""); } else { // 不加密,不加签 sb.Append(bizContent); } return sb.ToString(); } public static string RSAEncrypt(string content, string publicKeyPem, string charset) { try { var sPublicKeyPEM = File.ReadAllText(publicKeyPem); var rsa = CreateRsaProviderFromPublicKey(sPublicKeyPEM, "RSA"); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; var data = Encoding.GetEncoding(charset).GetBytes(content); var maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制 if (data.Length <= maxBlockSize) { var cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1); return Convert.ToBase64String(cipherbytes); } var plaiStream = new MemoryStream(data); var crypStream = new MemoryStream(); var buffer = new byte[maxBlockSize]; var blockSize = plaiStream.Read(buffer, 0, maxBlockSize); while (blockSize > 0) { var toEncrypt = new byte[blockSize]; Array.Copy(buffer, 0, toEncrypt, 0, blockSize); var cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1); crypStream.Write(cryptograph, 0, cryptograph.Length); blockSize = plaiStream.Read(buffer, 0, maxBlockSize); } return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None); } catch (Exception ex) { throw new AopException("EncryptContent = " + content + ",charset = " + charset, ex); } } public static string RSAEncrypt(string content, string publicKeyPem, string charset, bool keyFromFile) { try { string sPublicKeyPEM= publicKeyPem; if (keyFromFile) { sPublicKeyPEM = File.ReadAllText(publicKeyPem); } var rsa = CreateRsaProviderFromPublicKey(publicKeyPem, "RSA"); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; var data = Encoding.GetEncoding(charset).GetBytes(content); var maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制 if (data.Length <= maxBlockSize) { var cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1); return Convert.ToBase64String(cipherbytes); } var plaiStream = new MemoryStream(data); var crypStream = new MemoryStream(); var buffer = new byte[maxBlockSize]; var blockSize = plaiStream.Read(buffer, 0, maxBlockSize); while (blockSize > 0) { var toEncrypt = new byte[blockSize]; Array.Copy(buffer, 0, toEncrypt, 0, blockSize); var cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1); crypStream.Write(cryptograph, 0, cryptograph.Length); blockSize = plaiStream.Read(buffer, 0, maxBlockSize); } return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None); } catch (Exception ex) { throw new AopException("EncryptContent = " + content + ",charset = " + charset, ex); } } public static string RSADecrypt(string content, string privateKeyPem, string charset, string signType) { try { var rsaCsp = LoadCertificateFile(privateKeyPem, signType); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; var data = Convert.FromBase64String(content); var maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制 if (data.Length <= maxBlockSize) { var cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1); return Encoding.GetEncoding(charset).GetString(cipherbytes); } var crypStream = new MemoryStream(data); var plaiStream = new MemoryStream(); var buffer = new byte[maxBlockSize]; var blockSize = crypStream.Read(buffer, 0, maxBlockSize); while (blockSize > 0) { var toDecrypt = new byte[blockSize]; Array.Copy(buffer, 0, toDecrypt, 0, blockSize); var cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1); plaiStream.Write(cryptograph, 0, cryptograph.Length); blockSize = crypStream.Read(buffer, 0, maxBlockSize); } return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray()); } catch (Exception ex) { throw new AopException("DecryptContent = " + content + ",charset = " + charset, ex); } } public static string RSADecrypt(string content, string privateKeyPem, string charset, string signType, bool keyFromFile) { try { RSA rsaCsp = null; if (keyFromFile) rsaCsp = LoadCertificateFile(privateKeyPem, signType); else rsaCsp = LoadCertificateString(privateKeyPem, signType); if (string.IsNullOrEmpty(charset)) charset = DEFAULT_CHARSET; var data = Convert.FromBase64String(content); var maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制 if (data.Length <= maxBlockSize) { var cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1); return Encoding.GetEncoding(charset).GetString(cipherbytes); } var crypStream = new MemoryStream(data); var plaiStream = new MemoryStream(); var buffer = new byte[maxBlockSize]; var blockSize = crypStream.Read(buffer, 0, maxBlockSize); while (blockSize > 0) { var toDecrypt = new byte[blockSize]; Array.Copy(buffer, 0, toDecrypt, 0, blockSize); var cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1); plaiStream.Write(cryptograph, 0, cryptograph.Length); blockSize = crypStream.Read(buffer, 0, maxBlockSize); } return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray()); } catch (Exception ex) { throw new AopException("DecryptContent = " + content + ",charset = " + charset, ex); } } private static byte[] GetPem(string type, byte[] data) { var pem = Encoding.UTF8.GetString(data); var header = string.Format("-----BEGIN {0}-----\\n", type); var footer = string.Format("-----END {0}-----", type); var start = pem.IndexOf(header) + header.Length; var end = pem.IndexOf(footer, start); var base64 = pem.Substring(start, end - start); return Convert.FromBase64String(base64); } private static RSA LoadCertificateFile(string filename, string signType) { using (var fs = File.OpenRead(filename)) { var data = new byte[fs.Length]; byte[] res = null; fs.Read(data, 0, data.Length); if (data[0] != 0x30) res = GetPem("RSA PRIVATE KEY", data); try { var rsa = DecodeRSAPrivateKey(res, signType); return rsa; } catch (Exception ex) { } return null; } } public static RSA LoadCertificateString(string strKey, string signType) { byte[] data = null; //读取带 //ata = Encoding.Default.GetBytes(strKey); data = Convert.FromBase64String(strKey); //data = GetPem("RSA PRIVATE KEY", data); try { var rsa = DecodeRSAPrivateKey(data, signType); return rsa; } catch (Exception ex) { throw new AopException("Alipay.AopSdk.Core.Util.AlipaySignature LoadCertificateString DecodeRSAPrivateKey Error", ex); } return null; } private static RSA DecodeRSAPrivateKey(byte[] privkey, string signType) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key ------ var mem = new MemoryStream(privkey); var binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; var elems = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; twobytes = binr.ReadUInt16(); if (twobytes != 0x0102) //version number return null; bt = binr.ReadByte(); if (bt != 0x00) return null; //------ all private key components are Integer sequences ---- elems = GetIntegerSize(binr); MODULUS = binr.ReadBytes(elems); elems = GetIntegerSize(binr); E = binr.ReadBytes(elems); elems = GetIntegerSize(binr); D = binr.ReadBytes(elems); elems = GetIntegerSize(binr); P = binr.ReadBytes(elems); elems = GetIntegerSize(binr); Q = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DP = binr.ReadBytes(elems); elems = GetIntegerSize(binr); DQ = binr.ReadBytes(elems); elems = GetIntegerSize(binr); IQ = binr.ReadBytes(elems); // ------- create RSACryptoServiceProvider instance and initialize with public key ----- var CspParameters = new CspParameters(); CspParameters.Flags = CspProviderFlags.UseMachineKeyStore; var bitLen = 1024; if ("RSA2".Equals(signType)) bitLen = 2048; var rsa = RSA.Create(); rsa.KeySize = bitLen; var rsAparams = new RSAParameters(); rsAparams.Modulus = MODULUS; rsAparams.Exponent = E; rsAparams.D = D; rsAparams.P = P; rsAparams.Q = Q; rsAparams.DP = DP; rsAparams.DQ = DQ; rsAparams.InverseQ = IQ; rsa.ImportParameters(rsAparams); return rsa; } catch (Exception ex) { return null; } finally { binr.Close(); } } private static int GetIntegerSize(BinaryReader binr) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; var count = 0; bt = binr.ReadByte(); if (bt != 0x02) //expect integer return 0; bt = binr.ReadByte(); if (bt == 0x81) { count = binr.ReadByte(); // data size in next byte } else if (bt == 0x82) { highbyte = binr.ReadByte(); // data size in next 2 bytes lowbyte = binr.ReadByte(); byte[] modint = {lowbyte, highbyte, 0x00, 0x00}; count = BitConverter.ToInt32(modint, 0); } else { count = bt; // we already have the data size } while (binr.ReadByte() == 0x00) //remove high order zeros in data count -= 1; binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte return count; } } }