View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.gordianknot.impl.bc;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
21  import io.github.tonywasher.joceanus.gordianknot.api.keypair.spec.GordianKeyPairSpec;
22  import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianNewSignParams;
23  import io.github.tonywasher.joceanus.gordianknot.api.sign.spec.GordianSignatureSpec;
24  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPrivateKey;
25  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPublicKey;
26  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncySignature.BouncyDERCoder;
27  import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncySignature.BouncyDigestSignature;
28  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
29  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
30  import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianKeyPairValidity;
31  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreDSASpec;
32  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreKeyPairSpec;
33  import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
34  import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
35  import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
36  import org.bouncycastle.crypto.DSA;
37  import org.bouncycastle.crypto.digests.SHA256Digest;
38  import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
39  import org.bouncycastle.crypto.generators.DSAParametersGenerator;
40  import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
41  import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
42  import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
43  import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
44  import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
45  import org.bouncycastle.crypto.params.ParametersWithRandom;
46  import org.bouncycastle.crypto.util.PrivateKeyFactory;
47  import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
48  import org.bouncycastle.crypto.util.PublicKeyFactory;
49  import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
50  
51  import java.io.IOException;
52  import java.math.BigInteger;
53  import java.security.spec.PKCS8EncodedKeySpec;
54  import java.security.spec.X509EncodedKeySpec;
55  
56  /**
57   * DSA KeyPair classes.
58   */
59  public final class BouncyDSAKeyPair {
60      /**
61       * Private constructor.
62       */
63      private BouncyDSAKeyPair() {
64      }
65  
66      /**
67       * Bouncy DSA PublicKey.
68       */
69      public static class BouncyDSAPublicKey
70              extends BouncyPublicKey<DSAPublicKeyParameters> {
71          /**
72           * Constructor.
73           *
74           * @param pKeySpec   the keySpec
75           * @param pPublicKey the public key
76           */
77          BouncyDSAPublicKey(final GordianKeyPairSpec pKeySpec,
78                             final DSAPublicKeyParameters pPublicKey) {
79              super(pKeySpec, pPublicKey);
80          }
81  
82          @Override
83          protected boolean matchKey(final AsymmetricKeyParameter pThat) {
84              /* Access keys */
85              final DSAPublicKeyParameters myThis = getPublicKey();
86              final DSAPublicKeyParameters myThat = (DSAPublicKeyParameters) pThat;
87  
88              /* Compare keys */
89              return compareKeys(myThis, myThat);
90          }
91  
92          /**
93           * Is the private key valid for this public key?
94           *
95           * @param pPrivate the private key
96           * @return true/false
97           */
98          public boolean validPrivate(final BouncyDSAPrivateKey pPrivate) {
99              final DSAPrivateKeyParameters myPrivate = pPrivate.getPrivateKey();
100             return getPublicKey().getParameters().equals(myPrivate.getParameters());
101         }
102 
103         /**
104          * CompareKeys.
105          *
106          * @param pFirst  the first key
107          * @param pSecond the second key
108          * @return true/false
109          */
110         private static boolean compareKeys(final DSAPublicKeyParameters pFirst,
111                                            final DSAPublicKeyParameters pSecond) {
112             return pFirst.getY().equals(pSecond.getY())
113                     && pFirst.getParameters().equals(pSecond.getParameters());
114         }
115     }
116 
117     /**
118      * Bouncy DSA PrivateKey.
119      */
120     public static class BouncyDSAPrivateKey
121             extends BouncyPrivateKey<DSAPrivateKeyParameters> {
122         /**
123          * Constructor.
124          *
125          * @param pKeySpec    the keySpec
126          * @param pPrivateKey the private key
127          */
128         BouncyDSAPrivateKey(final GordianKeyPairSpec pKeySpec,
129                             final DSAPrivateKeyParameters pPrivateKey) {
130             super(pKeySpec, pPrivateKey);
131         }
132 
133         @Override
134         protected boolean matchKey(final AsymmetricKeyParameter pThat) {
135             /* Access keys */
136             final DSAPrivateKeyParameters myThis = getPrivateKey();
137             final DSAPrivateKeyParameters myThat = (DSAPrivateKeyParameters) pThat;
138 
139             /* Compare keys */
140             return compareKeys(myThis, myThat);
141         }
142 
143         /**
144          * CompareKeys.
145          *
146          * @param pFirst  the first key
147          * @param pSecond the second key
148          * @return true/false
149          */
150         private static boolean compareKeys(final DSAPrivateKeyParameters pFirst,
151                                            final DSAPrivateKeyParameters pSecond) {
152             return pFirst.getX().equals(pSecond.getX())
153                     && pFirst.getParameters().equals(pSecond.getParameters());
154         }
155     }
156 
157     /**
158      * BouncyCastle DSA KeyPair generator.
159      */
160     public static class BouncyDSAKeyPairGenerator
161             extends BouncyKeyPairGenerator {
162         /**
163          * Generator.
164          */
165         private final DSAKeyPairGenerator theGenerator;
166 
167         /**
168          * Constructor.
169          *
170          * @param pFactory the Security Factory
171          * @param pKeySpec the keySpec
172          */
173         BouncyDSAKeyPairGenerator(final GordianBaseFactory pFactory,
174                                   final GordianKeyPairSpec pKeySpec) {
175             /* Initialise underlying class */
176             super(pFactory, pKeySpec);
177 
178             /* Create the parameter generator */
179             final GordianCoreKeyPairSpec myKeySpec = (GordianCoreKeyPairSpec) pKeySpec;
180             final GordianCoreDSASpec myKeyType = myKeySpec.getDSASpec();
181             final DSAParameterGenerationParameters myGenParms = new DSAParameterGenerationParameters(myKeyType.getKeySize(),
182                     myKeyType.getHashSize(), PRIME_CERTAINTY, getRandom());
183             final DSAParametersGenerator myParmGenerator = new DSAParametersGenerator(new SHA256Digest());
184             myParmGenerator.init(myGenParms);
185 
186             /* Create and initialise the generator */
187             theGenerator = new DSAKeyPairGenerator();
188             final DSAKeyGenerationParameters myParams = new DSAKeyGenerationParameters(getRandom(), myParmGenerator.generateParameters());
189             theGenerator.init(myParams);
190         }
191 
192         @Override
193         public BouncyKeyPair generateKeyPair() {
194             /* Generate and return the keyPair */
195             final AsymmetricCipherKeyPair myPair = theGenerator.generateKeyPair();
196             final BouncyDSAPublicKey myPublic = new BouncyDSAPublicKey(getKeySpec(), (DSAPublicKeyParameters) myPair.getPublic());
197             final BouncyDSAPrivateKey myPrivate = new BouncyDSAPrivateKey(getKeySpec(), (DSAPrivateKeyParameters) myPair.getPrivate());
198             return new BouncyKeyPair(myPublic, myPrivate);
199         }
200 
201         @Override
202         public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
203             /* Protect against exceptions */
204             try {
205                 /* Check the keyPair type and keySpecs */
206                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
207 
208                 /* build and return the encoding */
209                 final BouncyDSAPrivateKey myPrivateKey = (BouncyDSAPrivateKey) getPrivateKey(pKeyPair);
210                 final DSAPrivateKeyParameters myParms = myPrivateKey.getPrivateKey();
211                 final PrivateKeyInfo myInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(myParms);
212                 return new PKCS8EncodedKeySpec(myInfo.getEncoded());
213 
214             } catch (IOException e) {
215                 throw new GordianCryptoException(ERROR_PARSE, e);
216             }
217         }
218 
219         @Override
220         public BouncyKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
221                                            final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
222             /* Protect against exceptions */
223             try {
224                 /* Check the keySpecs */
225                 checkKeySpec(pPrivateKey);
226 
227                 /* derive keyPair */
228                 final BouncyDSAPublicKey myPublic = derivePublicKey(pPublicKey);
229                 final PrivateKeyInfo myInfo = PrivateKeyInfo.getInstance(pPrivateKey.getEncoded());
230                 final DSAPrivateKeyParameters myParms = (DSAPrivateKeyParameters) PrivateKeyFactory.createKey(myInfo);
231                 final BouncyDSAPrivateKey myPrivate = new BouncyDSAPrivateKey(getKeySpec(), myParms);
232                 final BouncyKeyPair myPair = new BouncyKeyPair(myPublic, myPrivate);
233 
234                 /* Check that we have a matching pair */
235                 GordianKeyPairValidity.checkValidity(getFactory(), myPair);
236 
237                 /* Return the keyPair */
238                 return myPair;
239 
240             } catch (IOException e) {
241                 throw new GordianCryptoException(ERROR_PARSE, e);
242             }
243         }
244 
245         @Override
246         public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
247             /* Protect against exceptions */
248             try {
249                 /* Check the keyPair type and keySpecs */
250                 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
251 
252                 /* build and return the encoding */
253                 final BouncyDSAPublicKey myPublicKey = (BouncyDSAPublicKey) getPublicKey(pKeyPair);
254                 final DSAPublicKeyParameters myParms = myPublicKey.getPublicKey();
255                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(myParms);
256                 return new X509EncodedKeySpec(myInfo.getEncoded());
257 
258             } catch (IOException e) {
259                 throw new GordianCryptoException(ERROR_PARSE, e);
260             }
261         }
262 
263         @Override
264         public BouncyKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pEncodedKey) throws GordianException {
265             final BouncyDSAPublicKey myPublic = derivePublicKey(pEncodedKey);
266             return new BouncyKeyPair(myPublic);
267         }
268 
269         /**
270          * Derive public key from encoded.
271          *
272          * @param pEncodedKey the encoded key
273          * @return the public key
274          * @throws GordianException on error
275          */
276         private BouncyDSAPublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
277             /* Protect against exceptions */
278             try {
279                 /* Check the keySpecs */
280                 checkKeySpec(pEncodedKey);
281 
282                 /* derive publicKey */
283                 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(pEncodedKey.getEncoded());
284                 final DSAPublicKeyParameters myParms = (DSAPublicKeyParameters) PublicKeyFactory.createKey(myInfo);
285                 return new BouncyDSAPublicKey(getKeySpec(), myParms);
286 
287             } catch (IOException e) {
288                 throw new GordianCryptoException(ERROR_PARSE, e);
289             }
290         }
291     }
292 
293     /**
294      * DSA signer.
295      */
296     public static class BouncyDSASignature
297             extends BouncyDigestSignature {
298         /**
299          * The Signer.
300          */
301         private final DSA theSigner;
302 
303         /**
304          * The Coder.
305          */
306         private final BouncyDERCoder theCoder;
307 
308         /**
309          * Constructor.
310          *
311          * @param pFactory the factory
312          * @param pSpec    the signatureSpec.
313          * @throws GordianException on error
314          */
315         BouncyDSASignature(final GordianBaseFactory pFactory,
316                            final GordianSignatureSpec pSpec) throws GordianException {
317             /* Initialise underlying class */
318             super(pFactory, pSpec);
319 
320             /* Create the signer */
321             theSigner = BouncySignature.getDSASigner(pFactory, pSpec);
322             theCoder = new BouncyDERCoder();
323         }
324 
325         @Override
326         public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
327             /* Initialise detail */
328             super.initForSigning(pParams);
329             final BouncyKeyPair myPair = getKeyPair();
330             BouncyKeyPair.checkKeyPair(myPair);
331 
332             /* Initialise and set the signer */
333             final BouncyDSAPrivateKey myPrivate = (BouncyDSAPrivateKey) myPair.getPrivateKey();
334             final ParametersWithRandom myParms = new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom());
335             theSigner.init(true, myParms);
336         }
337 
338         @Override
339         public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
340             /* Initialise detail */
341             super.initForVerify(pParams);
342             final BouncyKeyPair myPair = getKeyPair();
343             BouncyKeyPair.checkKeyPair(myPair);
344 
345             /* Initialise and set the signer */
346             final BouncyDSAPublicKey myPublic = (BouncyDSAPublicKey) myPair.getPublicKey();
347             theSigner.init(false, myPublic.getPublicKey());
348         }
349 
350         @Override
351         public byte[] sign() throws GordianException {
352             /* Check that we are in signing mode */
353             checkMode(GordianSignatureMode.SIGN);
354 
355             /* Sign the message */
356             final BigInteger[] myValues = theSigner.generateSignature(getDigest());
357             return theCoder.dsaEncode(myValues[0], myValues[1]);
358         }
359 
360         @Override
361         public boolean verify(final byte[] pSignature) throws GordianException {
362             /* Check that we are in verify mode */
363             checkMode(GordianSignatureMode.VERIFY);
364 
365             /* Verify the message */
366             final BigInteger[] myValues = theCoder.dsaDecode(pSignature);
367             return theSigner.verifySignature(getDigest(), myValues[0], myValues[1]);
368         }
369     }
370 }