1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package io.github.tonywasher.joceanus.gordianknot.impl.jca.encrypt;
19
20 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
21 import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
22 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
23 import io.github.tonywasher.joceanus.gordianknot.impl.core.encrypt.GordianCoreEncryptor;
24 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
25 import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.encrypt.GordianCoreEncryptorSpec;
26 import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair;
27 import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair.JcaPrivateKey;
28 import io.github.tonywasher.joceanus.gordianknot.impl.jca.keypair.JcaKeyPair.JcaPublicKey;
29
30 import javax.crypto.BadPaddingException;
31 import javax.crypto.Cipher;
32 import javax.crypto.IllegalBlockSizeException;
33 import java.security.InvalidKeyException;
34 import java.util.Arrays;
35
36
37
38
39 public class JcaBlockEncryptor
40 extends GordianCoreEncryptor {
41
42
43
44 private static final String ERROR_INIT = "Failed to initialise";
45
46
47
48
49 private final Cipher theEncryptor;
50
51
52
53
54
55
56
57
58 JcaBlockEncryptor(final GordianBaseFactory pFactory,
59 final GordianCoreEncryptorSpec pSpec) throws GordianException {
60
61 super(pFactory, pSpec);
62 theEncryptor = JcaEncryptor.getJavaEncryptor(getAlgorithmName(pSpec));
63 }
64
65 @Override
66 protected JcaPublicKey getPublicKey() {
67 return (JcaPublicKey) super.getPublicKey();
68 }
69
70 @Override
71 protected JcaPrivateKey getPrivateKey() {
72 return (JcaPrivateKey) super.getPrivateKey();
73 }
74
75 @Override
76 public void initForEncrypt(final GordianKeyPair pKeyPair) throws GordianException {
77 try {
78
79 JcaKeyPair.checkKeyPair(pKeyPair);
80 super.initForEncrypt(pKeyPair);
81
82
83 theEncryptor.init(Cipher.ENCRYPT_MODE, getPublicKey().getPublicKey(), getRandom());
84 } catch (InvalidKeyException e) {
85 throw new GordianCryptoException(ERROR_INIT, e);
86 }
87 }
88
89 @Override
90 public void initForDecrypt(final GordianKeyPair pKeyPair) throws GordianException {
91 try {
92
93 JcaKeyPair.checkKeyPair(pKeyPair);
94 super.initForDecrypt(pKeyPair);
95
96
97 theEncryptor.init(Cipher.DECRYPT_MODE, getPrivateKey().getPrivateKey());
98 } catch (InvalidKeyException e) {
99 throw new GordianCryptoException(ERROR_INIT, e);
100 }
101 }
102
103 @Override
104 public byte[] encrypt(final byte[] pBytes) throws GordianException {
105
106 checkMode(GordianEncryptMode.ENCRYPT);
107
108
109 return processData(pBytes);
110 }
111
112 @Override
113 public byte[] decrypt(final byte[] pBytes) throws GordianException {
114
115 checkMode(GordianEncryptMode.DECRYPT);
116
117
118 return processData(pBytes);
119 }
120
121
122
123
124
125
126
127
128 private byte[] processData(final byte[] pData) throws GordianException {
129 try {
130
131 int myInLen = pData.length;
132 final int myInBlockLength = theEncryptor.getBlockSize();
133 final int myNumBlocks = getNumBlocks(myInLen, myInBlockLength);
134 final int myOutBlockLength = theEncryptor.getOutputSize(myInBlockLength);
135
136
137 final byte[] myOutput = new byte[myOutBlockLength * myNumBlocks];
138
139
140 int myInOff = 0;
141 int myOutOff = 0;
142 while (myInLen > 0) {
143
144 final int myLen = Math.min(myInLen, myInBlockLength);
145 final byte[] myBlock = theEncryptor.doFinal(pData, myInOff, myLen);
146
147
148 final int myOutLen = myBlock.length;
149 System.arraycopy(myBlock, 0, myOutput, myOutOff, myOutLen);
150 myOutOff += myOutLen;
151
152
153 myInOff += myInBlockLength;
154 myInLen -= myInBlockLength;
155 }
156
157
158 if (myOutOff == myOutput.length) {
159 return myOutput;
160 }
161
162
163 final byte[] myReturn = Arrays.copyOf(myOutput, myOutOff);
164 Arrays.fill(myOutput, (byte) 0);
165 return myReturn;
166
167 } catch (IllegalBlockSizeException
168 | BadPaddingException e) {
169 throw new GordianCryptoException("Failed to process data", e);
170 }
171 }
172
173
174
175
176
177
178
179
180 private static int getNumBlocks(final int pLength, final int pBlockLength) {
181 return (pLength + pBlockLength - 1) / pBlockLength;
182 }
183
184
185
186
187
188
189
190 private static String getAlgorithmName(final GordianCoreEncryptorSpec pSpec) {
191
192 final String myBase = pSpec.getKeyPairType().name();
193
194
195 return switch (pSpec.getDigestSpec().getDigestLength()) {
196 case LEN_224 -> myBase + "/ECB/OAEPWITHSHA224ANDMGF1PADDING";
197 case LEN_256 -> myBase + "/ECB/OAEPWITHSHA256ANDMGF1PADDING";
198 case LEN_384 -> myBase + "/ECB/OAEPWITHSHA384ANDMGF1PADDING";
199 default -> myBase + "/ECB/OAEPWITHSHA512ANDMGF1PADDING";
200 };
201 }
202 }