View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 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  
18  package io.github.tonywasher.joceanus.gordianknot.impl.jca.encrypt;
19  
20  
21  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
22  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
23  import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestSpec;
24  import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestType;
25  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
26  import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
27  import io.github.tonywasher.joceanus.gordianknot.impl.core.encrypt.GordianCoreEncryptor;
28  import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
29  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.encrypt.GordianCoreEncryptorSpec;
30  import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.encrypt.GordianCoreSM2EncryptionSpec;
31  import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair;
32  import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair.JcaPrivateKey;
33  import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair.JcaPublicKey;
34  
35  import javax.crypto.BadPaddingException;
36  import javax.crypto.Cipher;
37  import javax.crypto.IllegalBlockSizeException;
38  import java.security.InvalidKeyException;
39  
40  /**
41   * Hybrid Encryptor.
42   */
43  public class JcaHybridEncryptor
44          extends GordianCoreEncryptor {
45      /**
46       * Error string.
47       */
48      private static final String ERROR_INIT = "Failed to initialise";
49  
50      /**
51       * The underlying encryptor.
52       */
53      private final Cipher theEncryptor;
54  
55      /**
56       * Constructor.
57       *
58       * @param pFactory the factory
59       * @param pSpec    the encryptorSpec
60       * @throws GordianException on error
61       */
62      JcaHybridEncryptor(final GordianBaseFactory pFactory,
63                         final GordianCoreEncryptorSpec pSpec) throws GordianException {
64          /* Initialise underlying cipher */
65          super(pFactory, pSpec);
66          theEncryptor = JcaEncryptor.getJavaEncryptor(getAlgorithmName(pSpec));
67      }
68  
69      @Override
70      protected JcaPublicKey getPublicKey() {
71          return (JcaPublicKey) super.getPublicKey();
72      }
73  
74      @Override
75      protected JcaPrivateKey getPrivateKey() {
76          return (JcaPrivateKey) super.getPrivateKey();
77      }
78  
79      @Override
80      public void initForEncrypt(final GordianKeyPair pKeyPair) throws GordianException {
81          try {
82              /* Initialise underlying cipher */
83              JcaKeyPair.checkKeyPair(pKeyPair);
84              super.initForEncrypt(pKeyPair);
85  
86              /* Initialise for encryption */
87              theEncryptor.init(Cipher.ENCRYPT_MODE, getPublicKey().getPublicKey(), getRandom());
88          } catch (InvalidKeyException e) {
89              throw new GordianCryptoException(ERROR_INIT, e);
90          }
91      }
92  
93      @Override
94      public void initForDecrypt(final GordianKeyPair pKeyPair) throws GordianException {
95          try {
96              /* Initialise underlying cipher */
97              JcaKeyPair.checkKeyPair(pKeyPair);
98              super.initForDecrypt(pKeyPair);
99  
100             /* Initialise for decryption */
101             theEncryptor.init(Cipher.DECRYPT_MODE, getPrivateKey().getPrivateKey());
102         } catch (InvalidKeyException e) {
103             throw new GordianCryptoException(ERROR_INIT, e);
104         }
105     }
106 
107     @Override
108     public byte[] encrypt(final byte[] pBytes) throws GordianException {
109         /* Check that we are in encryption mode */
110         checkMode(GordianEncryptMode.ENCRYPT);
111 
112         /* Encrypt the message */
113         return processData(pBytes);
114     }
115 
116     @Override
117     public byte[] decrypt(final byte[] pBytes) throws GordianException {
118         /* Check that we are in decryption mode */
119         checkMode(GordianEncryptMode.DECRYPT);
120 
121         /* Decrypt the message */
122         return processData(pBytes);
123     }
124 
125     /**
126      * Process a data buffer.
127      *
128      * @param pData the buffer to process
129      * @return the processed buffer
130      * @throws GordianException on error
131      */
132     private byte[] processData(final byte[] pData) throws GordianException {
133         try {
134             return theEncryptor.doFinal(pData, 0, pData.length);
135         } catch (IllegalBlockSizeException
136                  | BadPaddingException e) {
137             throw new GordianCryptoException("Failed to process data", e);
138         }
139     }
140 
141     /**
142      * Obtain the algorithmName.
143      *
144      * @param pSpec the Spec
145      * @return the algorithm name
146      */
147     private static String getAlgorithmName(final GordianCoreEncryptorSpec pSpec) {
148         /* Switch on encryptor type */
149         final GordianCoreSM2EncryptionSpec mySpec = pSpec.getSM2EncryptionSpec();
150         final GordianDigestSpec myDigestSpec = mySpec.getDigestSpec();
151         final GordianDigestType myDigestType = myDigestSpec.getDigestType();
152         return switch (myDigestType) {
153             case SHA2 -> "SM2withSHA" + myDigestSpec.getDigestLength();
154             case BLAKE2 -> "SM2withBlake2" + (GordianLength.LEN_512.equals(myDigestSpec.getDigestLength()) ? "b" : "s");
155             default -> "SM2with" + myDigestType;
156         };
157     }
158 }