1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.impl.bc;
18
19 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20 import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestFactory;
21 import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestSpec;
22 import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestType;
23 import io.github.tonywasher.joceanus.gordianknot.api.encrypt.spec.GordianSM2EncryptionSpec;
24 import io.github.tonywasher.joceanus.gordianknot.api.encrypt.spec.GordianSM2EncryptionType;
25 import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
26 import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianNewSignParams;
27 import io.github.tonywasher.joceanus.gordianknot.api.sign.spec.GordianSignatureSpec;
28 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyEllipticKeyPair.BouncyECPrivateKey;
29 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyEllipticKeyPair.BouncyECPublicKey;
30 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPrivateKey;
31 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPublicKey;
32 import io.github.tonywasher.joceanus.gordianknot.impl.core.agree.GordianCoreAgreementFactory;
33 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
34 import io.github.tonywasher.joceanus.gordianknot.impl.core.encrypt.GordianCoreEncryptor;
35 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
36 import io.github.tonywasher.joceanus.gordianknot.impl.core.sign.GordianCoreSignature;
37 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.agree.GordianCoreAgreementSpec;
38 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.encrypt.GordianCoreEncryptorSpec;
39 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.sign.GordianCoreSignatureSpec;
40 import org.bouncycastle.crypto.CryptoException;
41 import org.bouncycastle.crypto.InvalidCipherTextException;
42 import org.bouncycastle.crypto.agreement.SM2KeyExchange;
43 import org.bouncycastle.crypto.engines.SM2Engine;
44 import org.bouncycastle.crypto.engines.SM2Engine.Mode;
45 import org.bouncycastle.crypto.params.ParametersWithID;
46 import org.bouncycastle.crypto.params.ParametersWithRandom;
47 import org.bouncycastle.crypto.params.SM2KeyExchangePrivateParameters;
48 import org.bouncycastle.crypto.params.SM2KeyExchangePublicParameters;
49 import org.bouncycastle.crypto.signers.SM2Signer;
50
51
52
53
54 public final class BouncySM2KeyPair {
55
56
57
58 private BouncySM2KeyPair() {
59 }
60
61
62
63
64 public static class BouncySM2Signature
65 extends GordianCoreSignature {
66
67
68
69 private final SM2Signer theSigner;
70
71
72
73
74
75
76
77
78 BouncySM2Signature(final GordianBaseFactory pFactory,
79 final GordianSignatureSpec pSpec) throws GordianException {
80
81 super(pFactory, pSpec);
82
83
84 final GordianDigestSpec mySpec = ((GordianCoreSignatureSpec) pSpec).getDigestSpec();
85 if (GordianDigestType.SM3.equals(mySpec.getDigestType())) {
86 theSigner = new SM2Signer();
87 } else {
88 final BouncyDigest myDigest = (BouncyDigest) pFactory.getDigestFactory().createDigest(mySpec);
89 theSigner = new SM2Signer(myDigest.getDigest());
90 }
91 }
92
93 @Override
94 public void update(final byte[] pBytes,
95 final int pOffset,
96 final int pLength) {
97 theSigner.update(pBytes, pOffset, pLength);
98 }
99
100 @Override
101 public void update(final byte pByte) {
102 theSigner.update(pByte);
103 }
104
105 @Override
106 public void update(final byte[] pBytes) {
107 theSigner.update(pBytes, 0, pBytes.length);
108 }
109
110 @Override
111 public void reset() {
112 theSigner.reset();
113 }
114
115 @Override
116 protected BouncyKeyPair getKeyPair() {
117 return (BouncyKeyPair) super.getKeyPair();
118 }
119
120 @Override
121 public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
122
123 super.initForSigning(pParams);
124 final BouncyKeyPair myPair = getKeyPair();
125 BouncyKeyPair.checkKeyPair(myPair);
126
127
128 final BouncyECPrivateKey myPrivate = (BouncyECPrivateKey) myPair.getPrivateKey();
129 final ParametersWithRandom myParms = new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom());
130 theSigner.init(true, myParms);
131 }
132
133 @Override
134 public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
135
136 super.initForVerify(pParams);
137 final BouncyKeyPair myPair = getKeyPair();
138 BouncyKeyPair.checkKeyPair(myPair);
139
140
141 final BouncyECPublicKey myPublic = (BouncyECPublicKey) myPair.getPublicKey();
142 theSigner.init(false, myPublic.getPublicKey());
143 }
144
145 @Override
146 public byte[] sign() throws GordianException {
147
148 checkMode(GordianSignatureMode.SIGN);
149
150
151 try {
152 return theSigner.generateSignature();
153 } catch (CryptoException e) {
154 throw new GordianCryptoException(BouncySignature.ERROR_SIGGEN, e);
155 }
156 }
157
158 @Override
159 public boolean verify(final byte[] pSignature) throws GordianException {
160
161 checkMode(GordianSignatureMode.VERIFY);
162
163
164 return theSigner.verifySignature(pSignature);
165 }
166 }
167
168
169
170
171 public static class BouncySM2AgreementEngine
172 extends BouncyAgreementBase {
173
174
175
176 private static final byte[] EMPTY = new byte[0];
177
178
179
180
181 private static final int KEYLEN = 128;
182
183
184
185
186 private final SM2KeyExchange theAgreement;
187
188
189
190
191
192
193
194
195 BouncySM2AgreementEngine(final GordianCoreAgreementFactory pFactory,
196 final GordianCoreAgreementSpec pSpec) throws GordianException {
197
198 super(pFactory, pSpec);
199
200
201 theAgreement = new SM2KeyExchange();
202 }
203
204 @Override
205 public void processClientHello() throws GordianException {
206
207 final BouncyECPublicKey myClientPublic = (BouncyECPublicKey) getPublicKey(getClientKeyPair());
208 final BouncyECPublicKey myClientEphPublic = (BouncyECPublicKey) getPublicKey(getClientEphemeral());
209 final BouncyECPrivateKey myPrivate = (BouncyECPrivateKey) getPrivateKey(getServerKeyPair());
210 final BouncyECPrivateKey myEphPrivate = (BouncyECPrivateKey) getPrivateKey(getServerEphemeral());
211
212
213 final byte[] myClientID = getClientName() == null ? EMPTY : getClientName();
214 final byte[] myServerID = getServerName() == null ? EMPTY : getServerName();
215
216
217 final SM2KeyExchangePrivateParameters myPrivParams = new SM2KeyExchangePrivateParameters(false,
218 myPrivate.getPrivateKey(), myEphPrivate.getPrivateKey());
219 final ParametersWithID myPrivIDParams = new ParametersWithID(myPrivParams, myServerID);
220 theAgreement.init(myPrivIDParams);
221 final SM2KeyExchangePublicParameters myPubParams = new SM2KeyExchangePublicParameters(myClientPublic.getPublicKey(),
222 myClientEphPublic.getPublicKey());
223 final ParametersWithID myPubIDParams = new ParametersWithID(myPubParams, myClientID);
224
225
226 if (getSpec().withConfirm()) {
227
228 final byte[][] myResults = theAgreement.calculateKeyWithConfirmation(KEYLEN, null, myPubIDParams);
229
230
231 setServerConfirm(myResults[1]);
232 setClientConfirm(myResults[2]);
233
234
235 storeSecret(myResults[0]);
236
237
238 } else {
239
240 storeSecret(theAgreement.calculateKey(KEYLEN, myPubIDParams));
241 }
242 }
243
244 @Override
245 public void processServerHello() throws GordianException {
246
247 final BouncyECPublicKey myServerPublic = (BouncyECPublicKey) getPublicKey(getServerKeyPair());
248 final BouncyECPublicKey myServerEphPublic = (BouncyECPublicKey) getPublicKey(getServerEphemeral());
249 final BouncyECPrivateKey myPrivate = (BouncyECPrivateKey) getPrivateKey(getClientKeyPair());
250 final BouncyECPrivateKey myEphPrivate = (BouncyECPrivateKey) getPrivateKey(getClientEphemeral());
251
252
253 final byte[] myClientID = getClientName() == null ? EMPTY : getClientName();
254 final byte[] myServerID = getServerName() == null ? EMPTY : getServerName();
255
256
257 final SM2KeyExchangePrivateParameters myPrivParams = new SM2KeyExchangePrivateParameters(true,
258 myPrivate.getPrivateKey(), myEphPrivate.getPrivateKey());
259 final ParametersWithID myPrivIDParams = new ParametersWithID(myPrivParams, myClientID);
260 theAgreement.init(myPrivIDParams);
261 final SM2KeyExchangePublicParameters myPubParams = new SM2KeyExchangePublicParameters(myServerPublic.getPublicKey(),
262 myServerEphPublic.getPublicKey());
263 final ParametersWithID myPubIDParams = new ParametersWithID(myPubParams, myServerID);
264
265
266 if (getSpec().withConfirm()) {
267
268 final byte[] myConfirm = getServerConfirm();
269
270
271 try {
272
273 final byte[][] myResults = theAgreement.calculateKeyWithConfirmation(KEYLEN, myConfirm, myPubIDParams);
274
275
276 if (setClientConfirm(myResults[1])) {
277
278 storeSecret(myResults[0]);
279 }
280
281
282 } catch (IllegalStateException e) {
283 getBuilder().setError("Server Confirmation failed");
284 }
285
286
287 } else {
288
289 storeSecret(theAgreement.calculateKey(KEYLEN, myPubIDParams));
290 }
291 }
292 }
293
294
295
296
297 public static class BouncySM2Encryptor
298 extends GordianCoreEncryptor {
299
300
301
302 private final SM2Engine theEncryptor;
303
304
305
306
307
308
309
310
311 BouncySM2Encryptor(final GordianBaseFactory pFactory,
312 final GordianCoreEncryptorSpec pSpec) throws GordianException {
313
314 super(pFactory, pSpec);
315 final GordianDigestFactory myFactory = pFactory.getDigestFactory();
316 final GordianSM2EncryptionSpec mySpec = pSpec.getSM2EncryptionSpec();
317 final BouncyDigest myDigest = (BouncyDigest) myFactory.createDigest(mySpec.getDigestSpec());
318 final Mode mySM2Mode = mySpec.getEncryptionType() == GordianSM2EncryptionType.C1C2C3
319 ? Mode.C1C2C3 : Mode.C1C3C2;
320 theEncryptor = new SM2Engine(myDigest.getDigest(), mySM2Mode);
321 }
322
323 @Override
324 protected BouncyPublicKey<?> getPublicKey() {
325 return (BouncyPublicKey<?>) super.getPublicKey();
326 }
327
328 @Override
329 protected BouncyPrivateKey<?> getPrivateKey() {
330 return (BouncyPrivateKey<?>) super.getPrivateKey();
331 }
332
333 @Override
334 public void initForEncrypt(final GordianKeyPair pKeyPair) throws GordianException {
335
336 BouncyKeyPair.checkKeyPair(pKeyPair);
337 super.initForEncrypt(pKeyPair);
338
339
340 final ParametersWithRandom myParms = new ParametersWithRandom(getPublicKey().getPublicKey(), getRandom());
341 theEncryptor.init(true, myParms);
342 }
343
344 @Override
345 public void initForDecrypt(final GordianKeyPair pKeyPair) throws GordianException {
346
347 BouncyKeyPair.checkKeyPair(pKeyPair);
348 super.initForDecrypt(pKeyPair);
349
350
351 theEncryptor.init(false, getPrivateKey().getPrivateKey());
352 }
353
354 @Override
355 public byte[] encrypt(final byte[] pBytes) throws GordianException {
356 try {
357
358 checkMode(GordianEncryptMode.ENCRYPT);
359
360
361 return theEncryptor.processBlock(pBytes, 0, pBytes.length);
362 } catch (InvalidCipherTextException e) {
363 throw new GordianCryptoException("Failed to encrypt data", e);
364 }
365 }
366
367 @Override
368 public byte[] decrypt(final byte[] pBytes) throws GordianException {
369 try {
370
371 checkMode(GordianEncryptMode.DECRYPT);
372
373
374 return theEncryptor.processBlock(pBytes, 0, pBytes.length);
375 } catch (InvalidCipherTextException e) {
376 throw new GordianCryptoException("Failed to decrypt data", e);
377 }
378 }
379 }
380 }