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.keypair.GordianKeyPair;
21 import io.github.tonywasher.joceanus.gordianknot.api.keypair.spec.GordianKeyPairSpec;
22 import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianNewSignParams;
23 import io.github.tonywasher.joceanus.gordianknot.api.sign.spec.GordianSignatureSpec;
24 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyEllipticKeyPair.BouncyECPrivateKey;
25 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyEllipticKeyPair.BouncyECPublicKey;
26 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncySignature.BouncyDSACoder;
27 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncySignature.BouncyDigestSignature;
28 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
29 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
30 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianIOException;
31 import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianKeyPairValidity;
32 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreKeyPairSpec;
33 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.sign.GordianCoreSignatureSpec;
34 import org.bouncycastle.asn1.ASN1BitString;
35 import org.bouncycastle.asn1.ASN1Encodable;
36 import org.bouncycastle.asn1.ASN1Integer;
37 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
38 import org.bouncycastle.asn1.ASN1OctetString;
39 import org.bouncycastle.asn1.ASN1Primitive;
40 import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
41 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
42 import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
43 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
44 import org.bouncycastle.asn1.x9.X9ECParameters;
45 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
46 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
47 import org.bouncycastle.crypto.params.ECDomainParameters;
48 import org.bouncycastle.crypto.params.ECGOST3410Parameters;
49 import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
50 import org.bouncycastle.crypto.params.ECNamedDomainParameters;
51 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
52 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
53 import org.bouncycastle.crypto.params.ParametersWithRandom;
54 import org.bouncycastle.crypto.signers.ECGOST3410Signer;
55 import org.bouncycastle.jcajce.provider.asymmetric.ecgost12.BCECGOST3410_2012PrivateKey;
56 import org.bouncycastle.jcajce.provider.asymmetric.ecgost12.BCECGOST3410_2012PublicKey;
57 import org.bouncycastle.jcajce.spec.GOST3410ParameterSpec;
58 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
59 import org.bouncycastle.math.ec.ECCurve;
60
61 import java.io.IOException;
62 import java.math.BigInteger;
63 import java.security.spec.PKCS8EncodedKeySpec;
64 import java.security.spec.X509EncodedKeySpec;
65
66
67
68
69 public final class BouncyGOSTKeyPair {
70
71
72
73 private static final String ALGO = "ECGOST3410-2012";
74
75
76
77
78 private static final int LEN32 = 32;
79
80
81
82
83 private static final int LEN64 = 64;
84
85
86
87
88 private static final byte ENCODING_ID = 0x04;
89
90
91
92
93 private BouncyGOSTKeyPair() {
94 }
95
96
97
98
99 public static class BouncyGOSTKeyPairGenerator
100 extends BouncyKeyPairGenerator {
101
102
103
104 private final ECKeyPairGenerator theGenerator;
105
106
107
108
109 private final ECDomainParameters theDomain;
110
111
112
113
114 private final ECNamedCurveSpec theSpec;
115
116
117
118
119
120
121
122 BouncyGOSTKeyPairGenerator(final GordianBaseFactory pFactory,
123 final GordianKeyPairSpec pKeySpec) {
124
125 super(pFactory, pKeySpec);
126
127
128 theGenerator = new ECKeyPairGenerator();
129
130
131 final GordianCoreKeyPairSpec myKeySpec = (GordianCoreKeyPairSpec) pKeySpec;
132 final String myCurve = myKeySpec.getElliptic().getCurveName();
133 final GOST3410ParameterSpec mySpec = new GOST3410ParameterSpec(myCurve);
134 final X9ECParameters x9 = ECGOST3410NamedCurves.getByNameX9(myCurve);
135 theSpec = new ECNamedCurveSpec(
136 myCurve,
137 x9.getCurve(),
138 x9.getG(),
139 x9.getN(),
140 x9.getH(),
141 x9.getSeed());
142 theDomain = new ECNamedDomainParameters(mySpec.getPublicKeyParamSet(), x9);
143
144
145 final ECKeyGenerationParameters myParams = new ECKeyGenerationParameters(
146 new ECGOST3410Parameters(theDomain, mySpec.getPublicKeyParamSet(), mySpec.getDigestParamSet(),
147 mySpec.getEncryptionParamSet()), getRandom());
148 theGenerator.init(myParams);
149 }
150
151 @Override
152 public BouncyKeyPair generateKeyPair() {
153
154 final AsymmetricCipherKeyPair myPair = theGenerator.generateKeyPair();
155 final BouncyECPublicKey myPublic = new BouncyECPublicKey(getKeySpec(), (ECPublicKeyParameters) myPair.getPublic());
156 final BouncyECPrivateKey myPrivate = new BouncyECPrivateKey(getKeySpec(), (ECPrivateKeyParameters) myPair.getPrivate());
157 return new BouncyKeyPair(myPublic, myPrivate);
158 }
159
160 @Override
161 public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
162
163 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
164
165
166 final BouncyECPrivateKey myPrivateKey = (BouncyECPrivateKey) getPrivateKey(pKeyPair);
167 final ECPrivateKeyParameters myParms = myPrivateKey.getPrivateKey();
168 final BouncyECPublicKey myPublicKey = (BouncyECPublicKey) getPublicKey(pKeyPair);
169 final ECPublicKeyParameters myPubParms = myPublicKey.getPublicKey();
170 final BCECGOST3410_2012PublicKey pubKey = new BCECGOST3410_2012PublicKey(ALGO, myPubParms, theSpec);
171 final BCECGOST3410_2012PrivateKey privKey = new BCECGOST3410_2012PrivateKey(ALGO, myParms, pubKey, theSpec);
172 return new PKCS8EncodedKeySpec(privKey.getEncoded());
173 }
174
175 @Override
176 public BouncyKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
177 final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
178
179 checkKeySpec(pPrivateKey);
180
181
182 final BouncyECPublicKey myPublic = derivePublicKey(pPublicKey);
183 final PrivateKeyInfo myInfo = PrivateKeyInfo.getInstance(pPrivateKey.getEncoded());
184 final ECPrivateKeyParameters myParms = deriveFromPrivKeyInfo(myInfo);
185 final BouncyECPrivateKey myPrivate = new BouncyECPrivateKey(getKeySpec(), myParms);
186 final BouncyKeyPair myPair = new BouncyKeyPair(myPublic, myPrivate);
187
188
189 GordianKeyPairValidity.checkValidity(getFactory(), myPair);
190
191
192 return myPair;
193 }
194
195 @Override
196 public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
197
198 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
199
200
201 final BouncyECPublicKey myPublicKey = (BouncyECPublicKey) getPublicKey(pKeyPair);
202 final ECPublicKeyParameters myParms = myPublicKey.getPublicKey();
203 final BCECGOST3410_2012PublicKey pubKey = new BCECGOST3410_2012PublicKey(ALGO, myParms, theSpec);
204 return new X509EncodedKeySpec(pubKey.getEncoded());
205 }
206
207 @Override
208 public BouncyKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pEncodedKey) throws GordianException {
209 final BouncyECPublicKey myPublic = derivePublicKey(pEncodedKey);
210 return new BouncyKeyPair(myPublic);
211 }
212
213
214
215
216
217
218
219
220 private BouncyECPublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
221
222 checkKeySpec(pEncodedKey);
223
224
225 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(pEncodedKey.getEncoded());
226 final ECPublicKeyParameters myParms = deriveFromPubKeyInfo(myInfo);
227 return new BouncyECPublicKey(getKeySpec(), myParms);
228 }
229
230
231
232
233
234
235
236
237 private ECPublicKeyParameters deriveFromPubKeyInfo(final SubjectPublicKeyInfo pKeyInfo) throws GordianException {
238 final ASN1ObjectIdentifier algOid = pKeyInfo.getAlgorithm().getAlgorithm();
239 final ASN1BitString bits = pKeyInfo.getPublicKeyData();
240 final ASN1OctetString key;
241
242 try {
243 key = (ASN1OctetString) ASN1Primitive.fromByteArray(bits.getBytes());
244 } catch (IOException ex) {
245 throw new GordianIOException("error recovering public key", ex);
246 }
247
248 final byte[] keyEnc = key.getOctets();
249 int fieldSize = LEN32;
250 if (algOid.equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512)) {
251 fieldSize = LEN64;
252 }
253
254 final int keySize = 2 * fieldSize;
255 final byte[] x9Encoding = new byte[1 + keySize];
256 x9Encoding[0] = ENCODING_ID;
257 for (int i = 1; i <= fieldSize; ++i) {
258 x9Encoding[i] = keyEnc[fieldSize - i];
259 x9Encoding[i + fieldSize] = keyEnc[keySize - i];
260 }
261
262 final ECCurve curve = theDomain.getCurve();
263 return new ECPublicKeyParameters(curve.decodePoint(x9Encoding), theDomain);
264 }
265
266
267
268
269
270
271
272
273 private ECPrivateKeyParameters deriveFromPrivKeyInfo(final PrivateKeyInfo pKeyInfo) throws GordianException {
274 try {
275 final ASN1Encodable privKey = pKeyInfo.parsePrivateKey();
276 final BigInteger myD;
277 if (privKey instanceof ASN1Integer) {
278 myD = ASN1Integer.getInstance(privKey).getPositiveValue();
279 } else {
280 final byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets();
281 final byte[] dVal = new byte[encVal.length];
282
283 for (int i = 0; i != encVal.length; i++) {
284 dVal[i] = encVal[encVal.length - 1 - i];
285 }
286
287 myD = new BigInteger(1, dVal);
288 }
289 return new ECPrivateKeyParameters(myD, theDomain);
290
291 } catch (IOException e) {
292 throw new GordianCryptoException(ERROR_PARSE, e);
293 }
294 }
295 }
296
297
298
299
300 static final class BouncyGOSTCoder
301 implements BouncyDSACoder {
302
303
304
305 private final Integer theLen;
306
307
308
309
310
311
312 BouncyGOSTCoder(final Integer pLen) {
313 theLen = pLen;
314 }
315
316 @Override
317 public byte[] dsaEncode(final BigInteger r,
318 final BigInteger s) {
319
320 final byte[] myFirst = makeUnsigned(s);
321 final byte[] mySecond = makeUnsigned(r);
322 final byte[] myResult = new byte[theLen];
323
324
325 System.arraycopy(myFirst, 0, myResult, theLen / 2 - myFirst.length, myFirst.length);
326 System.arraycopy(mySecond, 0, myResult, theLen - mySecond.length, mySecond.length);
327 return myResult;
328 }
329
330
331
332
333
334
335
336 private static byte[] makeUnsigned(final BigInteger pValue) {
337
338 final byte[] myResult = pValue.toByteArray();
339 if (myResult[0] != 0) {
340 return myResult;
341 }
342
343
344 final byte[] myTmp = new byte[myResult.length - 1];
345 System.arraycopy(myResult, 1, myTmp, 0, myTmp.length);
346 return myTmp;
347 }
348
349 @Override
350 public BigInteger[] dsaDecode(final byte[] pEncoded) {
351
352 final byte[] myFirst = new byte[pEncoded.length / 2];
353 final byte[] mySecond = new byte[pEncoded.length / 2];
354 System.arraycopy(pEncoded, 0, myFirst, 0, myFirst.length);
355 System.arraycopy(pEncoded, myFirst.length, mySecond, 0, mySecond.length);
356
357
358 final BigInteger[] sig = new BigInteger[2];
359 sig[1] = new BigInteger(1, myFirst);
360 sig[0] = new BigInteger(1, mySecond);
361 return sig;
362 }
363 }
364
365
366
367
368 public static class BouncyGOSTSignature
369 extends BouncyDigestSignature {
370
371
372
373 private final ECGOST3410Signer theSigner;
374
375
376
377
378 private final BouncyGOSTCoder theCoder;
379
380
381
382
383
384
385
386
387 BouncyGOSTSignature(final GordianBaseFactory pFactory,
388 final GordianSignatureSpec pSpec) throws GordianException {
389
390 super(pFactory, pSpec);
391
392
393 theSigner = new ECGOST3410Signer();
394 theCoder = new BouncyGOSTCoder(((GordianCoreSignatureSpec) pSpec).getDigestSpec().getDigestLength().getByteLength() << 1);
395 }
396
397 @Override
398 public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
399
400 super.initForSigning(pParams);
401 final BouncyKeyPair myPair = getKeyPair();
402 BouncyKeyPair.checkKeyPair(myPair);
403
404
405 final BouncyECPrivateKey myPrivate = (BouncyECPrivateKey) myPair.getPrivateKey();
406 final ParametersWithRandom myParms = new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom());
407 theSigner.init(true, myParms);
408 }
409
410 @Override
411 public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
412
413 super.initForVerify(pParams);
414 final BouncyKeyPair myPair = getKeyPair();
415 BouncyKeyPair.checkKeyPair(myPair);
416
417
418 final BouncyECPublicKey myPublic = (BouncyECPublicKey) myPair.getPublicKey();
419 theSigner.init(false, myPublic.getPublicKey());
420 }
421
422 @Override
423 public byte[] sign() throws GordianException {
424
425 checkMode(GordianSignatureMode.SIGN);
426
427
428 final BigInteger[] myValues = theSigner.generateSignature(getDigest());
429 return theCoder.dsaEncode(myValues[0], myValues[1]);
430 }
431
432 @Override
433 public boolean verify(final byte[] pSignature) throws GordianException {
434
435 checkMode(GordianSignatureMode.VERIFY);
436
437
438 final BigInteger[] myValues = theCoder.dsaDecode(pSignature);
439 return theSigner.verifySignature(getDigest(), myValues[0], myValues[1]);
440 }
441 }
442 }