View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
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.BouncyKeyPair.BouncyPrivateKey;
25  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPublicKey;
26  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
27  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
28  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianKeyPairValidity;
29  import io.github.tonywasher.joceanus.gordianknot.impl.core.sign.GordianCoreSignature;
30  import org.bouncycastle.asn1.ASN1Encoding;
31  import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
32  import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
33  import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
34  import org.bouncycastle.crypto.CryptoException;
35  import org.bouncycastle.crypto.Signer;
36  import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
37  import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator;
38  import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
39  import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
40  import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
41  import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
42  import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters;
43  import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters;
44  import org.bouncycastle.crypto.params.Ed448PublicKeyParameters;
45  import org.bouncycastle.crypto.signers.Ed25519Signer;
46  import org.bouncycastle.crypto.signers.Ed448Signer;
47  import org.bouncycastle.crypto.util.PrivateKeyFactory;
48  import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
49  import org.bouncycastle.crypto.util.PublicKeyFactory;
50  import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
51  
52  import java.io.IOException;
53  import java.security.spec.PKCS8EncodedKeySpec;
54  import java.security.spec.X509EncodedKeySpec;
55  import java.util.Arrays;
56  
57  /**
58   * EdwardsCurve DSA KeyPair classes.
59   */
60  public final class BouncyEdDSAKeyPair {
61      /**
62       * Private constructor.
63       */
64      private BouncyEdDSAKeyPair() {
65      }
66  
67      /**
68       * Bouncy EdwardsDSA25519 PublicKey.
69       */
70      public static class BouncyEd25519PublicKey
71              extends BouncyPublicKey<Ed25519PublicKeyParameters> {
72          /**
73           * Constructor.
74           *
75           * @param pKeySpec   the keySpec
76           * @param pPublicKey the public key
77           */
78          BouncyEd25519PublicKey(final GordianKeyPairSpec pKeySpec,
79                                 final Ed25519PublicKeyParameters pPublicKey) {
80              super(pKeySpec, pPublicKey);
81          }
82  
83          @Override
84          protected boolean matchKey(final AsymmetricKeyParameter pThat) {
85              /* Access keys */
86              final Ed25519PublicKeyParameters myThis = getPublicKey();
87              final Ed25519PublicKeyParameters myThat = (Ed25519PublicKeyParameters) pThat;
88  
89              /* Compare keys */
90              return Arrays.equals(myThis.getEncoded(), myThat.getEncoded());
91          }
92      }
93  
94      /**
95       * Bouncy EdwardsDSA25519 PrivateKey.
96       */
97      public static class BouncyEd25519PrivateKey
98              extends BouncyPrivateKey<Ed25519PrivateKeyParameters> {
99          /**
100          * Constructor.
101          *
102          * @param pKeySpec    the keySpec
103          * @param pPrivateKey the private key
104          */
105         BouncyEd25519PrivateKey(final GordianKeyPairSpec pKeySpec,
106                                 final Ed25519PrivateKeyParameters pPrivateKey) {
107             super(pKeySpec, pPrivateKey);
108         }
109 
110 
111         @Override
112         protected boolean matchKey(final AsymmetricKeyParameter pThat) {
113             /* Access keys */
114             final Ed25519PrivateKeyParameters myThis = getPrivateKey();
115             final Ed25519PrivateKeyParameters myThat = (Ed25519PrivateKeyParameters) pThat;
116 
117             /* Compare keys */
118             return Arrays.equals(myThis.getEncoded(), myThat.getEncoded());
119         }
120     }
121 
122     /**
123      * Bouncy EdwardsDSA448 PublicKey.
124      */
125     public static class BouncyEd448PublicKey
126             extends BouncyPublicKey<Ed448PublicKeyParameters> {
127         /**
128          * Constructor.
129          *
130          * @param pKeySpec   the keySpec
131          * @param pPublicKey the public key
132          */
133         BouncyEd448PublicKey(final GordianKeyPairSpec pKeySpec,
134                              final Ed448PublicKeyParameters pPublicKey) {
135             super(pKeySpec, pPublicKey);
136         }
137 
138         @Override
139         protected boolean matchKey(final AsymmetricKeyParameter pThat) {
140             /* Access keys */
141             final Ed448PublicKeyParameters myThis = getPublicKey();
142             final Ed448PublicKeyParameters myThat = (Ed448PublicKeyParameters) pThat;
143 
144             /* Compare keys */
145             return Arrays.equals(myThis.getEncoded(), myThat.getEncoded());
146         }
147     }
148 
149     /**
150      * Bouncy EdwardsDSA448 PrivateKey.
151      */
152     public static class BouncyEd448PrivateKey
153             extends BouncyPrivateKey<Ed448PrivateKeyParameters> {
154         /**
155          * Constructor.
156          *
157          * @param pKeySpec    the keySpec
158          * @param pPrivateKey the private key
159          */
160         BouncyEd448PrivateKey(final GordianKeyPairSpec pKeySpec,
161                               final Ed448PrivateKeyParameters pPrivateKey) {
162             super(pKeySpec, pPrivateKey);
163         }
164 
165 
166         @Override
167         protected boolean matchKey(final AsymmetricKeyParameter pThat) {
168             /* Access keys */
169             final Ed448PrivateKeyParameters myThis = getPrivateKey();
170             final Ed448PrivateKeyParameters myThat = (Ed448PrivateKeyParameters) pThat;
171 
172             /* Compare keys */
173             return Arrays.equals(myThis.getEncoded(), myThat.getEncoded());
174         }
175     }
176 
177     /**
178      * BouncyCastle EdwardsDSA25519 KeyPair generator.
179      */
180     public static class BouncyEd25519KeyPairGenerator
181             extends BouncyKeyPairGenerator {
182         /**
183          * Generator.
184          */
185         private final Ed25519KeyPairGenerator theGenerator;
186 
187         /**
188          * Constructor.
189          *
190          * @param pFactory the Security Factory
191          * @param pKeySpec the keySpec
192          */
193         BouncyEd25519KeyPairGenerator(final GordianBaseFactory pFactory,
194                                       final GordianKeyPairSpec pKeySpec) {
195             /* Initialise underlying class */
196             super(pFactory, pKeySpec);
197 
198             /* Create the generator */
199             theGenerator = new Ed25519KeyPairGenerator();
200 
201             /* Initialise the generator */
202             final Ed25519KeyGenerationParameters myParams = new Ed25519KeyGenerationParameters(getRandom());
203             theGenerator.init(myParams);
204         }
205 
206         @Override
207         public BouncyKeyPair generateKeyPair() {
208             /* Generate and return the keyPair */
209             final AsymmetricCipherKeyPair myPair = theGenerator.generateKeyPair();
210             final BouncyEd25519PublicKey myPublic = new BouncyEd25519PublicKey(getKeySpec(), (Ed25519PublicKeyParameters) myPair.getPublic());
211             final BouncyEd25519PrivateKey myPrivate = new BouncyEd25519PrivateKey(getKeySpec(), (Ed25519PrivateKeyParameters) myPair.getPrivate());
212             return new BouncyKeyPair(myPublic, myPrivate);
213         }
214 
215         @Override
216         public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
217             /* Protect against exceptions */
218             try {
219                 /* Check the keyPair type and keySpecs */
220                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
221 
222                 /* build and return the encoding */
223                 final BouncyEd25519PrivateKey myPrivateKey = (BouncyEd25519PrivateKey) getPrivateKey(pKeyPair);
224                 final Ed25519PrivateKeyParameters myParms = myPrivateKey.getPrivateKey();
225                 final PrivateKeyInfo myInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(myParms);
226                 return new PKCS8EncodedKeySpec(myInfo.getEncoded());
227 
228             } catch (IOException e) {
229                 throw new GordianCryptoException(ERROR_PARSE, e);
230             }
231         }
232 
233         @Override
234         public BouncyKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
235                                            final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
236             /* Protect against exceptions */
237             try {
238                 /* Check the keySpecs */
239                 checkKeySpec(pPrivateKey);
240 
241                 /* derive keyPair */
242                 final BouncyEd25519PublicKey myPublic = derivePublicKey(pPublicKey);
243                 final PrivateKeyInfo myInfo = PrivateKeyInfo.getInstance(pPrivateKey.getEncoded());
244                 final Ed25519PrivateKeyParameters myParms = (Ed25519PrivateKeyParameters) PrivateKeyFactory.createKey(myInfo);
245                 final BouncyEd25519PrivateKey myPrivate = new BouncyEd25519PrivateKey(getKeySpec(), myParms);
246                 final BouncyKeyPair myPair = new BouncyKeyPair(myPublic, myPrivate);
247 
248                 /* Check that we have a matching pair */
249                 GordianKeyPairValidity.checkValidity(getFactory(), myPair);
250 
251                 /* Return the keyPair */
252                 return myPair;
253 
254             } catch (IOException e) {
255                 throw new GordianCryptoException(ERROR_PARSE, e);
256             }
257         }
258 
259         @Override
260         public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
261             /* Protect against exceptions */
262             try {
263                 /* Check the keyPair type and keySpecs */
264                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
265 
266                 /* build and return the encoding */
267                 final BouncyEd25519PublicKey myPublicKey = (BouncyEd25519PublicKey) getPublicKey(pKeyPair);
268                 final Ed25519PublicKeyParameters myParms = myPublicKey.getPublicKey();
269                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(myParms);
270                 final byte[] myBytes = myInfo.getEncoded(ASN1Encoding.DER);
271                 return new X509EncodedKeySpec(myBytes);
272 
273             } catch (IOException e) {
274                 throw new GordianCryptoException(ERROR_PARSE, e);
275             }
276         }
277 
278         @Override
279         public BouncyKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pEncodedKey) throws GordianException {
280             final BouncyEd25519PublicKey myPublic = derivePublicKey(pEncodedKey);
281             return new BouncyKeyPair(myPublic);
282         }
283 
284         /**
285          * Derive public key from encoded.
286          *
287          * @param pEncodedKey the encoded key
288          * @return the public key
289          * @throws GordianException on error
290          */
291         private BouncyEd25519PublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
292             /* Protect against exceptions */
293             try {
294                 /* Check the keySpecs */
295                 checkKeySpec(pEncodedKey);
296 
297                 /* derive publicKey */
298                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(pEncodedKey.getEncoded());
299                 final Ed25519PublicKeyParameters myParms = (Ed25519PublicKeyParameters) PublicKeyFactory.createKey(myInfo);
300                 return new BouncyEd25519PublicKey(getKeySpec(), myParms);
301 
302             } catch (IOException e) {
303                 throw new GordianCryptoException(ERROR_PARSE, e);
304             }
305         }
306     }
307 
308     /**
309      * BouncyCastle EdwardsDSA448 KeyPair generator.
310      */
311     public static class BouncyEd448KeyPairGenerator
312             extends BouncyKeyPairGenerator {
313         /**
314          * Generator.
315          */
316         private final Ed448KeyPairGenerator theGenerator;
317 
318         /**
319          * Constructor.
320          *
321          * @param pFactory the Security Factory
322          * @param pKeySpec the keySpec
323          */
324         BouncyEd448KeyPairGenerator(final GordianBaseFactory pFactory,
325                                     final GordianKeyPairSpec pKeySpec) {
326             /* Initialise underlying class */
327             super(pFactory, pKeySpec);
328 
329             /* Create the generator */
330             theGenerator = new Ed448KeyPairGenerator();
331 
332             /* Initialise the generator */
333             final Ed448KeyGenerationParameters myParams = new Ed448KeyGenerationParameters(getRandom());
334             theGenerator.init(myParams);
335         }
336 
337         @Override
338         public BouncyKeyPair generateKeyPair() {
339             final AsymmetricCipherKeyPair myPair = theGenerator.generateKeyPair();
340             final BouncyEd448PublicKey myPublic = new BouncyEd448PublicKey(getKeySpec(), (Ed448PublicKeyParameters) myPair.getPublic());
341             final BouncyEd448PrivateKey myPrivate = new BouncyEd448PrivateKey(getKeySpec(), (Ed448PrivateKeyParameters) myPair.getPrivate());
342             return new BouncyKeyPair(myPublic, myPrivate);
343         }
344 
345         @Override
346         public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
347             try {
348                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
349                 final BouncyEd448PrivateKey myPrivateKey = (BouncyEd448PrivateKey) getPrivateKey(pKeyPair);
350                 final Ed448PrivateKeyParameters myParms = myPrivateKey.getPrivateKey();
351                 final PrivateKeyInfo myInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(myParms);
352                 return new PKCS8EncodedKeySpec(myInfo.getEncoded());
353             } catch (IOException e) {
354                 throw new GordianCryptoException(ERROR_PARSE, e);
355             }
356         }
357 
358         @Override
359         public BouncyKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
360                                            final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
361             try {
362                 checkKeySpec(pPrivateKey);
363                 final BouncyEd448PublicKey myPublic = derivePublicKey(pPublicKey);
364                 final PrivateKeyInfo myInfo = PrivateKeyInfo.getInstance(pPrivateKey.getEncoded());
365                 final Ed448PrivateKeyParameters myParms = (Ed448PrivateKeyParameters) PrivateKeyFactory.createKey(myInfo);
366                 final BouncyEd448PrivateKey myPrivate = new BouncyEd448PrivateKey(getKeySpec(), myParms);
367                 final BouncyKeyPair myPair = new BouncyKeyPair(myPublic, myPrivate);
368                 GordianKeyPairValidity.checkValidity(getFactory(), myPair);
369                 return myPair;
370             } catch (IOException e) {
371                 throw new GordianCryptoException(ERROR_PARSE, e);
372             }
373         }
374 
375         @Override
376         public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
377             try {
378                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
379                 final BouncyEd448PublicKey myPublicKey = (BouncyEd448PublicKey) getPublicKey(pKeyPair);
380                 final Ed448PublicKeyParameters myParms = myPublicKey.getPublicKey();
381                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(myParms);
382                 final byte[] myBytes = myInfo.getEncoded(ASN1Encoding.DER);
383                 return new X509EncodedKeySpec(myBytes);
384             } catch (IOException e) {
385                 throw new GordianCryptoException(ERROR_PARSE, e);
386             }
387         }
388 
389         @Override
390         public BouncyKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pEncodedKey) throws GordianException {
391             final BouncyEd448PublicKey myPublic = derivePublicKey(pEncodedKey);
392             return new BouncyKeyPair(myPublic);
393         }
394 
395         /**
396          * Derive public key from encoded.
397          *
398          * @param pEncodedKey the encoded key
399          * @return the public key
400          * @throws GordianException on error
401          */
402         private BouncyEd448PublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
403             try {
404                 checkKeySpec(pEncodedKey);
405                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(pEncodedKey.getEncoded());
406                 final Ed448PublicKeyParameters myParms = (Ed448PublicKeyParameters) PublicKeyFactory.createKey(myInfo);
407                 return new BouncyEd448PublicKey(getKeySpec(), myParms);
408             } catch (IOException e) {
409                 throw new GordianCryptoException(ERROR_PARSE, e);
410             }
411         }
412     }
413 
414     /**
415      * EdDSA signature.
416      */
417     public static class BouncyEdDSASignature
418             extends GordianCoreSignature {
419         /**
420          * The Signer.
421          */
422         private Signer theSigner;
423 
424         /**
425          * Constructor.
426          *
427          * @param pFactory the factory
428          * @param pSpec    the signatureSpec.
429          */
430         BouncyEdDSASignature(final GordianBaseFactory pFactory,
431                              final GordianSignatureSpec pSpec) {
432             /* Initialise underlying class */
433             super(pFactory, pSpec);
434         }
435 
436         /**
437          * Create the signer according to the keyPair.
438          *
439          * @param pKeyPair the keyPair
440          * @return the signer
441          */
442         private static Signer createSigner(final GordianKeyPair pKeyPair) {
443             /* Determine the EdwardsCurve */
444             final boolean is25519 = pKeyPair.getKeyPairSpec().getEdwardsElliptic().is25519();
445             final byte[] myContext = new byte[0];
446 
447             /* Create the internal digests */
448             return is25519
449                     ? new Ed25519Signer()
450                     : new Ed448Signer(myContext);
451         }
452 
453         @Override
454         public void initForSigning(final GordianSignParams pParams) throws GordianException {
455             /* Initialise detail */
456             super.initForSigning(pParams);
457             final GordianKeyPair myPair = getKeyPair();
458             BouncyKeyPair.checkKeyPair(myPair);
459 
460             /* Initialise and set the signer */
461             theSigner = createSigner(myPair);
462             final BouncyPrivateKey<?> myPrivate = getKeyPair().getPrivateKey();
463             theSigner.init(true, myPrivate.getPrivateKey());
464         }
465 
466         @Override
467         public void initForVerify(final GordianSignParams pParams) throws GordianException {
468             /* Initialise detail */
469             super.initForVerify(pParams);
470             final GordianKeyPair myPair = getKeyPair();
471             BouncyKeyPair.checkKeyPair(myPair);
472 
473             /* Initialise and set the signer */
474             theSigner = createSigner(myPair);
475             final BouncyPublicKey<?> myPublic = getKeyPair().getPublicKey();
476             theSigner.init(false, myPublic.getPublicKey());
477         }
478 
479         @Override
480         public void update(final byte[] pBytes,
481                            final int pOffset,
482                            final int pLength) {
483             theSigner.update(pBytes, pOffset, pLength);
484         }
485 
486         @Override
487         public void update(final byte pByte) {
488             theSigner.update(pByte);
489         }
490 
491         @Override
492         public void update(final byte[] pBytes) {
493             theSigner.update(pBytes, 0, pBytes.length);
494         }
495 
496         @Override
497         public void reset() {
498             theSigner.reset();
499         }
500 
501         @Override
502         protected BouncyKeyPair getKeyPair() {
503             return (BouncyKeyPair) super.getKeyPair();
504         }
505 
506         @Override
507         public GordianBaseFactory getFactory() {
508             return (GordianBaseFactory) super.getFactory();
509         }
510 
511         @Override
512         public byte[] sign() throws GordianException {
513             /* Check that we are in signing mode */
514             checkMode(GordianSignatureMode.SIGN);
515 
516             /* Sign the message */
517             try {
518                 return theSigner.generateSignature();
519             } catch (CryptoException e) {
520                 throw new GordianCryptoException(BouncySignature.ERROR_SIGGEN, e);
521             }
522         }
523 
524         @Override
525         public boolean verify(final byte[] pSignature) throws GordianException {
526             /* Check that we are in verify mode */
527             checkMode(GordianSignatureMode.VERIFY);
528 
529             /* Verify the message */
530             return theSigner.verifySignature(pSignature);
531         }
532     }
533 }