1+ import java .security .*;
2+ import java .util .Arrays ;
3+ import java .util .Base64 ;
4+ import javax .crypto .Cipher ;
5+ import javax .crypto .KeyGenerator ;
6+ import javax .crypto .Mac ;
7+ import javax .crypto .SecretKey ;
8+ import javax .crypto .SecretKeyFactory ;
9+ import javax .crypto .spec .PBEKeySpec ;
10+ import javax .crypto .spec .SecretKeySpec ;
11+
12+
13+ class BadMacUse {
14+
15+ private byte [] generateSalt (int length ) {
16+ byte [] salt = new byte [length ];
17+ new SecureRandom ().nextBytes (salt );
18+ return salt ;
19+ }
20+
21+ public void CipherThenMac (byte [] encryptionKeyBytes , byte [] macKeyBytes ) throws Exception {
22+ // Create keys directly from provided byte arrays
23+ SecretKey encryptionKey = new SecretKeySpec (encryptionKeyBytes , "AES" );
24+ SecretKey macKey = new SecretKeySpec (macKeyBytes , "HmacSHA256" );
25+
26+ // Encrypt some sample data using the encryption key
27+ Cipher cipher = Cipher .getInstance ("AES/GCM/NoPadding" );
28+ cipher .init (Cipher .ENCRYPT_MODE , encryptionKey , new SecureRandom ());
29+ byte [] plaintext = "Further Use Test Data" .getBytes ();
30+ byte [] ciphertext = cipher .doFinal (plaintext );
31+
32+ // Compute HMAC over the ciphertext using the MAC key
33+ Mac mac = Mac .getInstance ("HmacSHA256" );
34+ mac .init (macKey );
35+ byte [] computedMac = mac .doFinal (ciphertext );
36+
37+ // Concatenate ciphertext and MAC
38+ byte [] output = new byte [ciphertext .length + computedMac .length ];
39+ System .arraycopy (ciphertext , 0 , output , 0 , ciphertext .length );
40+ System .arraycopy (computedMac , 0 , output , ciphertext .length , computedMac .length );
41+ }
42+
43+
44+ public void BadDecryptThenMacOnPlaintextVerify (byte [] encryptionKeyBytes , byte [] macKeyBytes , byte [] input ) throws Exception {
45+ // Split input into ciphertext and MAC
46+ int macLength = 32 ; // HMAC-SHA256 output length
47+ byte [] ciphertext = Arrays .copyOfRange (input , 0 , input .length - macLength );
48+ byte [] receivedMac = Arrays .copyOfRange (input , input .length - macLength , input .length );
49+
50+ // Decrypt first (unsafe)
51+ SecretKey encryptionKey = new SecretKeySpec (encryptionKeyBytes , "AES" );
52+ Cipher cipher = Cipher .getInstance ("AES/GCM/NoPadding" );
53+ cipher .init (Cipher .DECRYPT_MODE , encryptionKey , new SecureRandom ());
54+ byte [] plaintext = cipher .doFinal (ciphertext ); // $Source
55+
56+ // Now verify MAC (too late)
57+ SecretKey macKey = new SecretKeySpec (macKeyBytes , "HmacSHA256" );
58+ Mac mac = Mac .getInstance ("HmacSHA256" );
59+ mac .init (macKey );
60+ byte [] computedMac = mac .doFinal (plaintext ); // $Alert[java/quantum/bad-mac-order-decrypt-to-mac]
61+
62+ if (!MessageDigest .isEqual (receivedMac , computedMac )) {
63+ throw new SecurityException ("MAC verification failed" );
64+ }
65+ }
66+
67+ public void BadMacOnPlaintext (byte [] encryptionKeyBytes , byte [] macKeyBytes , byte [] plaintext ) throws Exception {// $Alert[java/quantum/bad-mac-order-encrypt-plaintext-also-in-mac]
68+ // Create keys directly from provided byte arrays
69+ SecretKey encryptionKey = new SecretKeySpec (encryptionKeyBytes , "AES" );
70+ SecretKey macKey = new SecretKeySpec (macKeyBytes , "HmacSHA256" );
71+
72+ // BAD Compute MAC over plaintext (not ciphertext)
73+ Mac mac = Mac .getInstance ("HmacSHA256" );
74+ mac .init (macKey );
75+ byte [] computedMac = mac .doFinal (plaintext ); // Integrity not tied to encrypted data
76+
77+ // Encrypt the plaintext
78+ Cipher cipher = Cipher .getInstance ("AES/GCM/NoPadding" );
79+ cipher .init (Cipher .ENCRYPT_MODE , encryptionKey , new SecureRandom ());
80+ byte [] ciphertext = cipher .doFinal (plaintext );
81+
82+ // Concatenate ciphertext and MAC
83+ byte [] output = new byte [ciphertext .length + computedMac .length ];
84+ System .arraycopy (ciphertext , 0 , output , 0 , ciphertext .length );
85+ System .arraycopy (computedMac , 0 , output , ciphertext .length , computedMac .length );
86+ }
87+ }
0 commit comments