· 6 years ago · Jun 27, 2019, 05:18 AM
1package arp;
2
3import java.nio.ByteBuffer;
4import java.nio.charset.Charset;
5import java.security.Key;
6import java.security.SecureRandom;
7import java.security.spec.KeySpec;
8import java.util.Arrays;
9
10import javax.crypto.Cipher;
11import javax.crypto.SecretKeyFactory;
12import javax.crypto.spec.IvParameterSpec;
13import javax.crypto.spec.PBEKeySpec;
14import javax.crypto.spec.PBEParameterSpec;
15import javax.xml.bind.DatatypeConverter;
16
17public class Solution {
18
19 private static final String ALGORITHM = "PBEWITHHMACSHA512ANDAES_128";
20 private static final Charset charset = Charset.forName("utf-8");
21 private static final int ITERATION_COUNT = 100;
22
23 private static byte[] getIvAndMac(String message, String key, byte[] salt) {
24 try {
25 KeySpec spec = new PBEKeySpec(key.toCharArray(), salt, ITERATION_COUNT);
26 // salt is also used as IV
27 IvParameterSpec ivSpec = new IvParameterSpec(salt);
28 PBEParameterSpec pbSpec = new PBEParameterSpec(salt, ITERATION_COUNT, ivSpec);
29 Key secretKey = SecretKeyFactory.getInstance(ALGORITHM).generateSecret(spec);
30 Cipher eCipher = Cipher.getInstance(ALGORITHM);
31 eCipher.init(Cipher.ENCRYPT_MODE, secretKey, pbSpec);
32
33 byte[] mac = eCipher.doFinal(message.getBytes(charset));
34 ByteBuffer bb = ByteBuffer.allocate(salt.length + mac.length);
35 bb.put(salt);
36 bb.put(mac);
37
38 return bb.array();
39 }
40 catch(Exception e) {
41 throw new RuntimeException(e);
42 }
43 }
44
45 // Add a message autentication code to the message using a specific key.
46 public static String addMac(String message, String key, int messagelength) {
47 // here the messagelength property is not used at all, not sure why it is even passed when the whole message needs to be MACed
48
49 // salt is to keep the generated MAC unique even when the same message is passed as input
50 // instead of using a counter which requires storing the messages for future reference to detect duplicates
51 byte[] salt = new byte[16];
52 SecureRandom random = new SecureRandom();
53 random.nextBytes(salt);
54
55 byte[] ivAndMac = getIvAndMac(message, key, salt);
56
57 return message + DatatypeConverter.printHexBinary(ivAndMac);
58 }
59
60 // Verify a message authentication code to the message given a specific key.
61 public static boolean checkMac(String message, String key, int messagelength) {
62 if(message == null) {
63 return false;
64 }
65
66 if(messagelength > message.length()) {
67 return false;
68 }
69
70 String plainText = message.substring(0, messagelength);
71 String macHex = message.substring(messagelength);
72 byte[] ivAndMac = DatatypeConverter.parseHexBinary(macHex);
73 if(ivAndMac.length < 20) { // salt itself is 16 bytes so the whole array should be much more than just 16 bytes
74 return false;
75 }
76
77 byte[] salt = new byte[16];
78 System.arraycopy(ivAndMac, 0, salt, 0, 16);
79
80 byte[] reComputedIvAndMac = getIvAndMac(plainText, key, salt);
81
82 return Arrays.equals(ivAndMac, reComputedIvAndMac);
83 }
84
85 public static void main(String[] args) {
86 String message = "this is a secret message";
87 String key = "and this is my 32 character secret key";
88
89 String authenticatedMessage = addMac(message, key, message.length());
90 //System.out.println(authenticatedMessage);
91 boolean verified = checkMac(authenticatedMessage, key, message.length());
92 //System.out.println(verified);
93 if(!verified) {
94 System.out.println("corrupted message, MAC check failed");
95 }
96
97 String anotherAuthMessageWithSameMessage = addMac(message, key, message.length());
98 if(authenticatedMessage.equals(anotherAuthMessageWithSameMessage)) {
99 System.out.println("program failed to generate unique MAC for the same input");
100 }
101
102 // check MAC by altering the message
103 verified = checkMac("abc" + anotherAuthMessageWithSameMessage, key, anotherAuthMessageWithSameMessage.length() + 3);
104 if(verified) {
105 System.out.println("MAC check should fail");
106 }
107 }
108}