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