Repository navigation
Expand file tree
/
Copy pathMACOperation.java
More file actions
249 lines (231 loc) · 9.72 KB
/
Copy pathMACOperation.java
File metadata and controls
249 lines (231 loc) · 9.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* MACOperation demonstrates various Message Authentication Code (MAC)
* operations and further use of MAC outputs as inputs into higher-level
* cryptosystems.
*
* Flows include:
*
* 1. Secure HMAC-SHA2 (HMAC-SHA256) - a widely accepted MAC. 2. Secure
* HMAC-SHA3 (HMAC-SHA3-256) - an alternative using the SHA-3 family. 3. Secure
* Poly1305 MAC - using BouncyCastle's implementation. 4. Secure GMAC - using
* AES-GCM's authentication tag in a dedicated MAC mode. 5. Secure KMAC - using
* KMAC128 (from the SHA-3 family).
*
* Insecure examples include:
*
* 6. Insecure HMAC-SHA1 - which is deprecated.
*
* Further flows:
*
* A. processMACOutput: Uses the MAC output directly as key material for AES
* encryption. (Note: This is acceptable only if the MAC is produced by a secure
* function.)
*
* B. alternativeMACFlow: Uses the MAC output as an identifier that is then
* encrypted.
*
* C. furtherUseMACForKeyDerivation: Uses PBKDF2 to split a MAC output into two
* keys, one for encryption and one for MACing ciphertext.
*
* SAST/CBOM Notes: - Secure MAC algorithms (HMAC-SHA256, HMAC-SHA3-256,
* Poly1305, GMAC, KMAC128) are acceptable if used correctly. - HMAC-SHA1 is
* flagged as insecure. - Using a raw MAC output directly as key material is
* ambiguous unless the MAC is produced by a secure KDF.
*/
public class MACOperation {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
// ---------- MAC Operations ----------
/**
* Secure MAC using HMAC-SHA256. SAST: HMAC-SHA256 is widely considered
* secure.
*/
public byte[] secureHMACSHA256(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using HMAC-SHA3-256. SAST: HMAC-SHA3 is a modern alternative
* from the SHA-3 family.
*/
public byte[] secureHMACSHA3(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA3-256", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA3-256");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using Poly1305. SAST: Poly1305 is secure when used with a
* one-time key from a cipher (e.g. ChaCha20).
*/
public byte[] securePoly1305(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("Poly1305", "BC");
SecretKey secretKey = new SecretKeySpec(key, "Poly1305");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using GMAC. SAST: GMAC (the MAC part of AES-GCM) is secure
* when used correctly.
*/
public byte[] secureGMAC(String message, byte[] key) throws Exception {
// For GMAC, we use the GMac algorithm as provided by BC.
Mac mac = Mac.getInstance("GMac", "BC");
// Initialize the key for GMAC; key should be appropriate for the underlying
// block cipher.
SecretKey secretKey = new SecretKeySpec(key, "AES");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using KMAC128. SAST: KMAC128 is part of the SHA-3 family and
* is secure when used properly.
*/
public byte[] secureKMAC(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("KMAC128", "BC");
SecretKey secretKey = new SecretKeySpec(key, "KMAC128");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Insecure MAC using HMAC-SHA1. SAST: HMAC-SHA1 is considered deprecated
* and weak.
*/
public byte[] insecureHMACSHA1(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA1");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
// ---------- Further Use of MAC Outputs ----------
/**
* Processes the MAC output by using it as key material for AES encryption.
* SAST: Using a raw MAC output as key material is acceptable only if the
* MAC was produced by a secure function; otherwise, this is ambiguous.
*
* @param macOutput The computed MAC output.
* @throws Exception if encryption fails.
*/
public void processMACOutput(byte[] macOutput) throws Exception {
// Derive a 128-bit AES key from the MAC output.
SecretKey key = new SecretKeySpec(macOutput, 0, 16, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
storeEncryptedMAC(encryptedData);
}
/**
* Alternative flow: Uses the MAC output as an identifier and then encrypts
* it. SAST: Using a MAC as an identifier is common; subsequent encryption
* must be secure.
*
* @param macOutput The computed MAC output.
* @throws Exception if encryption fails.
*/
public void alternativeMACFlow(byte[] macOutput) throws Exception {
byte[] identifier = Base64.getEncoder().encode(macOutput);
encryptAndSend(identifier);
}
/**
* Further use: Derives two separate keys from the MAC output using PBKDF2,
* then uses one key for encryption and one for computing an additional MAC
* over the ciphertext.
*
* SAST: This key-splitting technique is acceptable if PBKDF2 is used
* securely.
*
* @param macOutput The MAC output to derive keys from.
* @throws Exception if key derivation or encryption fails.
*/
public void furtherUseMACForKeyDerivation(byte[] macOutput) throws Exception {
// Use the Base64 representation of the MAC as the password input to PBKDF2.
String macAsPassword = Base64.getEncoder().encodeToString(macOutput);
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(macAsPassword.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] keyMaterial = factory.generateSecret(spec).getEncoded();
// Split into two 128-bit keys.
byte[] encryptionKeyBytes = Arrays.copyOfRange(keyMaterial, 0, 16);
byte[] macKeyBytes = Arrays.copyOfRange(keyMaterial, 16, 32);
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
SecretKey derivedMacKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
// Encrypt some sample data using the derived encryption key.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new SecureRandom());
byte[] ciphertext = cipher.doFinal("Further Use Test Data".getBytes());
// Compute HMAC over the ciphertext using the derived MAC key.
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(derivedMacKey);
byte[] computedMac = mac.doFinal(ciphertext);
// Concatenate the ciphertext and MAC for further use.
byte[] output = new byte[ciphertext.length + computedMac.length];
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
storeEncryptedMAC(output);
}
// ---------- Output/Storage Methods ----------
/**
* Simulates secure storage or transmission of an encrypted MAC output.
* SAST: In production, storage and transmission must be protected.
*
* @param encryptedMAC The encrypted MAC output.
*/
public void storeEncryptedMAC(byte[] encryptedMAC) {
String stored = Base64.getEncoder().encodeToString(encryptedMAC);
// In production, this string would be securely stored or transmitted.
}
/**
* Encrypts data using AES-GCM and simulates secure transmission. SAST: Uses
* a securely generated AES key.
*
* @param data The data to encrypt.
* @throws Exception if encryption fails.
*/
public void encryptAndSend(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKey key = generateAESKey();
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal(data);
storeEncryptedMAC(encryptedData);
}
// ---------- Helper Methods ----------
/**
* Generates a secure 256-bit AES key. SAST: Uses a strong RNG for key
* generation.
*
* @return A SecretKey for AES.
* @throws NoSuchAlgorithmException if AES is unsupported.
*/
private SecretKey generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return keyGen.generateKey();
}
/**
* Generates a random salt of the specified length using SecureRandom. SAST:
* Salting is essential for secure key derivation.
*
* @param length The salt length.
* @return A byte array representing the salt.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}