-
Notifications
You must be signed in to change notification settings - Fork 19
/
SM2Util.java
313 lines (286 loc) · 13.8 KB
/
SM2Util.java
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
package twgc.gm.sm2;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.function.Supplier;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.*;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;
import twgc.gm.random.SecureRandomFactory;
import twgc.gm.utils.Const;
/**
* @author SamYuan; 吴仙杰
* @Description 国密SM2工具类, 算法提供者 Bouncy Castle
* @date 2020/10
* ref:
* https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02
* http://gmssl.org/docs/oid.html
* http://www.jonllen.com/jonllen/work/164.aspx
* https://blog.csdn.net/Vincent2014Linux/article/details/108668186
* https://www.pixelstech.net/article/1464167276-Generating-CSR-using-Java
* http://senthadev.com/generating-csr-using-java-and-bouncycastle-api.html
* https://github.com/Trisia/alg-sm2-demo
*/
public class SM2Util {
public SM2Util() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
signature = Signature.getInstance(Const.SM3SM2_VALUE, BouncyCastleProvider.PROVIDER_NAME);
generator = KeyPairGenerator.getInstance(Const.EC_VALUE, BouncyCastleProvider.PROVIDER_NAME);
generator.initialize(new ECGenParameterSpec(Const.CURVE_NAME));
}
public Signature getSignature() {
return signature;
}
public void setSignature(Signature signature) {
this.signature = signature;
}
private Signature signature;
private static final X9ECParameters X_9_EC_PARAMETERS = GMNamedCurves.getByName(Const.CURVE_NAME);
private static final ECDomainParameters EC_DOMAIN_PARAMETERS = new ECDomainParameters(X_9_EC_PARAMETERS.getCurve(), X_9_EC_PARAMETERS.getG(), X_9_EC_PARAMETERS.getN());
private static final ECParameterSpec PARAMETER_SPEC = new ECParameterSpec(X_9_EC_PARAMETERS.getCurve(), X_9_EC_PARAMETERS.getG(), X_9_EC_PARAMETERS.getN());
private static KeyPairGenerator generator;
private static final JcaPEMKeyConverter CONVERTER = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME);
/**
* 生成 PKCS#10 证书请求
*
* @return RSA P10 证书请求 Base64 字符串
*/
public KeyPair generatekeyPair() {
return generator.generateKeyPair();
}
public byte[] encrypt(SM2Engine sm2Engine, PublicKey publicKey, byte[] message) throws InvalidCipherTextException {
BCECPublicKey localECPublicKey = (BCECPublicKey) publicKey;
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), EC_DOMAIN_PARAMETERS);
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, SecureRandomFactory.getSecureRandom()));
return sm2Engine.processBlock(message, 0, message.length);
}
public byte[] decrypt(SM2Engine sm2Engine, PrivateKey privateKey, byte[] message) throws InvalidCipherTextException {
BCECPrivateKey localECPrivateKey = (BCECPrivateKey) privateKey;
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), EC_DOMAIN_PARAMETERS);
sm2Engine.init(false, ecPrivateKeyParameters);
return sm2Engine.processBlock(message, 0, message.length);
}
public byte[] sign(PrivateKey privateKey, byte[] message) throws SignatureException, InvalidKeyException {
synchronized (this) {
signature.initSign(privateKey, SecureRandomFactory.getSecureRandom());
signature.update(message);
return signature.sign();
}
}
public boolean verify(PublicKey publicKey, byte[] message, byte[] sigBytes) throws InvalidKeyException, SignatureException {
synchronized (this) {
signature.initVerify(publicKey);
signature.update(message);
return signature.verify(sigBytes);
}
}
public static PKCS10CertificationRequest generateCSR(KeyPair keyPair, X500Principal subject) throws OperatorCreationException {
ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2").build(keyPair.getPrivate());
PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic());
return builder.build(signer);
}
public static String pemFrom(PrivateKey privateKey, String password) throws OperatorCreationException, IOException {
StringWriter sw = new StringWriter();
try (JcaPEMWriter pemWriter = new JcaPEMWriter(sw)) {
OutputEncryptor encryptor = null;
if (password != null && password.length() > 0) {
encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_256_CBC)
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.setRandom(SecureRandomFactory.getSecureRandom())
.setPasssword(password.toCharArray())
.build();
}
PKCS8Generator generator = new JcaPKCS8Generator(privateKey, encryptor);
pemWriter.writeObject(generator);
}
return sw.toString();
}
public static String pemFrom(PublicKey publicKey) throws IOException {
StringWriter sw = new StringWriter();
try (PemWriter pemWriter = new PemWriter(sw)) {
PemObject pem = new PemObject("PUBLIC KEY", publicKey.getEncoded());
pemWriter.writeObject(pem);
}
return sw.toString();
}
/**
* 打印 OpenSSL PEM 格式文件字符串的 SSL 证书请求 CSR 文件内容
*
* @param csr 证书请求对象
*/
public static String pemFrom(PKCS10CertificationRequest csr) throws IOException {
StringWriter sw = new StringWriter();
try (PemWriter pemWriter = new PemWriter(sw)) {
PemObject pem = new PemObject("CERTIFICATE REQUEST", csr.getEncoded());
pemWriter.writeObject(pem);
}
return sw.toString();
}
public static String pemFrom(X509Certificate x509Certificate) throws IOException, CertificateEncodingException {
StringWriter sw = new StringWriter();
try (PemWriter pemWriter = new PemWriter(sw)) {
PemObject pem = new PemObject("CERTIFICATE", x509Certificate.getEncoded());
pemWriter.writeObject(pem);
}
return sw.toString();
}
public static PrivateKey loadPrivFromFile(String filename, String password) throws IOException, OperatorCreationException, PKCSException {
return loadPriv(password, () -> {
try {
return new FileReader(filename);
} catch (FileNotFoundException e) {
throw new RuntimeException("Private key \"" + filename + "\" not found", e);
}
});
}
public static PublicKey loadPublicFromFile(String filename) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
return loadPublic(() -> {
try {
return new FileReader(filename);
} catch (FileNotFoundException e) {
throw new RuntimeException("Public key \"" + filename + "\" not found", e);
}
});
}
public static X509Certificate loadX509CertificateFromFile(String filename) throws IOException, CertificateException,
NoSuchProviderException {
try (FileInputStream in = new FileInputStream(filename)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
return (X509Certificate) cf.generateCertificate(in);
}
}
/**
* 从字符串加载私钥
*
* @param privateKey 字符串字私钥
* @param password 密码
* @return {@link PrivateKey} 私钥对象
* @throws IOException
* @throws OperatorCreationException
* @throws PKCSException
*/
public static PrivateKey loadPrivFromString(String privateKey, String password) throws IOException, OperatorCreationException, PKCSException {
return loadPriv(password, () -> new StringReader(privateKey));
}
/**
* 从字符串加载公钥
*
* @param publicKey 字符串公钥
* @return {@link PublicKey} 公钥对象
* @throws IOException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
public static PublicKey loadPublicFromString(String publicKey) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
return loadPublic(() -> new StringReader(publicKey));
}
/**
* 从字符串加载证书
*
* @param cert 字符串证书
* @return {@link X509Certificate} 证书对象
* @throws IOException
* @throws CertificateException
* @throws NoSuchProviderException
*/
public static X509Certificate loadX509CertificateFromString(String cert) throws IOException, CertificateException, NoSuchProviderException {
try (InputStream in = new ByteArrayInputStream(cert.getBytes())) {
CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
return (X509Certificate) cf.generateCertificate(in);
}
}
public static PublicKey derivePublicFromPrivate(PrivateKey privateKey) {
BCECPrivateKey localECPrivateKey = (BCECPrivateKey) privateKey;
BigInteger d = localECPrivateKey.getD();
ECPoint ecpoint = new FixedPointCombMultiplier().multiply(GMNamedCurves.getByName(Const.CURVE_NAME).getG(), d);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecpoint, PARAMETER_SPEC);
return new BCECPublicKey(privateKey.getAlgorithm(), pubKeySpec,
BouncyCastleProvider.CONFIGURATION);
}
/**
* 加载私钥
*
* @param password 密码
* @param fx {@link Reader} 回调函数
* @return {@link PrivateKey}
* @throws IOException
* @throws OperatorCreationException
* @throws PKCSException
*/
public static PrivateKey loadPriv(String password, Supplier<Reader> fx) throws IOException, OperatorCreationException, PKCSException {
PrivateKey priv = null;
try (PEMParser pemParser = new PEMParser(fx.get())) {
Object obj = pemParser.readObject();
if (password != null && password.length() > 0) {
if (obj instanceof PKCS8EncryptedPrivateKeyInfo) {
PKCS8EncryptedPrivateKeyInfo epkInfo = (PKCS8EncryptedPrivateKeyInfo) obj;
InputDecryptorProvider decryptor = new JceOpenSSLPKCS8DecryptorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(password.toCharArray());
PrivateKeyInfo pkInfo = epkInfo.decryptPrivateKeyInfo(decryptor);
priv = CONVERTER.getPrivateKey(pkInfo);
}
} else {
priv = CONVERTER.getPrivateKey((PrivateKeyInfo) obj);
}
}
return priv;
}
/**
* 加载公钥
*
* @param fx {@link Reader} 回调函数
* @return {@link PublicKey}
* @throws IOException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
public static PublicKey loadPublic(Supplier<Reader> fx) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
try (PemReader pemReader = new PemReader(fx.get())) {
PemObject spki = pemReader.readPemObject();
Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
return KeyFactory.getInstance(Const.EC_VALUE, BouncyCastleProvider.PROVIDER_NAME).generatePublic(new X509EncodedKeySpec(spki.getContent()));
}
}
}