Skip to content

Commit 66e9d76

Browse files
committed
Crypto: Add jca unit tests.
1 parent f4fea6d commit 66e9d76

29 files changed

+10556
-0
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package com.example.crypto.algorithms;
2+
3+
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
4+
5+
import java.security.*;
6+
import javax.crypto.Cipher;
7+
import javax.crypto.KeyGenerator;
8+
import javax.crypto.SecretKey;
9+
import javax.crypto.SecretKeyFactory;
10+
import javax.crypto.spec.IvParameterSpec;
11+
import javax.crypto.spec.PBEKeySpec;
12+
import javax.crypto.spec.SecretKeySpec;
13+
import java.security.SecureRandom;
14+
import java.util.Arrays;
15+
import java.util.Base64;
16+
17+
/**
18+
* AesWrapAndPBEWithTest demonstrates key wrapping and password-based encryption
19+
* using various transformations.
20+
*
21+
* This file includes:
22+
*
23+
* 1. AESWrap Examples:
24+
* - secureAESWrap(): Uses a randomly generated wrapping key.
25+
* - insecureAESWrap(): Uses a fixed, hard-coded wrapping key.
26+
*
27+
* 2. PBEWith Examples:
28+
* - insecurePBEExample(): Uses the legacy PBEWithMD5AndDES.
29+
* - securePBEExample(): Uses PBKDF2WithHmacSHA256.
30+
* - additionalPBEExample(): Uses PBEWithSHA256And128BitAES-CBC-BC.
31+
* - additionalPBEExample2(): Uses PBEWithSHA1And128BitAES-CBC-BC.
32+
*
33+
* 3. Dynamic PBE Encryption:
34+
* - dynamicPBEEncryption(): Chooses the PBE transformation based on a
35+
* configuration string.
36+
*
37+
* Best Practices:
38+
* - Use secure random keys and salts.
39+
* - Avoid legacy algorithms like PBEWithMD5AndDES.
40+
* - Prefer modern KDFs (PBKDF2WithHmacSHA256) and secure provider-specific PBE
41+
* transformations.
42+
*
43+
* SAST/CBOM Notes:
44+
* - Insecure examples (PBEWithMD5AndDES, fixed keys) should be flagged.
45+
* - Secure examples use random salt, high iteration counts, and strong
46+
* algorithms.
47+
*/
48+
public class AesWrapAndPBEWith {
49+
50+
// static {
51+
// // Register BouncyCastle as a provider.
52+
// Security.addProvider(new BouncyCastleProvider());
53+
// }
54+
55+
// ===========================
56+
// 1. AESWrap Examples
57+
// ===========================
58+
59+
/**
60+
* Secure AES key wrapping.
61+
* Generates a random 256-bit wrapping key to wrap a target AES key.
62+
*
63+
* @return The wrapped key (Base64-encoded).
64+
* @throws Exception if an error occurs.
65+
*/
66+
public String secureAESWrap() throws Exception {
67+
KeyGenerator kg = KeyGenerator.getInstance("AES");
68+
kg.init(256, new SecureRandom());
69+
SecretKey wrappingKey = kg.generateKey();
70+
71+
kg.init(128, new SecureRandom());
72+
SecretKey targetKey = kg.generateKey();
73+
74+
Cipher cipher = Cipher.getInstance("AESWrap");
75+
cipher.init(Cipher.WRAP_MODE, wrappingKey);
76+
byte[] wrappedKey = cipher.wrap(targetKey);
77+
78+
return Base64.getEncoder().encodeToString(wrappedKey);
79+
}
80+
81+
/**
82+
* Insecure AES key wrapping.
83+
* Uses a fixed (hard-coded) wrapping key.
84+
*
85+
* @return The wrapped key (Base64-encoded).
86+
* @throws Exception if an error occurs.
87+
*/
88+
public String insecureAESWrap() throws Exception {
89+
byte[] fixedKeyBytes = new byte[32];
90+
Arrays.fill(fixedKeyBytes, (byte) 0x01);
91+
SecretKey wrappingKey = new SecretKeySpec(fixedKeyBytes, "AES");
92+
93+
KeyGenerator kg = KeyGenerator.getInstance("AES");
94+
kg.init(128, new SecureRandom());
95+
SecretKey targetKey = kg.generateKey();
96+
97+
Cipher cipher = Cipher.getInstance("AESWrap");
98+
cipher.init(Cipher.WRAP_MODE, wrappingKey);
99+
byte[] wrappedKey = cipher.wrap(targetKey);
100+
101+
return Base64.getEncoder().encodeToString(wrappedKey);
102+
}
103+
104+
// ===========================
105+
// 2. PBEWith Examples
106+
// ===========================
107+
108+
/**
109+
* Insecure PBE example using PBEWithMD5AndDES.
110+
*
111+
* @param password The input password.
112+
* @return The derived key (Base64-encoded).
113+
* @throws Exception if key derivation fails.
114+
*/
115+
public String insecurePBEExample(String password) throws Exception {
116+
byte[] salt = new byte[8];
117+
Arrays.fill(salt, (byte) 0x00); // Fixed salt (insecure)
118+
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 64);
119+
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
120+
byte[] keyBytes = factory.generateSecret(spec).getEncoded();
121+
return Base64.getEncoder().encodeToString(keyBytes);
122+
}
123+
124+
/**
125+
* Secure PBE example using PBKDF2WithHmacSHA256.
126+
*
127+
* @param password The input password.
128+
* @return The derived 256-bit AES key (Base64-encoded).
129+
* @throws Exception if key derivation fails.
130+
*/
131+
public String securePBEExample(String password) throws Exception {
132+
byte[] salt = new byte[16];
133+
new SecureRandom().nextBytes(salt);
134+
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
135+
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
136+
byte[] keyBytes = factory.generateSecret(spec).getEncoded();
137+
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
138+
return Base64.getEncoder().encodeToString(aesKey.getEncoded());
139+
}
140+
141+
/**
142+
* Additional PBE example using PBEWithSHA256And128BitAES-CBC-BC.
143+
*
144+
* @param password The input password.
145+
* @param plaintext The plaintext to encrypt.
146+
* @return The IV concatenated with ciphertext (Base64-encoded).
147+
* @throws Exception if key derivation or encryption fails.
148+
*/
149+
public String additionalPBEExample(String password, String plaintext) throws Exception {
150+
byte[] salt = new byte[16];
151+
new SecureRandom().nextBytes(salt);
152+
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 128);
153+
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithSHA256And128BitAES-CBC-BC");
154+
SecretKey pbeKey = factory.generateSecret(spec);
155+
SecretKey aesKey = new SecretKeySpec(pbeKey.getEncoded(), "AES");
156+
157+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
158+
byte[] iv = new byte[16];
159+
new SecureRandom().nextBytes(iv);
160+
IvParameterSpec ivSpec = new IvParameterSpec(iv);
161+
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
162+
byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
163+
byte[] output = concatenate(iv, ciphertext);
164+
return Base64.getEncoder().encodeToString(output);
165+
}
166+
167+
/**
168+
* Additional PBE example using PBEWithSHA1And128BitAES-CBC-BC.
169+
* This is less preferred than PBKDF2WithHmacSHA256 but demonstrates another
170+
* variant.
171+
*
172+
* @param password The input password.
173+
* @param plaintext The plaintext to encrypt.
174+
* @return The IV concatenated with ciphertext (Base64-encoded).
175+
* @throws Exception if key derivation or encryption fails.
176+
*/
177+
public String additionalPBEExample2(String password, String plaintext) throws Exception {
178+
byte[] salt = new byte[16];
179+
new SecureRandom().nextBytes(salt);
180+
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 128);
181+
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithSHA1And128BitAES-CBC-BC");
182+
SecretKey pbeKey = factory.generateSecret(spec);
183+
SecretKey aesKey = new SecretKeySpec(pbeKey.getEncoded(), "AES");
184+
185+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
186+
byte[] iv = new byte[16];
187+
new SecureRandom().nextBytes(iv);
188+
IvParameterSpec ivSpec = new IvParameterSpec(iv);
189+
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
190+
byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
191+
byte[] output = concatenate(iv, ciphertext);
192+
return Base64.getEncoder().encodeToString(output);
193+
}
194+
195+
// ===========================
196+
// 3. Dynamic PBE Encryption
197+
// ===========================
198+
199+
/**
200+
* Dynamically selects a PBE transformation based on a configuration string.
201+
*
202+
* Acceptable values:
203+
* - "PBKDF2": Uses PBKDF2WithHmacSHA256.
204+
* - "SHA256AES": Uses PBEWithSHA256And128BitAES-CBC-BC.
205+
* - "SHA1AES": Uses PBEWithSHA1And128BitAES-CBC-BC.
206+
* - Otherwise, falls back to insecure PBEWithMD5AndDES.
207+
*
208+
* @param config The configuration string.
209+
* @param password The input password.
210+
* @param plaintext The plaintext to encrypt.
211+
* @return The Base64-encoded encrypted output.
212+
* @throws Exception if an error occurs.
213+
*/
214+
public String dynamicPBEEncryption(String config, String password, String plaintext) throws Exception {
215+
if ("PBKDF2".equalsIgnoreCase(config)) {
216+
return securePBEExample(password);
217+
} else if ("SHA256AES".equalsIgnoreCase(config)) {
218+
return additionalPBEExample(password, plaintext);
219+
} else if ("SHA1AES".equalsIgnoreCase(config)) {
220+
return additionalPBEExample2(password, plaintext);
221+
} else {
222+
// Fallback insecure option.
223+
return insecurePBEExample(password);
224+
}
225+
}
226+
227+
// ===========================
228+
// Helper Methods
229+
// ===========================
230+
231+
/**
232+
* Concatenates two byte arrays.
233+
*/
234+
private byte[] concatenate(byte[] a, byte[] b) {
235+
byte[] result = new byte[a.length + b.length];
236+
System.arraycopy(a, 0, result, 0, a.length);
237+
System.arraycopy(b, 0, result, a.length, b.length);
238+
return result;
239+
}
240+
241+
}

0 commit comments

Comments
 (0)