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