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.digest.GordianDigestFactory;
21  import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestSpec;
22  import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestSpecBuilder;
23  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
24  import io.github.tonywasher.joceanus.gordianknot.api.keypair.spec.GordianKeyPairSpec;
25  import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianNewSignParams;
26  import io.github.tonywasher.joceanus.gordianknot.api.sign.spec.GordianSignatureSpec;
27  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPrivateKey;
28  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPublicKey;
29  import io.github.tonywasher.joceanus.gordianknot.impl.core.agree.GordianCoreAgreementFactory;
30  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
31  import io.github.tonywasher.joceanus.gordianknot.impl.core.encrypt.GordianCoreEncryptor;
32  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
33  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianIOException;
34  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianKeyPairValidity;
35  import io.github.tonywasher.joceanus.gordianknot.impl.core.sign.GordianCoreSignature;
36  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.agree.GordianCoreAgreementSpec;
37  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.encrypt.GordianCoreEncryptorSpec;
38  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreKeyPairSpec;
39  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.sign.GordianCoreSignatureSpec;
40  import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
41  import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
42  import org.bouncycastle.crypto.AsymmetricBlockCipher;
43  import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
44  import org.bouncycastle.crypto.CipherParameters;
45  import org.bouncycastle.crypto.CryptoException;
46  import org.bouncycastle.crypto.DataLengthException;
47  import org.bouncycastle.crypto.DerivationFunction;
48  import org.bouncycastle.crypto.InvalidCipherTextException;
49  import org.bouncycastle.crypto.SecretWithEncapsulation;
50  import org.bouncycastle.crypto.Signer;
51  import org.bouncycastle.crypto.encodings.OAEPEncoding;
52  import org.bouncycastle.crypto.engines.RSABlindedEngine;
53  import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
54  import org.bouncycastle.crypto.kems.RSAKEMExtractor;
55  import org.bouncycastle.crypto.kems.RSAKEMGenerator;
56  import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
57  import org.bouncycastle.crypto.params.ParametersWithRandom;
58  import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
59  import org.bouncycastle.crypto.params.RSAKeyParameters;
60  import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
61  import org.bouncycastle.crypto.signers.ISO9796d2Signer;
62  import org.bouncycastle.crypto.signers.ISOTrailers;
63  import org.bouncycastle.crypto.signers.PSSSigner;
64  import org.bouncycastle.crypto.signers.RSADigestSigner;
65  import org.bouncycastle.crypto.signers.X931Signer;
66  import org.bouncycastle.crypto.util.PrivateKeyFactory;
67  import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
68  import org.bouncycastle.crypto.util.PublicKeyFactory;
69  import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
70  
71  import javax.crypto.spec.PSource;
72  import javax.security.auth.DestroyFailedException;
73  import java.io.IOException;
74  import java.math.BigInteger;
75  import java.security.spec.PKCS8EncodedKeySpec;
76  import java.security.spec.X509EncodedKeySpec;
77  import java.util.Arrays;
78  
79  /**
80   * RSA KeyPair classes.
81   */
82  public final class BouncyRSAKeyPair {
83      /**
84       * Private constructor.
85       */
86      private BouncyRSAKeyPair() {
87      }
88  
89      /**
90       * Bouncy RSA PublicKey.
91       */
92      public static class BouncyRSAPublicKey
93              extends BouncyPublicKey<RSAKeyParameters> {
94          /**
95           * Constructor.
96           *
97           * @param pKeySpec   the keySpec
98           * @param pPublicKey the public key
99           */
100         BouncyRSAPublicKey(final GordianKeyPairSpec pKeySpec,
101                            final RSAKeyParameters pPublicKey) {
102             super(pKeySpec, pPublicKey);
103         }
104 
105         @Override
106         protected boolean matchKey(final AsymmetricKeyParameter pThat) {
107             /* Access keys */
108             final RSAKeyParameters myThis = getPublicKey();
109             final RSAKeyParameters myThat = (RSAKeyParameters) pThat;
110 
111             /* Compare keys */
112             return compareKeys(myThis, myThat);
113         }
114 
115         /**
116          * CompareKeys.
117          *
118          * @param pFirst  the first key
119          * @param pSecond the second key
120          * @return true/false
121          */
122         private static boolean compareKeys(final RSAKeyParameters pFirst,
123                                            final RSAKeyParameters pSecond) {
124             return pFirst.getExponent().equals(pSecond.getExponent())
125                     && pFirst.getModulus().equals(pSecond.getModulus());
126         }
127 
128         /**
129          * Is the private key valid for this public key?
130          *
131          * @param pPrivate the private key
132          * @return true/false
133          */
134         public boolean validPrivate(final BouncyRSAPrivateKey pPrivate) {
135             final RSAPrivateCrtKeyParameters myPrivate = pPrivate.getPrivateKey();
136             return getPublicKey().getExponent().equals(myPrivate.getExponent())
137                     && getPublicKey().getModulus().equals(myPrivate.getModulus());
138         }
139     }
140 
141     /**
142      * Bouncy RSA PrivateKey.
143      */
144     public static class BouncyRSAPrivateKey
145             extends BouncyPrivateKey<RSAPrivateCrtKeyParameters> {
146         /**
147          * Constructor.
148          *
149          * @param pKeySpec    the keySpec
150          * @param pPrivateKey the private key
151          */
152         BouncyRSAPrivateKey(final GordianKeyPairSpec pKeySpec,
153                             final RSAPrivateCrtKeyParameters pPrivateKey) {
154             super(pKeySpec, pPrivateKey);
155         }
156 
157         @Override
158         protected boolean matchKey(final AsymmetricKeyParameter pThat) {
159             /* Access keys */
160             final RSAPrivateCrtKeyParameters myThis = getPrivateKey();
161             final RSAPrivateCrtKeyParameters myThat = (RSAPrivateCrtKeyParameters) pThat;
162 
163             /* Compare keys */
164             return compareKeys(myThis, myThat);
165         }
166 
167         /**
168          * CompareKeys.
169          *
170          * @param pFirst  the first key
171          * @param pSecond the second key
172          * @return true/false
173          */
174         private static boolean compareKeys(final RSAPrivateCrtKeyParameters pFirst,
175                                            final RSAPrivateCrtKeyParameters pSecond) {
176             if (!pFirst.getExponent().equals(pSecond.getExponent())
177                     || !pFirst.getModulus().equals(pSecond.getModulus())) {
178                 return false;
179             }
180 
181             if (!pFirst.getP().equals(pSecond.getP())
182                     || !pFirst.getQ().equals(pSecond.getQ())) {
183                 return false;
184             }
185 
186             if (!pFirst.getDP().equals(pSecond.getDP())
187                     || !pFirst.getDQ().equals(pSecond.getDQ())) {
188                 return false;
189             }
190 
191             return pFirst.getPublicExponent().equals(pSecond.getPublicExponent())
192                     && pFirst.getQInv().equals(pSecond.getQInv());
193         }
194     }
195 
196     /**
197      * BouncyCastle RSA KeyPair generator.
198      */
199     public static class BouncyRSAKeyPairGenerator
200             extends BouncyKeyPairGenerator {
201         /**
202          * RSA exponent.
203          */
204         private static final BigInteger RSA_EXPONENT = new BigInteger("10001", 16);
205 
206         /**
207          * Generator.
208          */
209         private final RSAKeyPairGenerator theGenerator;
210 
211         /**
212          * Constructor.
213          *
214          * @param pFactory the Security Factory
215          * @param pKeySpec the keySpec
216          */
217         BouncyRSAKeyPairGenerator(final GordianBaseFactory pFactory,
218                                   final GordianKeyPairSpec pKeySpec) {
219             /* Initialise underlying class */
220             super(pFactory, pKeySpec);
221 
222             /* Create and initialise the generator */
223             theGenerator = new RSAKeyPairGenerator();
224             final GordianCoreKeyPairSpec myKeySpec = (GordianCoreKeyPairSpec) pKeySpec;
225             final RSAKeyGenerationParameters myParams
226                     = new RSAKeyGenerationParameters(RSA_EXPONENT, getRandom(), myKeySpec.getRSASpec().getLength(), PRIME_CERTAINTY);
227             theGenerator.init(myParams);
228         }
229 
230         @Override
231         public BouncyKeyPair generateKeyPair() {
232             /* Generate and return the keyPair */
233             final AsymmetricCipherKeyPair myPair = theGenerator.generateKeyPair();
234             final BouncyRSAPublicKey myPublic = new BouncyRSAPublicKey(getKeySpec(), (RSAKeyParameters) myPair.getPublic());
235             final BouncyRSAPrivateKey myPrivate = new BouncyRSAPrivateKey(getKeySpec(), (RSAPrivateCrtKeyParameters) myPair.getPrivate());
236             return new BouncyKeyPair(myPublic, myPrivate);
237         }
238 
239         @Override
240         public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
241             /* Protect against exceptions */
242             try {
243                 /* Check the keyPair type and keySpecs */
244                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
245 
246                 /* build and return the encoding */
247                 final BouncyRSAPrivateKey myPrivateKey = (BouncyRSAPrivateKey) getPrivateKey(pKeyPair);
248                 final RSAPrivateCrtKeyParameters myParms = myPrivateKey.getPrivateKey();
249                 final PrivateKeyInfo myInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(myParms);
250                 return new PKCS8EncodedKeySpec(myInfo.getEncoded());
251 
252             } catch (IOException e) {
253                 throw new GordianCryptoException(ERROR_PARSE, e);
254             }
255         }
256 
257         @Override
258         public BouncyKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
259                                            final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
260             /* Protect against exceptions */
261             try {
262                 /* Check the keySpecs */
263                 checkKeySpec(pPrivateKey);
264 
265                 /* derive keyPair */
266                 final BouncyRSAPublicKey myPublic = derivePublicKey(pPublicKey);
267                 final PrivateKeyInfo myInfo = PrivateKeyInfo.getInstance(pPrivateKey.getEncoded());
268                 final RSAPrivateCrtKeyParameters myParms = (RSAPrivateCrtKeyParameters) PrivateKeyFactory.createKey(myInfo);
269                 final BouncyRSAPrivateKey myPrivate = new BouncyRSAPrivateKey(getKeySpec(), myParms);
270                 final BouncyKeyPair myPair = new BouncyKeyPair(myPublic, myPrivate);
271 
272                 /* Check that we have a matching pair */
273                 GordianKeyPairValidity.checkValidity(getFactory(), myPair);
274 
275                 /* Return the keyPair */
276                 return myPair;
277 
278             } catch (IOException e) {
279                 throw new GordianCryptoException(ERROR_PARSE, e);
280             }
281         }
282 
283         @Override
284         public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
285             /* Protect against exceptions */
286             try {
287                 /* Check the keyPair type and keySpecs */
288                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
289 
290                 /* build and return the encoding */
291                 final BouncyRSAPublicKey myPublicKey = (BouncyRSAPublicKey) getPublicKey(pKeyPair);
292                 final RSAKeyParameters myParms = myPublicKey.getPublicKey();
293                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(myParms);
294                 return new X509EncodedKeySpec(myInfo.getEncoded());
295 
296             } catch (IOException e) {
297                 throw new GordianCryptoException(ERROR_PARSE, e);
298             }
299         }
300 
301         @Override
302         public BouncyKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pEncodedKey) throws GordianException {
303             final BouncyRSAPublicKey myPublic = derivePublicKey(pEncodedKey);
304             return new BouncyKeyPair(myPublic);
305         }
306 
307         /**
308          * Derive public key from encoded.
309          *
310          * @param pEncodedKey the encoded key
311          * @return the public key
312          * @throws GordianException on error
313          */
314         private BouncyRSAPublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
315             /* Protect against exceptions */
316             try {
317                 /* Check the keySpecs */
318                 checkKeySpec(pEncodedKey);
319 
320                 /* derive publicKey */
321                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(pEncodedKey.getEncoded());
322                 final RSAKeyParameters myParms = (RSAKeyParameters) PublicKeyFactory.createKey(myInfo);
323                 return new BouncyRSAPublicKey(getKeySpec(), myParms);
324 
325             } catch (IOException e) {
326                 throw new GordianCryptoException(ERROR_PARSE, e);
327             }
328         }
329     }
330 
331     /**
332      * PSS signature base.
333      */
334     private abstract static class BouncyPSSSignature
335             extends GordianCoreSignature {
336         /**
337          * The RSA Signer.
338          */
339         private final Signer theSigner;
340 
341         /**
342          * Constructor.
343          *
344          * @param pFactory the factory
345          * @param pSpec    the signatureSpec.
346          * @throws GordianException on error
347          */
348         BouncyPSSSignature(final GordianBaseFactory pFactory,
349                            final GordianSignatureSpec pSpec) throws GordianException {
350             super(pFactory, pSpec);
351             theSigner = getRSASigner(pFactory, (GordianCoreSignatureSpec) pSpec);
352         }
353 
354         /**
355          * Obtain the signer.
356          *
357          * @return the signer.
358          */
359         protected Signer getSigner() {
360             return theSigner;
361         }
362 
363         @Override
364         public void update(final byte[] pBytes,
365                            final int pOffset,
366                            final int pLength) {
367             theSigner.update(pBytes, pOffset, pLength);
368         }
369 
370         @Override
371         public void update(final byte pByte) {
372             theSigner.update(pByte);
373         }
374 
375         @Override
376         public void update(final byte[] pBytes) {
377             theSigner.update(pBytes, 0, pBytes.length);
378         }
379 
380         @Override
381         public void reset() {
382             theSigner.reset();
383         }
384 
385         /**
386          * Obtain RSASigner.
387          *
388          * @param pFactory the factory
389          * @param pSpec    the signatureSpec
390          * @return the RSASigner
391          * @throws GordianException on error
392          */
393         private static Signer getRSASigner(final GordianBaseFactory pFactory,
394                                            final GordianCoreSignatureSpec pSpec) throws GordianException {
395             /* Create the digest */
396             final GordianDigestSpec myDigestSpec = pSpec.getDigestSpec();
397             final GordianDigestFactory myFactory = pFactory.getDigestFactory();
398             final BouncyDigest myDigest = (BouncyDigest) myFactory.createDigest(myDigestSpec);
399             final GordianDigestSpecBuilder myBuilder = myFactory.newDigestSpecBuilder();
400             final int mySaltLength = myDigestSpec.getDigestLength().getByteLength();
401 
402             /* Access the signature type */
403             return switch (pSpec.getSignatureType()) {
404                 case ISO9796D2 ->
405                         new ISO9796d2Signer(new RSABlindedEngine(), myDigest.getDigest(), ISOTrailers.noTrailerAvailable(myDigest.getDigest()));
406                 case X931 ->
407                         new X931Signer(new RSABlindedEngine(), myDigest.getDigest(), ISOTrailers.noTrailerAvailable(myDigest.getDigest()));
408                 case PREHASH -> new RSADigestSigner(myDigest.getDigest());
409                 case PSS128 -> new PSSSigner(new RSABlindedEngine(), myDigest.getDigest(),
410                         ((BouncyDigest) pFactory.getDigestFactory().createDigest(myBuilder.shake128())).getDigest(), mySaltLength);
411                 case PSS256 -> new PSSSigner(new RSABlindedEngine(), myDigest.getDigest(),
412                         ((BouncyDigest) pFactory.getDigestFactory().createDigest(myBuilder.shake256())).getDigest(), mySaltLength);
413                 default -> new PSSSigner(new RSABlindedEngine(), myDigest.getDigest(), mySaltLength);
414             };
415         }
416     }
417 
418     /**
419      * RSA signature.
420      */
421     public static class BouncyRSASignature
422             extends BouncyPSSSignature {
423         /**
424          * Constructor.
425          *
426          * @param pFactory the factory
427          * @param pSpec    the signatureSpec
428          * @throws GordianException on error
429          */
430         BouncyRSASignature(final GordianBaseFactory pFactory,
431                            final GordianSignatureSpec pSpec) throws GordianException {
432             /* Initialise underlying class */
433             super(pFactory, pSpec);
434         }
435 
436         @Override
437         protected BouncyKeyPair getKeyPair() {
438             return (BouncyKeyPair) super.getKeyPair();
439         }
440 
441         @Override
442         public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
443             /* Initialise detail */
444             super.initForSigning(pParams);
445             final BouncyKeyPair myPair = getKeyPair();
446             BouncyKeyPair.checkKeyPair(myPair);
447 
448             /* Initialise and set the signer */
449             final BouncyRSAPrivateKey myPrivate = (BouncyRSAPrivateKey) myPair.getPrivateKey();
450             final CipherParameters myParms = getSignatureSpec().getCoreType().isPSS()
451                     ? new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom())
452                     : myPrivate.getPrivateKey();
453             getSigner().init(true, myParms);
454         }
455 
456         @Override
457         public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
458             /* Initialise detail */
459             super.initForVerify(pParams);
460             final BouncyKeyPair myPair = getKeyPair();
461             BouncyKeyPair.checkKeyPair(myPair);
462 
463             /* Initialise and set the signer */
464             final BouncyRSAPublicKey myPublic = (BouncyRSAPublicKey) myPair.getPublicKey();
465             getSigner().init(false, myPublic.getPublicKey());
466         }
467 
468         @Override
469         public byte[] sign() throws GordianException {
470             /* Check that we are in signing mode */
471             checkMode(GordianSignatureMode.SIGN);
472 
473             /* Sign the message */
474             try {
475                 return getSigner().generateSignature();
476             } catch (DataLengthException
477                      | CryptoException e) {
478                 throw new GordianCryptoException(BouncySignature.ERROR_SIGGEN, e);
479             }
480         }
481 
482         @Override
483         public boolean verify(final byte[] pSignature) throws GordianException {
484             /* Check that we are in verify mode */
485             checkMode(GordianSignatureMode.VERIFY);
486 
487             /* Verify the message */
488             return getSigner().verifySignature(pSignature);
489         }
490     }
491 
492     /**
493      * RSA Agreement Engine.
494      */
495     public static class BouncyRSAAgreementEngine
496             extends BouncyAgreementBase {
497         /**
498          * Key Length.
499          */
500         private static final int KEYLEN = 32;
501 
502         /**
503          * Derivation function.
504          */
505         private final DerivationFunction theDerivation;
506 
507         /**
508          * Constructor.
509          *
510          * @param pFactory the security factory
511          * @param pSpec    the agreementSpec
512          * @throws GordianException on error
513          */
514         BouncyRSAAgreementEngine(final GordianCoreAgreementFactory pFactory,
515                                  final GordianCoreAgreementSpec pSpec) throws GordianException {
516             /* Initialize underlying class */
517             super(pFactory, pSpec);
518 
519             /* Initialise the derivation function */
520             theDerivation = newDerivationFunction();
521         }
522 
523         @Override
524         public void buildClientHello() throws GordianException {
525             /* Protect against exceptions */
526             try {
527                 /* Create encapsulation */
528                 final BouncyRSAPublicKey myPublic = (BouncyRSAPublicKey) getPublicKey(getServerKeyPair());
529                 final RSAKEMGenerator myGenerator = new RSAKEMGenerator(KEYLEN, theDerivation, getRandom());
530                 final SecretWithEncapsulation myResult = myGenerator.generateEncapsulated(myPublic.getPublicKey());
531 
532                 /* Store the encapsulation */
533                 setEncapsulated(myResult.getEncapsulation());
534 
535                 /* Store secret and create initVector */
536                 storeSecret(myResult.getSecret());
537                 myResult.destroy();
538 
539             } catch (DestroyFailedException e) {
540                 throw new GordianIOException("Failed to destroy secret", e);
541             }
542         }
543 
544         @Override
545         public void processClientHello() throws GordianException {
546             /* Create encapsulation */
547             final BouncyRSAPrivateKey myPrivate = (BouncyRSAPrivateKey) getPrivateKey(getServerKeyPair());
548             final RSAKEMExtractor myExtractor = new RSAKEMExtractor(myPrivate.getPrivateKey(), KEYLEN, theDerivation);
549 
550             /* Parse encapsulated message and store secret */
551             final byte[] myMessage = getEncapsulated();
552             storeSecret(myExtractor.extractSecret(myMessage));
553         }
554     }
555 
556     /**
557      * RSA Encryptor.
558      */
559     public static class BouncyRSAEncryptor
560             extends BouncyCoreEncryptor {
561         /**
562          * Constructor.
563          *
564          * @param pFactory the factory
565          * @param pSpec    the encryptorSpec
566          * @throws GordianException on error
567          */
568         BouncyRSAEncryptor(final GordianBaseFactory pFactory,
569                            final GordianCoreEncryptorSpec pSpec) throws GordianException {
570             /* Initialise underlying cipher */
571             super(pFactory, pSpec, new RSABlindedEngine());
572         }
573 
574         @Override
575         protected BouncyRSAPublicKey getPublicKey() {
576             return (BouncyRSAPublicKey) super.getPublicKey();
577         }
578 
579         @Override
580         protected BouncyRSAPrivateKey getPrivateKey() {
581             return (BouncyRSAPrivateKey) super.getPrivateKey();
582         }
583     }
584 
585     /**
586      * RSA Encryptor.
587      */
588     public static class BouncyCoreEncryptor
589             extends GordianCoreEncryptor {
590         /**
591          * The underlying encryptor.
592          */
593         private final AsymmetricBlockCipher theEncryptor;
594 
595         /**
596          * Constructor.
597          *
598          * @param pFactory the factory
599          * @param pSpec    the encryptorSpec
600          * @param pEngine  the underlying engine
601          * @throws GordianException on error
602          */
603         protected BouncyCoreEncryptor(final GordianBaseFactory pFactory,
604                                       final GordianCoreEncryptorSpec pSpec,
605                                       final AsymmetricBlockCipher pEngine) throws GordianException {
606             /* Initialise underlying cipher */
607             super(pFactory, pSpec);
608             final BouncyDigest myDigest = (BouncyDigest) pFactory.getDigestFactory().createDigest(pSpec.getDigestSpec());
609             theEncryptor = new OAEPEncoding(pEngine, myDigest.getDigest(), PSource.PSpecified.DEFAULT.getValue());
610         }
611 
612         @Override
613         protected BouncyPublicKey<?> getPublicKey() {
614             return (BouncyPublicKey<?>) super.getPublicKey();
615         }
616 
617         @Override
618         protected BouncyPrivateKey<?> getPrivateKey() {
619             return (BouncyPrivateKey<?>) super.getPrivateKey();
620         }
621 
622         @Override
623         public void initForEncrypt(final GordianKeyPair pKeyPair) throws GordianException {
624             /* Initialise underlying cipher */
625             BouncyKeyPair.checkKeyPair(pKeyPair);
626             super.initForEncrypt(pKeyPair);
627 
628             /* Initialise for encryption */
629             final ParametersWithRandom myParms = new ParametersWithRandom(getPublicKey().getPublicKey(), getRandom());
630             theEncryptor.init(true, myParms);
631         }
632 
633         @Override
634         public void initForDecrypt(final GordianKeyPair pKeyPair) throws GordianException {
635             /* Initialise underlying cipher */
636             BouncyKeyPair.checkKeyPair(pKeyPair);
637             super.initForDecrypt(pKeyPair);
638 
639             /* Initialise for decryption */
640             theEncryptor.init(false, getPrivateKey().getPrivateKey());
641         }
642 
643         @Override
644         public byte[] encrypt(final byte[] pBytes) throws GordianException {
645             /* Check that we are in encryption mode */
646             checkMode(GordianEncryptMode.ENCRYPT);
647 
648             /* Encrypt the message */
649             return processData(pBytes);
650         }
651 
652         @Override
653         public byte[] decrypt(final byte[] pBytes) throws GordianException {
654             /* Check that we are in decryption mode */
655             checkMode(GordianEncryptMode.DECRYPT);
656 
657             /* Decrypt the message */
658             return processData(pBytes);
659         }
660 
661         /**
662          * Process a data buffer.
663          *
664          * @param pData the buffer to process
665          * @return the processed buffer
666          * @throws GordianException on error
667          */
668         private byte[] processData(final byte[] pData) throws GordianException {
669             try {
670                 /* Create the output buffer */
671                 int myInLen = pData.length;
672                 final byte[] myOutput = new byte[getProcessedLength(myInLen)];
673 
674                 /* Access input block length */
675                 final int myInBlockLength = theEncryptor.getInputBlockSize();
676 
677                 /* Loop encrypting the blocks */
678                 int myInOff = 0;
679                 int myOutOff = 0;
680                 while (myInLen > 0) {
681                     /* Process the data */
682                     final int myLen = Math.min(myInLen, myInBlockLength);
683                     final byte[] myBlock = theEncryptor.processBlock(pData, myInOff, myLen);
684 
685                     /* Copy to the output buffer */
686                     final int myOutLen = myBlock.length;
687                     System.arraycopy(myBlock, 0, myOutput, myOutOff, myOutLen);
688                     myOutOff += myOutLen;
689 
690                     /* Move to next block */
691                     myInOff += myInBlockLength;
692                     myInLen -= myInBlockLength;
693                 }
694 
695                 /* Return full buffer if possible */
696                 if (myOutOff == myOutput.length) {
697                     return myOutput;
698                 }
699 
700                 /* Cut down buffer */
701                 final byte[] myReturn = Arrays.copyOf(myOutput, myOutOff);
702                 Arrays.fill(myOutput, (byte) 0);
703                 return myReturn;
704 
705             } catch (InvalidCipherTextException e) {
706                 throw new GordianCryptoException("Failed to process data", e);
707             }
708         }
709 
710         /**
711          * Obtain the length of the buffer required for the processed output.
712          *
713          * @param pLength the length of input data
714          * @return the number of bytes.
715          */
716         private int getProcessedLength(final int pLength) {
717             return theEncryptor.getOutputBlockSize() * getNumBlocks(pLength, theEncryptor.getInputBlockSize());
718         }
719 
720         /**
721          * Obtain the number of blocks required for the length in terms of blocks.
722          *
723          * @param pLength      the length of data
724          * @param pBlockLength the blockLength
725          * @return the number of blocks.
726          */
727         private static int getNumBlocks(final int pLength, final int pBlockLength) {
728             return (pLength + pBlockLength - 1) / pBlockLength;
729         }
730     }
731 }