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