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