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