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