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.jca;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.agree.spec.GordianAgreementKDF;
20  import io.github.tonywasher.joceanus.gordianknot.api.agree.spec.GordianAgreementType;
21  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
22  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
23  import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianSymKeyType;
24  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
25  import io.github.tonywasher.joceanus.gordianknot.api.keypair.spec.GordianKeyPairSpec;
26  import io.github.tonywasher.joceanus.gordianknot.api.keypair.spec.GordianKeyPairType;
27  import io.github.tonywasher.joceanus.gordianknot.impl.core.agree.GordianCoreAgreementEngine;
28  import io.github.tonywasher.joceanus.gordianknot.impl.core.agree.GordianCoreAgreementFactory;
29  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseData;
30  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
31  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
32  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianPrivateKey;
33  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianPublicKey;
34  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.agree.GordianCoreAgreementSpec;
35  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreKeyPairSpec;
36  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreNTRUPrimeSpec;
37  import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaPrivateKey;
38  import io.github.tonywasher.joceanus.gordianknot.impl.jca.JcaKeyPair.JcaPublicKey;
39  import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
40  import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
41  import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
42  import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
43  import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
44  import org.bouncycastle.jcajce.spec.DHUParameterSpec;
45  import org.bouncycastle.jcajce.spec.KEMExtractSpec;
46  import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
47  import org.bouncycastle.jcajce.spec.MQVParameterSpec;
48  import org.bouncycastle.jcajce.spec.SM2KeyExchangeSpec;
49  import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
50  
51  import javax.crypto.KeyAgreement;
52  import javax.crypto.KeyGenerator;
53  import java.security.InvalidAlgorithmParameterException;
54  import java.security.InvalidKeyException;
55  import java.security.NoSuchAlgorithmException;
56  import java.security.Provider;
57  import java.security.PublicKey;
58  
59  /**
60   * Agreement classes.
61   */
62  public final class JcaAgreement {
63      /**
64       * Failed agreement message.
65       */
66      private static final String ERR_AGREEMENT = "Failed Agreement";
67  
68      /**
69       * Private constructor.
70       */
71      private JcaAgreement() {
72      }
73  
74      /**
75       * Jca PostQuantum Agreement.
76       */
77      public static class JcaPostQuantumEngine
78              extends JcaAgreementBase {
79          /**
80           * Key Agreement.
81           */
82          private final KeyGenerator theGenerator;
83  
84          /**
85           * Constructor.
86           *
87           * @param pFactory   the security factory
88           * @param pSpec      the agreementSpec
89           * @param pGenerator the generator
90           */
91          JcaPostQuantumEngine(final GordianCoreAgreementFactory pFactory,
92                               final GordianCoreAgreementSpec pSpec,
93                               final KeyGenerator pGenerator) throws GordianException {
94              /* Initialize underlying class */
95              super(pFactory, pSpec);
96  
97              /* Store the generator */
98              theGenerator = pGenerator;
99          }
100 
101         @Override
102         public void buildClientHello() throws GordianException {
103             /* Protect against exceptions */
104             try {
105                 /* Create encapsulation */
106                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
107                 final KEMGenerateSpec mySpec = new KEMGenerateSpec.Builder(myPublic.getPublicKey(),
108                         GordianSymKeyType.AES.toString(), GordianLength.LEN_256.getLength()).withKdfAlgorithm(derivationAlgorithmId()).build();
109                 theGenerator.init(mySpec, getRandom());
110                 final SecretKeyWithEncapsulation mySecret = (SecretKeyWithEncapsulation) theGenerator.generateKey();
111 
112                 /* Store the encapsulation */
113                 setEncapsulated(mySecret.getEncapsulation());
114 
115                 /* Store secret */
116                 storeSecret(mySecret.getEncoded());
117 
118             } catch (InvalidAlgorithmParameterException e) {
119                 throw new GordianCryptoException(ERR_AGREEMENT, e);
120             }
121         }
122 
123         @Override
124         public void processClientHello() throws GordianException {
125             /* Protect against exceptions */
126             try {
127                 /* Create extractor */
128                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
129                 final KEMExtractSpec mySpec = new KEMExtractSpec.Builder(myPrivate.getPrivateKey(), getEncapsulated(),
130                         GordianSymKeyType.AES.toString(), GordianLength.LEN_256.getLength()).withKdfAlgorithm(derivationAlgorithmId()).build();
131                 theGenerator.init(mySpec);
132 
133                 /* Store secret */
134                 final SecretKeyWithEncapsulation mySecret = (SecretKeyWithEncapsulation) theGenerator.generateKey();
135                 storeSecret(mySecret.getEncoded());
136 
137             } catch (InvalidAlgorithmParameterException e) {
138                 throw new GordianCryptoException(ERR_AGREEMENT, e);
139             }
140         }
141     }
142 
143     /**
144      * Jca NewHope Agreement.
145      */
146     public static class JcaNewHopeEngine
147             extends JcaAgreementBase {
148         /**
149          * Key Agreement.
150          */
151         private final KeyAgreement theAgreement;
152 
153         /**
154          * Constructor.
155          *
156          * @param pFactory   the security factory
157          * @param pSpec      the agreementSpec
158          * @param pAgreement the agreement
159          */
160         JcaNewHopeEngine(final GordianCoreAgreementFactory pFactory,
161                          final GordianCoreAgreementSpec pSpec,
162                          final KeyAgreement pAgreement) throws GordianException {
163             /* Initialize underlying class */
164             super(pFactory, pSpec);
165 
166             /* Store the agreement */
167             theAgreement = pAgreement;
168         }
169 
170         @Override
171         public void buildClientHello() throws GordianException {
172             /* Protect against exceptions */
173             try {
174                 /* Derive the secret */
175                 theAgreement.init(null, getRandom());
176                 final JcaPublicKey myTarget = (JcaPublicKey) getPublicKey(getServerKeyPair());
177                 final PublicKey myKey = (PublicKey) theAgreement.doPhase(myTarget.getPublicKey(), true);
178 
179                 /* Store the ephemeral */
180                 final GordianKeyPairSpec mySpec = getSpec().getKeyPairSpec();
181                 final JcaPublicKey myPublic = new JcaPublicKey(mySpec, myKey);
182                 final JcaKeyPair myEphemeral = new JcaKeyPair(myPublic);
183                 setClientEphemeralAsEncapsulated(myEphemeral);
184 
185                 /* Store secret */
186                 storeSecret(theAgreement.generateSecret());
187 
188             } catch (InvalidKeyException e) {
189                 throw new GordianCryptoException(ERR_AGREEMENT, e);
190             }
191         }
192 
193         @Override
194         public void processClientHello() throws GordianException {
195             /* Protect against exceptions */
196             try {
197                 /* Derive the secret */
198                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
199                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
200                 theAgreement.init(myPrivate.getPrivateKey());
201                 theAgreement.doPhase(myPublic.getPublicKey(), true);
202 
203                 /* Store secret */
204                 storeSecret(theAgreement.generateSecret());
205 
206             } catch (InvalidKeyException e) {
207                 throw new GordianCryptoException(ERR_AGREEMENT, e);
208             }
209         }
210     }
211 
212     /**
213      * Jca Anonymous Agreement.
214      */
215     public static class JcaAnonEngine
216             extends JcaAgreementBase {
217         /**
218          * Key Agreement.
219          */
220         private KeyAgreement theAgreement;
221 
222         /**
223          * Constructor.
224          *
225          * @param pFactory   the security factory
226          * @param pSpec      the agreementSpec
227          * @param pAgreement the agreement
228          */
229         JcaAnonEngine(final GordianCoreAgreementFactory pFactory,
230                       final GordianCoreAgreementSpec pSpec,
231                       final KeyAgreement pAgreement) throws GordianException {
232             /* Initialize underlying class */
233             super(pFactory, pSpec);
234 
235             /* Store the agreement */
236             theAgreement = pAgreement;
237         }
238 
239         @Override
240         public void buildClientHello() throws GordianException {
241             /* Protect against exceptions */
242             try {
243                 /* Access keys */
244                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
245                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
246                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
247 
248                 /* Derive the secret */
249                 initAgreement(theAgreement, myPrivate);
250                 theAgreement.doPhase(myPublic.getPublicKey(), true);
251                 storeSecret(theAgreement.generateSecret());
252 
253             } catch (InvalidKeyException e) {
254                 throw new GordianCryptoException(ERR_AGREEMENT, e);
255             }
256         }
257 
258         @Override
259         public void processClientHello() throws GordianException {
260             /* Protect against exceptions */
261             try {
262                 /* Access keys */
263                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
264                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
265                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
266 
267                 /* Derive the secret */
268                 initAgreement(theAgreement, myPrivate);
269                 theAgreement.doPhase(myPublic.getPublicKey(), true);
270                 storeSecret(theAgreement.generateSecret());
271 
272             } catch (InvalidKeyException e) {
273                 throw new GordianCryptoException(ERR_AGREEMENT, e);
274             }
275         }
276     }
277 
278     /**
279      * Jca Basic Agreement.
280      */
281     public static class JcaBasicEngine
282             extends JcaAgreementBase {
283         /**
284          * Key Agreement.
285          */
286         private KeyAgreement theAgreement;
287 
288         /**
289          * Constructor.
290          *
291          * @param pFactory   the security factory
292          * @param pSpec      the agreementSpec
293          * @param pAgreement the agreement
294          */
295         JcaBasicEngine(final GordianCoreAgreementFactory pFactory,
296                        final GordianCoreAgreementSpec pSpec,
297                        final KeyAgreement pAgreement) throws GordianException {
298             /* Initialize underlying class */
299             super(pFactory, pSpec);
300 
301             /* Store the agreement */
302             theAgreement = pAgreement;
303         }
304 
305         @Override
306         public void processClientHello() throws GordianException {
307             /* Protect against exceptions */
308             try {
309                 /* Access keys */
310                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
311                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
312                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
313 
314                 /* Derive the secret */
315                 initAgreement(theAgreement, myPrivate);
316                 theAgreement.doPhase(myPublic.getPublicKey(), true);
317                 storeSecret(theAgreement.generateSecret());
318 
319             } catch (InvalidKeyException e) {
320                 throw new GordianCryptoException(ERR_AGREEMENT, e);
321             }
322         }
323 
324         @Override
325         public void processServerHello() throws GordianException {
326             /* Protect against exceptions */
327             try {
328                 /* Access keys */
329                 final JcaPublicKey myPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
330                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
331                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
332 
333                 /* Derive the secret */
334                 initAgreement(theAgreement, myPrivate);
335                 theAgreement.doPhase(myPublic.getPublicKey(), true);
336                 storeSecret(theAgreement.generateSecret());
337 
338             } catch (InvalidKeyException e) {
339                 throw new GordianCryptoException(ERR_AGREEMENT, e);
340             }
341         }
342     }
343 
344     /**
345      * Jca Unified Agreement.
346      */
347     public static class JcaUnifiedEngine
348             extends JcaAgreementBase {
349         /**
350          * Key Agreement.
351          */
352         private KeyAgreement theAgreement;
353 
354         /**
355          * Constructor.
356          *
357          * @param pFactory   the security factory
358          * @param pSpec      the agreementSpec
359          * @param pAgreement the agreement
360          */
361         JcaUnifiedEngine(final GordianCoreAgreementFactory pFactory,
362                          final GordianCoreAgreementSpec pSpec,
363                          final KeyAgreement pAgreement) throws GordianException {
364             /* Initialize underlying class */
365             super(pFactory, pSpec);
366 
367             /* Store the agreement */
368             theAgreement = pAgreement;
369         }
370 
371         @Override
372         public void processClientHello() throws GordianException {
373             /* Protect against exceptions */
374             try {
375                 /* Access keys */
376                 final JcaPublicKey myClientPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
377                 final JcaPublicKey myClientEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
378                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
379                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
380                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getServerEphemeral());
381                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
382 
383                 /* Derive the secret */
384                 final DHUParameterSpec myParams = new DHUParameterSpec(myEphPublic.getPublicKey(),
385                         myEphPrivate.getPrivateKey(), myClientEphPublic.getPublicKey(), getAdditional());
386                 theAgreement.init(myPrivate.getPrivateKey(), myParams, getRandom());
387                 theAgreement.doPhase(myClientPublic.getPublicKey(), true);
388                 storeSecret(theAgreement.generateSecret());
389 
390             } catch (InvalidKeyException
391                      | InvalidAlgorithmParameterException e) {
392                 throw new GordianCryptoException(ERR_AGREEMENT, e);
393             }
394         }
395 
396         @Override
397         public void processServerHello() throws GordianException {
398             /* Protect against exceptions */
399             try {
400                 /* Access keys */
401                 final JcaPublicKey myServerPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
402                 final JcaPublicKey myServerEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
403                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
404                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
405                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
406                 theAgreement = adjustAgreement(theAgreement, getServerKeyPair());
407 
408                 /* Derive the secret */
409                 final DHUParameterSpec myParams = new DHUParameterSpec(myEphPublic.getPublicKey(),
410                         myEphPrivate.getPrivateKey(), myServerEphPublic.getPublicKey(), getAdditional());
411                 theAgreement.init(myPrivate.getPrivateKey(), myParams);
412                 theAgreement.doPhase(myServerPublic.getPublicKey(), true);
413                 storeSecret(theAgreement.generateSecret());
414 
415             } catch (InvalidKeyException
416                      | InvalidAlgorithmParameterException e) {
417                 throw new GordianCryptoException(ERR_AGREEMENT, e);
418             }
419         }
420     }
421 
422     /**
423      * Jca MQV Agreement.
424      */
425     public static class JcaMQVEngine
426             extends JcaAgreementBase {
427         /**
428          * Key Agreement.
429          */
430         private final KeyAgreement theAgreement;
431 
432         /**
433          * Constructor.
434          *
435          * @param pFactory   the security factory
436          * @param pSpec      the agreementSpec
437          * @param pAgreement the agreement
438          */
439         JcaMQVEngine(final GordianCoreAgreementFactory pFactory,
440                      final GordianCoreAgreementSpec pSpec,
441                      final KeyAgreement pAgreement) throws GordianException {
442             /* Initialize underlying class */
443             super(pFactory, pSpec);
444 
445             /* Store the agreement */
446             theAgreement = pAgreement;
447         }
448 
449         @Override
450         public void processClientHello() throws GordianException {
451             /* Protect against exceptions */
452             try {
453                 /* Access keys */
454                 final JcaPublicKey myClientPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
455                 final JcaPublicKey myClientEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
456                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
457                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
458                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getServerEphemeral());
459 
460                 /* Derive the secret */
461                 final MQVParameterSpec myParams = new MQVParameterSpec(myEphPublic.getPublicKey(),
462                         myEphPrivate.getPrivateKey(), myClientEphPublic.getPublicKey(), getAdditional());
463                 theAgreement.init(myPrivate.getPrivateKey(), myParams, getRandom());
464                 theAgreement.doPhase(myClientPublic.getPublicKey(), true);
465                 storeSecret(theAgreement.generateSecret());
466 
467             } catch (InvalidKeyException
468                      | InvalidAlgorithmParameterException e) {
469                 throw new GordianCryptoException(ERR_AGREEMENT, e);
470             }
471         }
472 
473         @Override
474         public void processServerHello() throws GordianException {
475             /* Protect against exceptions */
476             try {
477                 /* Access keys */
478                 final JcaPublicKey myServerPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
479                 final JcaPublicKey myServerEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
480                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
481                 final JcaPublicKey myEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
482                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
483 
484                 /* Derive the secret */
485                 final MQVParameterSpec myParams = new MQVParameterSpec(myEphPublic.getPublicKey(),
486                         myEphPrivate.getPrivateKey(), myServerEphPublic.getPublicKey(), getAdditional());
487                 theAgreement.init(myPrivate.getPrivateKey(), myParams);
488                 theAgreement.doPhase(myServerPublic.getPublicKey(), true);
489                 storeSecret(theAgreement.generateSecret());
490 
491             } catch (InvalidKeyException
492                      | InvalidAlgorithmParameterException e) {
493                 throw new GordianCryptoException(ERR_AGREEMENT, e);
494             }
495         }
496     }
497 
498     /**
499      * Jca SM2 Agreement.
500      */
501     public static class JcaSM2Engine
502             extends JcaAgreementBase {
503         /**
504          * Key Agreement.
505          */
506         private final KeyAgreement theAgreement;
507 
508         /**
509          * Constructor.
510          *
511          * @param pFactory   the security factory
512          * @param pSpec      the agreementSpec
513          * @param pAgreement the agreement
514          */
515         JcaSM2Engine(final GordianCoreAgreementFactory pFactory,
516                      final GordianCoreAgreementSpec pSpec,
517                      final KeyAgreement pAgreement) throws GordianException {
518             /* Initialize underlying class */
519             super(pFactory, pSpec);
520 
521             /* Store the agreement */
522             theAgreement = pAgreement;
523         }
524 
525         @Override
526         public void processClientHello() throws GordianException {
527             /* Protect against exceptions */
528             try {
529                 /* Access keys */
530                 final JcaPublicKey myClientPublic = (JcaPublicKey) getPublicKey(getClientKeyPair());
531                 final JcaPublicKey myClientEphPublic = (JcaPublicKey) getPublicKey(getClientEphemeral());
532                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getServerKeyPair());
533                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getServerEphemeral());
534 
535                 /* Access IDs */
536                 final byte[] myClientID = getClientName() == null ? EMPTY : getClientName();
537                 final byte[] myServerID = getServerName() == null ? EMPTY : getServerName();
538 
539                 /* Derive the secret */
540                 final SM2KeyExchangeSpec mySpec = new SM2KeyExchangeSpec(false,
541                         myEphPrivate.getPrivateKey(), myClientEphPublic.getPublicKey(), myServerID, myClientID);
542                 theAgreement.init(myPrivate.getPrivateKey(), mySpec);
543                 theAgreement.doPhase(myClientPublic.getPublicKey(), true);
544                 storeSecret(theAgreement.generateSecret());
545 
546             } catch (InvalidKeyException
547                      | InvalidAlgorithmParameterException e) {
548                 throw new GordianCryptoException(ERR_AGREEMENT, e);
549             }
550         }
551 
552         @Override
553         public void processServerHello() throws GordianException {
554             /* Protect against exceptions */
555             try {
556                 /* Access keys */
557                 final JcaPublicKey myServerPublic = (JcaPublicKey) getPublicKey(getServerKeyPair());
558                 final JcaPublicKey myServerEphPublic = (JcaPublicKey) getPublicKey(getServerEphemeral());
559                 final JcaPrivateKey myPrivate = (JcaPrivateKey) getPrivateKey(getClientKeyPair());
560                 final JcaPrivateKey myEphPrivate = (JcaPrivateKey) getPrivateKey(getClientEphemeral());
561 
562                 /* Access IDs */
563                 final byte[] myClientID = getClientName() == null ? EMPTY : getClientName();
564                 final byte[] myServerID = getServerName() == null ? EMPTY : getServerName();
565 
566                 /* Derive the secret */
567                 final SM2KeyExchangeSpec mySpec = new SM2KeyExchangeSpec(true,
568                         myEphPrivate.getPrivateKey(), myServerEphPublic.getPublicKey(), myClientID, myServerID);
569                 theAgreement.init(myPrivate.getPrivateKey(), mySpec);
570                 theAgreement.doPhase(myServerPublic.getPublicKey(), true);
571                 storeSecret(theAgreement.generateSecret());
572 
573             } catch (InvalidKeyException
574                      | InvalidAlgorithmParameterException e) {
575                 throw new GordianCryptoException(ERR_AGREEMENT, e);
576             }
577         }
578     }
579 
580     /**
581      * Base Agreement Engine class.
582      */
583     public abstract static class JcaAgreementBase
584             extends GordianCoreAgreementEngine {
585         /**
586          * Empty byteArray.
587          */
588         static final byte[] EMPTY = new byte[0];
589 
590         /**
591          * Constructor.
592          *
593          * @param pFactory the security factory
594          * @param pSpec    the agreementSpec
595          * @throws GordianException on error
596          */
597         JcaAgreementBase(final GordianCoreAgreementFactory pFactory,
598                          final GordianCoreAgreementSpec pSpec) throws GordianException {
599             /* Invoke underlying constructor */
600             super(pFactory, pSpec);
601         }
602 
603         @Override
604         protected GordianPublicKey getPublicKey(final GordianKeyPair pKeyPair) throws GordianException {
605             /* Validate the keyPair */
606             if (!(pKeyPair instanceof JcaKeyPair)) {
607                 /* Reject keyPair */
608                 throw new GordianDataException("Invalid KeyPair");
609             }
610 
611             /* Access private key */
612             return super.getPublicKey(pKeyPair);
613         }
614 
615         @Override
616         protected GordianPrivateKey getPrivateKey(final GordianKeyPair pKeyPair) throws GordianException {
617             /* Validate the keyPair */
618             if (!(pKeyPair instanceof JcaKeyPair)) {
619                 /* Reject keyPair */
620                 throw new GordianDataException("Invalid KeyPair");
621             }
622 
623             /* Access private key */
624             return super.getPrivateKey(pKeyPair);
625         }
626 
627         /**
628          * Initialise agreement.
629          *
630          * @param pAgreement the agreement
631          * @param pPrivate   the private key
632          */
633         void initAgreement(final KeyAgreement pAgreement,
634                            final JcaPrivateKey pPrivate) throws GordianException {
635             /* Protect against exceptions */
636             try {
637                 if (getSpec().getKDFType() == GordianAgreementKDF.NONE) {
638                     pAgreement.init(pPrivate.getPrivateKey(), getRandom());
639                 } else {
640                     pAgreement.init(pPrivate.getPrivateKey(), new UserKeyingMaterialSpec(getAdditional()), getRandom());
641                 }
642             } catch (InvalidKeyException
643                      | InvalidAlgorithmParameterException e) {
644                 throw new GordianCryptoException(ERR_AGREEMENT, e);
645             }
646         }
647 
648         /**
649          * Obtain the additionalInfo.
650          *
651          * @return the additionalData
652          */
653         byte[] getAdditional() {
654             final byte[] myAdditional = this.getBuilder().getState().getAdditionalData();
655             return myAdditional == null ? EMPTY : myAdditional;
656         }
657 
658         /**
659          * adjust agreement if necessary.
660          *
661          * @param pCurrent the current agreement
662          * @param pKeyPair the keyPair
663          * @return the adjusted agreement
664          * @throws GordianException on error
665          */
666         KeyAgreement adjustAgreement(final KeyAgreement pCurrent,
667                                      final GordianKeyPair pKeyPair) throws GordianException {
668             /* If we need to change agreement based on keySpec */
669             if (getSpec().getKeyPairSpec().getKeyPairType().equals(GordianKeyPairType.XDH)) {
670                 final String myBase = pKeyPair.getKeyPairSpec().toString();
671                 final String myXtra = GordianAgreementType.UNIFIED.equals(getSpec().getAgreementType())
672                         ? "U" : "";
673                 final String myName = getFullAgreementName(myBase + myXtra, getSpec());
674                 return getJavaKeyAgreement(myName, false);
675             }
676 
677             /* Just return the current agreement */
678             return pCurrent;
679         }
680 
681         /**
682          * Obtain the required derivation id.
683          *
684          * @return the derivation id
685          */
686         AlgorithmIdentifier derivationAlgorithmId() {
687             final GordianCoreAgreementSpec mySpec = getSpec();
688             return switch (mySpec.getKDFType()) {
689                 case SHA256KDF ->
690                         new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf2, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
691                 case SHA512KDF ->
692                         new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf2, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
693                 case SHA256CKDF ->
694                         new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
695                 case SHA512CKDF ->
696                         new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));
697                 case SHA256HKDF -> new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha256, null);
698                 case SHA512HKDF -> new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha512, null);
699                 case KMAC128 -> new AlgorithmIdentifier(NISTObjectIdentifiers.id_Kmac128, null);
700                 case KMAC256 -> new AlgorithmIdentifier(NISTObjectIdentifiers.id_Kmac256, null);
701                 case SHAKE256 -> new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256, null);
702                 default -> null;
703             };
704         }
705     }
706 
707     /**
708      * Create the BouncyCastle KeyGenerator via JCA.
709      *
710      * @param pSpec the KeySpec
711      * @return the KeyFactory
712      * @throws GordianException on error
713      */
714     static KeyGenerator getJavaKeyGenerator(final GordianKeyPairSpec pSpec) throws GordianException {
715         /* Protect against exceptions */
716         try {
717             /* Determine the algorithm name */
718             String myName = pSpec.getKeyPairType().toString();
719             final GordianCoreKeyPairSpec mySpec = (GordianCoreKeyPairSpec) pSpec;
720             switch (pSpec.getKeyPairType()) {
721                 case NTRUPRIME:
722                     final GordianCoreNTRUPrimeSpec myNTRUSpec = mySpec.getNTRUPrimeSpec();
723                     myName = myNTRUSpec.getType() + "PRIME";
724                     break;
725                 case MLKEM:
726                     myName = "ML-KEM";
727                     break;
728                 default:
729                     break;
730             }
731 
732             /* Determine source of keyGenerator */
733             final Provider myProvider = mySpec.getCoreKeyPairType().isStandardJca() ? JcaProvider.BCPROV : JcaProvider.BCPQPROV;
734 
735             /* Return a KeyAgreement for the algorithm */
736             return KeyGenerator.getInstance(myName, myProvider);
737 
738             /* Catch exceptions */
739         } catch (NoSuchAlgorithmException e) {
740             /* Throw the exception */
741             throw new GordianCryptoException("Failed to create KeyGenerator", e);
742         }
743     }
744 
745     /**
746      * Create the BouncyCastle KeyFactory via JCA.
747      *
748      * @param pAlgorithm  the Algorithm
749      * @param postQuantum is this a postQuantum algorithm?
750      * @return the KeyFactory
751      * @throws GordianException on error
752      */
753     static KeyAgreement getJavaKeyAgreement(final String pAlgorithm,
754                                             final boolean postQuantum) throws GordianException {
755         /* Protect against exceptions */
756         try {
757             /* Return a KeyAgreement for the algorithm */
758             return KeyAgreement.getInstance(pAlgorithm, postQuantum
759                     ? JcaProvider.BCPQPROV
760                     : JcaProvider.BCPROV);
761 
762             /* Catch exceptions */
763         } catch (NoSuchAlgorithmException e) {
764             /* Throw the exception */
765             throw new GordianCryptoException("Failed to create KeyAgreement", e);
766         }
767     }
768 
769     /**
770      * Obtain the agreement name.
771      *
772      * @param pBase          the base agreement
773      * @param pAgreementSpec the agreementSpec
774      * @return the full agreement name
775      * @throws GordianException on error
776      */
777     static String getFullAgreementName(final String pBase,
778                                        final GordianCoreAgreementSpec pAgreementSpec) throws GordianException {
779         return switch (pAgreementSpec.getKDFType()) {
780             case NONE -> pBase;
781             case SHA256KDF -> pBase + "withSHA256KDF";
782             case SHA512KDF -> pBase + "withSHA512KDF";
783             case SHA256CKDF -> pBase + "withSHA256CKDF";
784             case SHA512CKDF -> pBase + "withSHA512CKDF";
785             case SHA256HKDF -> pBase + "withSHA256HKDF";
786             case SHA512HKDF -> pBase + "withSHA512HKDF";
787             default -> throw new GordianDataException(GordianBaseData.getInvalidText(pAgreementSpec));
788         };
789     }
790 }