在前面文章我们已经可以制作SM2证书了,主要应用了SM2签名验证算法和SM3摘要算法,在本文中主要介绍SM2公钥加密算法。这里我们使用SM2数字证书来做SM2非对称加密,然后使用硬件加密设备做解密,比如加密文件只能由指定的智能密码钥匙UKey才能解开。
SM2加密算法
SM2加密同样使用接收方公钥加密,公钥由一个曲线坐标点组成,在X.509证书中的共钥表示为04标记开始的2个32byte的BigInteger,即曲线点P(x,y)。SM2公钥加密算法比RSA相对复杂,加密结果由3个部分组成,SM2加密过程中使用了随机数,因此同样的明文数据每一次加密结果都不一样。SM2加密算法流程如下图所示。
根据国密推荐的SM2椭圆曲线公钥密码算法,首先产生随机数计算出曲线点C1,2个32byte的BigInteger大数,即为SM2加密结果的第1部分。第2部分则是真正的密文,是对明文的加密结果,长度和明文一样。第3部分是杂凑值,用来效验数据。按国密推荐的256位椭圆曲线,明文加密结果比原长度会大96byte。
SM2加密算法同样也可以基于使用BouncyCastle库实现。一般使用数字证书来标识身份,同时使用证书中公钥加密数据。如下SM2Cipher类是C#下SM2软算法实现。
SM2Cipher.cs 1publicclass SM2Cipher
2...{
3
privateint ct =1;
4
5
private ECPoint p2;
6
private SM3Digest sm3keybase;
7
private SM3Digest sm3c3;
8
9
privatebyte[] key =newbyte[32];
10
privatebyte keyOff =0;
11
12
public SM2Cipher() ...{ }
13
14
15
16
privatevoid Reset()
17
...{
18
sm3keybase =new SM3Digest();
19
sm3c3 =new SM3Digest();
20
21
byte[] p;
22
23
p = p2.X.ToBigInteger().ToByteArrayUnsigned();
24
sm3keybase.BlockUpdate(p, 0, p.Length);
25
sm3c3.BlockUpdate(p, 0, p.Length);
26
27
p = p2.Y.ToBigInteger().ToByteArrayUnsigned();
28
sm3keybase.BlockUpdate(p, 0, p.Length);
29
30
ct =1;
31
NextKey();
32
}
33
34
privatevoid NextKey()
35
...{
36
SM3Digest sm3keycur =new SM3Digest(sm3keybase);
37
sm3keycur.Update((byte)(ct >>24&0x00ff));
38
sm3keycur.Update((byte)(ct >>16&0x00ff));
39
sm3keycur.Update((byte)(ct >>8&0x00ff));
40
sm3keycur.Update((byte)(ct &0x00ff));
41
sm3keycur.DoFinal(key, 0);
42
keyOff =0;
43
ct++;
44
}
45
46
publicvirtual ECPoint InitEncipher(ECPoint userKey)
47
...{
48
BigInteger k =null;
49
ECPoint c1 =null;
50
51
if (1==1)
52
...{
53
AsymmetricCipherKeyPair key = SM2CryptoServiceProvider.SM2KeyPairGenerator.GenerateKeyPair();
54
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;
55
ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;
56
57
k = ecpriv.D;
58
c1 = ecpub.Q;
59
}
60
61
p2 = userKey.Multiply(k);
62
Reset();
63
64
return c1;
65
66
}
67
68
publicvirtualvoid Encrypt(byte[] data)
69
...{
70
sm3c3.BlockUpdate(data, 0, data.Length);
71
for (int i =0; i < data.Length; i++)
72
...{
73
if (keyOff == key.Length)
74
NextKey();
75
76
data[i] ^= key[keyOff++];
77
}
78
}
79
80
publicvirtualvoid InitDecipher(BigInteger userD, ECPoint c1)
81
...{
82
p2 = c1.Multiply(userD);
83
Reset();
84
}
85
86
publicvirtualvoid Decrypt(byte[] data)
87
...{
88
for (int i =0; i < data.Length; i++)
89
...{
90
if (keyOff == key.Length)
91
NextKey();
92
93
data[i] ^= key[keyOff++];
94
}
95
sm3c3.BlockUpdate(data, 0, data.Length);
96
}
97
98
publicvirtualvoid Dofinal(byte[] c3)
99
...{
100
byte[] p = p2.Y.ToBigInteger().ToByteArrayUnsigned();
101
sm3c3.BlockUpdate(p, 0, p.Length);
102
sm3c3.DoFinal(c3, 0);
103
Reset();
104
}
105
106
107
/**////<summary>
108
/// 使用SM2公钥加密数据
109
///</summary>
110
///<param></param>
111
///<param></param>
112
///<returns></returns>
113publicstring Encrypt(ECPoint pubKey, byte[] plaintext)
114
...{
115
116
byte[] data =newbyte[plaintext.Length];
117
Array.Copy(plaintext, data, plaintext.Length);
118
119
ECPoint c1 = InitEncipher(pubKey);
120
Encrypt(data);
121
122
byte[] c3 =newbyte[32];
123
Dofinal(c3);
124
125
string hexString = c1.X.ToBigInteger().ToString(16) + c1.Y.ToBigInteger().ToString(16)
126
+ Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(data)
127
+ Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(c3);
128
129
return hexString;
130
131
}
132
133
/**////<summary>
134
/// 使用SM2解密数据
135
///</summary>
136
///<param></param>
137
///<param></param>
138
///<returns></returns>
139publicbyte[] Decrypt(BigInteger privateKey, string ciphertext)
140
...{
141
142
string hexString = ciphertext;
143
string c1X = hexString.Substring(0, 64);
144
string c1Y = hexString.Substring(0+ c1X.Length, 64);
145
string encrypData = hexString.Substring(c1X.Length + c1Y.Length, hexString.Length - c1X.Length - c1Y.Length -64);
146
string c3 = hexString.Substring(hexString.Length -64);
147
148
byte[] data = SM2CryptoServiceProvider.StrToToHexByte(encrypData);
149
150
ECPoint c1 = CreatePoint(c1X, c1Y);
151
152
InitDecipher(privateKey, c1);
153
Decrypt(data);
154
155
byte[] c3_ =newbyte[32];
156
Dofinal(c3_);
157
158
string decryptData = Encoding.Default.GetString(data);
159
bool isDecrypt = Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(c3_) == c3;
160
161
return (isDecrypt ? data : newbyte[0]);
162
}
163
164
165
/**////<summary>
166
/// 创建坐标点
167
///</summary>
168
///<param></param>
169
///<param></param>
170
///<returns></returns>
171publicstatic ECPoint CreatePoint(string x, string y)
172
...{
173
174
BigInteger biX =new BigInteger(x, 16);
175
BigInteger biY =new BigInteger(y, 16);
176
ECFieldElement fx =new FpFieldElement(SM2CryptoServiceProvider.ecc_p, biX);
177
ECFieldElement fy =new FpFieldElement(SM2CryptoServiceProvider.ecc_p, biY);
178
ECPoint point =new FpPoint(SM2CryptoServiceProvider.ecc_curve, fx,fy);
179
return point;
180
}
181
182
183
184}
SM2解密算法