View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.gordianknot.impl.jca.sign;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20  import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestType;
21  import io.github.tonywasher.joceanus.gordianknot.api.keypair.spec.GordianKeyPairType;
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.core.base.GordianBaseFactory;
25  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
26  import io.github.tonywasher.joceanus.gordianknot.impl.core.sign.GordianCoreSignature;
27  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.keypair.GordianCoreKeyPairType;
28  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.sign.GordianCoreSignatureSpec;
29  import io.github.tonywasher.joceanus.gordianknot.impl.jca.base.JcaProvider;
30  import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair;
31  import org.bouncycastle.jcajce.spec.ContextParameterSpec;
32  
33  import java.security.InvalidAlgorithmParameterException;
34  import java.security.InvalidKeyException;
35  import java.security.NoSuchAlgorithmException;
36  import java.security.Signature;
37  import java.security.SignatureException;
38  
39  /**
40   * Jca implementation of signature.
41   */
42  public abstract class JcaSignature
43          extends GordianCoreSignature {
44      /**
45       * The Signature error.
46       */
47      private static final String SIG_ERROR = "Signature error";
48  
49      /**
50       * The RSA PSS MGF1 Algorithm.
51       */
52      private static final String RSA_PSSMGF1_ALGOBASE = "withRSAandMGF1";
53  
54      /**
55       * The RSA PSS SHAKE128 Algorithm.
56       */
57      private static final String RSA_PSS128_ALGOBASE = "withRSAandSHAKE128";
58  
59      /**
60       * The RSA PSS SHAKE256 Algorithm.
61       */
62      private static final String RSA_PSS256_ALGOBASE = "withRSAandSHAKE256";
63  
64      /**
65       * The RSA PSS PureSHAKE Algorithm.
66       */
67      private static final String RSA_PSSSHAKE_ALGOBASE = "withRSA/PSS";
68  
69      /**
70       * The RSA X9.31 Algorithm.
71       */
72      private static final String RSA_X931_ALGOBASE = "withRSA/X9.31";
73  
74      /**
75       * The RSA ISO9796d2 Algorithm.
76       */
77      private static final String RSA_ISO9796D2_ALGOBASE = "withRSA/ISO9796-2";
78  
79      /**
80       * The RSA preHash Algorithm.
81       */
82      private static final String RSA_PREHASH_ALGOBASE = "withRSAEncryption";
83  
84      /**
85       * The ECDSA Signature.
86       */
87      private static final String EC_DSA_ALGOBASE = "withECDSA";
88  
89      /**
90       * The ECDDSA Signature.
91       */
92      private static final String EC_DDSA_ALGOBASE = "withECDDSA";
93  
94      /**
95       * The DSA Signature.
96       */
97      private static final String DSA_ALGOBASE = "withDSA";
98  
99      /**
100      * The DDSA Signature.
101      */
102     private static final String DDSA_ALGOBASE = "withDDSA";
103 
104     /**
105      * The ECNR Signature.
106      */
107     private static final String EC_NR_ALGOBASE = "withECNR";
108 
109     /**
110      * The SM2 Signature.
111      */
112     private static final String EC_SM2_ALGOBASE = "WITHSM2";
113 
114     /**
115      * The PQC Hash prefix.
116      */
117     static final String PQC_HASH_PFX = "HASH-";
118 
119     /**
120      * The RSA Signer.
121      */
122     private Signature theSigner;
123 
124     /**
125      * Constructor.
126      *
127      * @param pFactory the factory
128      * @param pSpec    the signature Spec
129      */
130     JcaSignature(final GordianBaseFactory pFactory,
131                  final GordianSignatureSpec pSpec) {
132         super(pFactory, pSpec);
133     }
134 
135     /**
136      * Set the signer.
137      *
138      * @param pSigner the signer.
139      */
140     protected void setSigner(final Signature pSigner) {
141         theSigner = pSigner;
142     }
143 
144     /**
145      * Obtain the signer.
146      *
147      * @return the signer.
148      */
149     protected Signature getSigner() {
150         return theSigner;
151     }
152 
153     @Override
154     public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
155         /* Initialise detail */
156         super.initForSigning(pParams);
157         final JcaKeyPair myPair = getKeyPair();
158         final byte[] myContext = getContext();
159         JcaKeyPair.checkKeyPair(myPair);
160 
161         /* Initialise for signing */
162         try {
163             /* Determine whether we should use random for signatures */
164             final GordianCoreKeyPairType myType = GordianCoreKeyPairType.mapCoreType(getSignatureSpec().getKeyPairType());
165             final boolean useRandom = myType.useRandomForSignatures();
166 
167             /* Initialise the signing */
168             if (useRandom) {
169                 getSigner().initSign(myPair.getPrivateKey().getPrivateKey(), getRandom());
170             } else {
171                 getSigner().initSign(myPair.getPrivateKey().getPrivateKey());
172             }
173 
174             /* If we support context */
175             if (getSignatureSpec().supportsContext()) {
176                 /* Declare the context to the signer */
177                 final ContextParameterSpec mySpec = myContext == null ? null : new ContextParameterSpec(myContext);
178                 getSigner().setParameter(mySpec);
179             }
180 
181             /* Catch exceptions */
182         } catch (InvalidKeyException
183                  | InvalidAlgorithmParameterException e) {
184             throw new GordianCryptoException(SIG_ERROR, e);
185         }
186     }
187 
188     @Override
189     public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
190         /* Initialise detail */
191         super.initForVerify(pParams);
192         final JcaKeyPair myPair = getKeyPair();
193         final byte[] myContext = getContext();
194         JcaKeyPair.checkKeyPair(myPair);
195 
196         /* Initialise for signing */
197         try {
198             /* Initialise for verification */
199             getSigner().initVerify(myPair.getPublicKey().getPublicKey());
200 
201             /* If we support context */
202             if (getSignatureSpec().supportsContext()) {
203                 /* Declare the context to the signer */
204                 final ContextParameterSpec mySpec = myContext == null ? null : new ContextParameterSpec(myContext);
205                 getSigner().setParameter(mySpec);
206             }
207 
208             /* Catch exceptions */
209         } catch (InvalidKeyException
210                  | InvalidAlgorithmParameterException e) {
211             throw new GordianCryptoException(SIG_ERROR, e);
212         }
213     }
214 
215     @Override
216     public void update(final byte[] pBytes,
217                        final int pOffset,
218                        final int pLength) {
219         try {
220             theSigner.update(pBytes, pOffset, pLength);
221         } catch (SignatureException e) {
222             throw new IllegalArgumentException(e);
223         }
224     }
225 
226     @Override
227     public void update(final byte pByte) {
228         try {
229             theSigner.update(pByte);
230         } catch (SignatureException e) {
231             throw new IllegalArgumentException(e);
232         }
233     }
234 
235     @Override
236     public byte[] sign() throws GordianException {
237         /* Check that we are in signing mode */
238         checkMode(GordianSignatureMode.SIGN);
239 
240         /* Protect against exception */
241         try {
242             return getSigner().sign();
243 
244         } catch (SignatureException e) {
245             throw new GordianCryptoException(SIG_ERROR, e);
246         }
247     }
248 
249     @Override
250     public boolean verify(final byte[] pSignature) throws GordianException {
251         /* Check that we are in verify mode */
252         checkMode(GordianSignatureMode.VERIFY);
253 
254         /* Protect against exception */
255         try {
256             return getSigner().verify(pSignature);
257         } catch (SignatureException e) {
258             throw new GordianCryptoException(SIG_ERROR, e);
259         }
260     }
261 
262     @Override
263     public void reset() {
264         /* NoOp */
265     }
266 
267     @Override
268     protected JcaKeyPair getKeyPair() {
269         return (JcaKeyPair) super.getKeyPair();
270     }
271 
272     /**
273      * Obtain Signer base.
274      *
275      * @param pSignatureSpec the signatureSpec
276      * @return the base
277      */
278     static String getSignatureBase(final GordianSignatureSpec pSignatureSpec) {
279         /* Handle SM2 explicitly */
280         if (GordianKeyPairType.SM2.equals(pSignatureSpec.getKeyPairType())) {
281             return EC_SM2_ALGOBASE;
282         }
283 
284         /* Note if we are DSA */
285         final GordianCoreSignatureSpec mySpec = (GordianCoreSignatureSpec) pSignatureSpec;
286         final boolean isDSA = GordianKeyPairType.DSA.equals(pSignatureSpec.getKeyPairType());
287         final boolean isSHAKE = GordianDigestType.SHAKE.equals(mySpec.getDigestSpec().getDigestType());
288 
289         /* Switch on signature type */
290         return switch (pSignatureSpec.getSignatureType()) {
291             case PSSMGF1 -> RSA_PSSMGF1_ALGOBASE;
292             case PSS128 -> isSHAKE ? RSA_PSSSHAKE_ALGOBASE : RSA_PSS128_ALGOBASE;
293             case PSS256 -> isSHAKE ? RSA_PSSSHAKE_ALGOBASE : RSA_PSS256_ALGOBASE;
294             case X931 -> RSA_X931_ALGOBASE;
295             case ISO9796D2 -> RSA_ISO9796D2_ALGOBASE;
296             case PREHASH -> RSA_PREHASH_ALGOBASE;
297             case DSA -> isDSA
298                     ? DSA_ALGOBASE
299                     : EC_DSA_ALGOBASE;
300             case DDSA -> isDSA
301                     ? DDSA_ALGOBASE
302                     : EC_DDSA_ALGOBASE;
303             case NR -> EC_NR_ALGOBASE;
304             default -> null;
305         };
306     }
307 
308     /**
309      * Create the BouncyCastle Signature via JCA.
310      *
311      * @param pAlgorithm  the Algorithm
312      * @param postQuantum is this a postQuantum algorithm?
313      * @return the KeyPairGenerator
314      * @throws GordianException on error
315      */
316     static Signature getJavaSignature(final String pAlgorithm,
317                                       final boolean postQuantum) throws GordianException {
318         /* Protect against exceptions */
319         try {
320             /* Return a Signature for the algorithm */
321             return Signature.getInstance(pAlgorithm, postQuantum
322                     ? JcaProvider.BCPQPROV
323                     : JcaProvider.BCPROV);
324 
325             /* Catch exceptions */
326         } catch (NoSuchAlgorithmException e) {
327             /* Throw the exception */
328             throw new GordianCryptoException("Failed to create Signature", e);
329         }
330     }
331 }