Skip to content

Commit 08889ab

Browse files
authored
Merge pull request #164 from cconlon/secretKey
AES and DESede SecretKeyFactory and SecretKey support
2 parents 87c7bfb + 1a29a06 commit 08889ab

File tree

7 files changed

+1194
-8
lines changed

7 files changed

+1194
-8
lines changed

scripts/infer.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ infer --fail-on-issue run -- javac \
100100
src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java \
101101
src/main/java/com/wolfssl/provider/jce/WolfCryptPssParameters.java \
102102
src/main/java/com/wolfssl/provider/jce/WolfCryptRandom.java \
103+
src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKey.java \
103104
src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java \
104105
src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java \
105106
src/main/java/com/wolfssl/provider/jce/WolfCryptUtil.java \

src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,14 @@ private void registerServices() {
484484
"com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcPBKDF2WithHmacSHA3_512");
485485
}
486486
}
487+
if (FeatureDetect.AesEnabled()) {
488+
put("SecretKeyFactory.AES",
489+
"com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcAES");
490+
}
491+
if (FeatureDetect.Des3Enabled() && !Fips.enabled) {
492+
put("SecretKeyFactory.DESede",
493+
"com.wolfssl.provider.jce.WolfCryptSecretKeyFactory$wcDESede");
494+
}
487495

488496
/* KeyFactory */
489497
if (FeatureDetect.EccEnabled()) {
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/* WolfCryptSecretKey.java
2+
*
3+
* Copyright (C) 2006-2025 wolfSSL Inc.
4+
*
5+
* This file is part of wolfSSL.
6+
*
7+
* wolfSSL is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 2 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfSSL is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*/
21+
22+
package com.wolfssl.provider.jce;
23+
24+
import java.util.Arrays;
25+
import java.util.Objects;
26+
import java.security.InvalidKeyException;
27+
import java.security.MessageDigest;
28+
import javax.crypto.SecretKey;
29+
30+
import com.wolfssl.wolfcrypt.Aes;
31+
import com.wolfssl.wolfcrypt.Des3;
32+
33+
/**
34+
* wolfCrypt SecretKey implementation for symmetric algorithms.
35+
*
36+
* Supports AES and 3DES/DESede algorithms with key size validation.
37+
*/
38+
public class WolfCryptSecretKey implements SecretKey {
39+
40+
private static final long serialVersionUID = 1L;
41+
42+
/** Encoded key byte array */
43+
private byte[] encoded = null;
44+
45+
/** Key algorithm name */
46+
private String algorithm = null;
47+
48+
/** Has object been destroyed or not */
49+
private boolean destroyed = false;
50+
51+
/**
52+
* Create new WolfCryptSecretKey object.
53+
*
54+
* @param algorithm key algorithm name ("AES", "DESede")
55+
* @param encoded encoded key byte array
56+
*
57+
* @throws InvalidKeyException if algorithm is null/empty,
58+
* encoded is null/zero length, or key size is invalid
59+
*/
60+
public WolfCryptSecretKey(String algorithm, byte[] encoded)
61+
throws InvalidKeyException {
62+
63+
if (algorithm == null || algorithm.isEmpty()) {
64+
throw new InvalidKeyException(
65+
"Algorithm String cannot be null or empty");
66+
}
67+
68+
if (encoded == null || encoded.length == 0) {
69+
throw new InvalidKeyException(
70+
"Encoded key cannot be null or zero length");
71+
}
72+
73+
/* Validate key size based on algorithm. Sanitize matching wolfCrypt
74+
* aes.c and des3.c input sizes. */
75+
if (algorithm.equalsIgnoreCase("AES")) {
76+
if (encoded.length != Aes.KEY_SIZE_128 &&
77+
encoded.length != Aes.KEY_SIZE_192 &&
78+
encoded.length != Aes.KEY_SIZE_256) {
79+
throw new InvalidKeyException(
80+
"AES key must be 16, 24, or 32 bytes, got: " +
81+
encoded.length);
82+
}
83+
}
84+
else if (algorithm.equalsIgnoreCase("DESede") ||
85+
algorithm.equalsIgnoreCase("TripleDES")) {
86+
if (encoded.length != Des3.KEY_SIZE) {
87+
throw new InvalidKeyException(
88+
"DESede key must be 24 bytes, got: " + encoded.length);
89+
}
90+
}
91+
else {
92+
throw new InvalidKeyException(
93+
"Unsupported algorithm: " + algorithm);
94+
}
95+
96+
this.algorithm = algorithm;
97+
this.encoded = encoded.clone();
98+
}
99+
100+
/**
101+
* Check if this object has been destroyed with destroy().
102+
* Must be called from synchronized context.
103+
*
104+
* @throws IllegalStateException if object has been destroyed
105+
*/
106+
private void checkDestroyed()
107+
throws IllegalStateException {
108+
109+
if (this.destroyed) {
110+
throw new IllegalStateException(
111+
"SecretKey has been destroyed");
112+
}
113+
}
114+
115+
/**
116+
* Return algorithm String representing this SecretKey.
117+
*
118+
* @return algorithm string matching this object
119+
*
120+
* @throws IllegalStateException if object has been destroyed
121+
*/
122+
@Override
123+
public synchronized String getAlgorithm() {
124+
125+
checkDestroyed();
126+
127+
return this.algorithm;
128+
}
129+
130+
/**
131+
* Return encoding format for this SecretKey.
132+
*
133+
* @return encoding format string, will be "RAW" for this object
134+
*
135+
* @throws IllegalStateException if object has been destroyed
136+
*/
137+
@Override
138+
public synchronized String getFormat() {
139+
140+
checkDestroyed();
141+
142+
return "RAW";
143+
}
144+
145+
/**
146+
* Return encoded byte array of this SecretKey.
147+
*
148+
* @return encoded byte array
149+
*
150+
* @throws IllegalStateException if object has been destroyed
151+
*/
152+
@Override
153+
public synchronized byte[] getEncoded() {
154+
155+
checkDestroyed();
156+
157+
return this.encoded.clone();
158+
}
159+
160+
/**
161+
* Destroy this object.
162+
*
163+
* Zeroize key bytes contained in this object and mark it as unusable.
164+
*/
165+
@Override
166+
public synchronized void destroy() {
167+
168+
if (this.encoded != null) {
169+
Arrays.fill(this.encoded, (byte)0);
170+
this.encoded = null;
171+
}
172+
173+
this.algorithm = null;
174+
this.destroyed = true;
175+
}
176+
177+
/**
178+
* Return if this object has been destroyed.
179+
*
180+
* Object can be destroyed by calling destroy(), which will zeroize
181+
* internal buffers for this object.
182+
*
183+
* @return true if object has been destroyed, otherwise false
184+
*/
185+
@Override
186+
public synchronized boolean isDestroyed() {
187+
return this.destroyed;
188+
}
189+
190+
@Override
191+
public synchronized int hashCode() {
192+
checkDestroyed();
193+
return Arrays.hashCode(encoded);
194+
}
195+
196+
@Override
197+
public synchronized boolean equals(Object obj) {
198+
199+
byte[] sKeyEncoded = null;
200+
byte[] thisEncoded = null;
201+
SecretKey sKey;
202+
203+
checkDestroyed();
204+
205+
if (obj == this) {
206+
return true;
207+
}
208+
209+
if (!(obj instanceof SecretKey)) {
210+
return false;
211+
}
212+
sKey = (SecretKey)obj;
213+
214+
try {
215+
sKeyEncoded = sKey.getEncoded();
216+
thisEncoded = getEncoded();
217+
218+
if (sKeyEncoded == null || thisEncoded == null) {
219+
return false;
220+
}
221+
222+
/* MessageDigest.isEqual() for constant-time comparison */
223+
if (!MessageDigest.isEqual(sKeyEncoded, thisEncoded)) {
224+
return false;
225+
}
226+
227+
if (!Objects.equals(sKey.getAlgorithm(), getAlgorithm())) {
228+
return false;
229+
}
230+
231+
if (!Objects.equals(sKey.getFormat(), getFormat())) {
232+
return false;
233+
}
234+
235+
return true;
236+
237+
} catch (Exception e) {
238+
/* If encoding fails for either key, cannot be equal */
239+
return false;
240+
241+
} finally {
242+
if (sKeyEncoded != null) {
243+
Arrays.fill(sKeyEncoded, (byte)0);
244+
}
245+
if (thisEncoded != null) {
246+
Arrays.fill(thisEncoded, (byte)0);
247+
}
248+
}
249+
}
250+
}
251+

0 commit comments

Comments
 (0)