chengkun
2025-06-05 4080b5997b38ca84b3b203c7101dcadb97b76925
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Collections.Generic;
 
namespace Alipay.EasySDK.Kernel.Util
{
    /// <summary>
    /// SHA256WithRSA签名器
    /// </summary>
    public class Signer
    {
        /// <summary>
        /// 计算签名
        /// </summary>
        /// <param name="content">待签名的内容</param>
        /// <param name="privateKeyPem">私钥</param>
        /// <returns>签名值的Base64串</returns>
        public static string Sign(string content, string privateKeyPem)
        {
            try
            {
                using (RSACryptoServiceProvider rsaService = BuildRSAServiceProvider(Convert.FromBase64String(privateKeyPem)))
                {
                    byte[] data = AlipayConstants.DEFAULT_CHARSET.GetBytes(content);
                    byte[] sign = rsaService.SignData(data, "SHA256");
                    return Convert.ToBase64String(sign);
                }
            }
            catch (Exception e)
            {
                string errorMessage = "签名遭遇异常,content=" + content + " privateKeySize=" + privateKeyPem.Length + " reason=" + e.Message;
                Console.WriteLine(errorMessage);
                throw new Exception(errorMessage, e);
            }
        }
 
        /// <summary>
        /// 验证签名
        /// </summary>
        /// <param name="content">待验签的内容</param>
        /// <param name="sign">签名值的Base64串</param>
        /// <param name="publicKeyPem">支付宝公钥</param>
        /// <returns>true:验证成功;false:验证失败</returns>
        public static bool Verify(string content, string sign, string publicKeyPem)
        {
            try
            {
                using (RSACryptoServiceProvider rsaService = new RSACryptoServiceProvider())
                {
                    rsaService.PersistKeyInCsp = false;
                    rsaService.ImportParameters(ConvertFromPemPublicKey(publicKeyPem));
                    return rsaService.VerifyData(AlipayConstants.DEFAULT_CHARSET.GetBytes(content),
                        "SHA256", Convert.FromBase64String(sign));
                }
            }
            catch (Exception e)
            {
                string errorMessage = "验签遭遇异常,content=" + content + " sign=" + sign +
                   " publicKey=" + publicKeyPem + " reason=" + e.Message;
                Console.WriteLine(errorMessage);
                throw new Exception(errorMessage, e);
            }
 
        }
 
        /// <summary>
        /// 对参数集合进行验签
        /// </summary>
        /// <param name="parameters">参数集合</param>
        /// <param name="publicKeyPem">支付宝公钥</param>
        /// <returns>true:验证成功;false:验证失败</returns>
        public static bool VerifyParams(Dictionary<string, string> parameters, string publicKeyPem)
        {
            string sign = parameters[AlipayConstants.SIGN_FIELD];
            parameters.Remove(AlipayConstants.SIGN_FIELD);
            parameters.Remove(AlipayConstants.SIGN_TYPE_FIELD);
 
            string content = GetSignContent(parameters);
 
            return Verify(content, sign, publicKeyPem);
        }
 
        private static string GetSignContent(IDictionary<string, string> parameters)
        {
            // 把字典按Key的字母顺序排序
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);
            IEnumerator<KeyValuePair<string, string>> iterator = sortedParams.GetEnumerator();
 
            // 把所有参数名和参数值串在一起
            StringBuilder query = new StringBuilder("");
            while (iterator.MoveNext())
            {
                string key = iterator.Current.Key;
                string value = iterator.Current.Value;
                query.Append(key).Append("=").Append(value).Append("&");
            }
            string content = query.ToString().Substring(0, query.Length - 1);
 
            return content;
        }
 
        private static 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
            {
                Modulus = pemModulus,
                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;
        }
    }
}