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
|
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* @test
* @bug 8041787 8288050
* @summary Check if doFinal and update operation result in same PBMac
* @author Alexander Fomin
* @run main PBMacDoFinalVsUpdate
*/
public class PBMacDoFinalVsUpdate {
public static void main(String[] args) {
String[] PBMAC1Algorithms = {
"HmacPBESHA1",
"PBEWithHmacSHA1",
"PBEWithHmacSHA224",
"PBEWithHmacSHA256",
"PBEWithHmacSHA384",
"PBEWithHmacSHA512",
"PBEWithHmacSHA512/224",
"PBEWithHmacSHA512/256",
};
String[] PBKDF2Algorithms = {
"PBKDF2WithHmacSHA1",
"PBKDF2WithHmacSHA224",
"PBKDF2WithHmacSHA256",
"PBKDF2WithHmacSHA384",
"PBKDF2WithHmacSHA512",
"PBKDF2WithHmacSHA512/224",
"PBKDF2WithHmacSHA512/256",
};
PBMacDoFinalVsUpdate testRunner = new PBMacDoFinalVsUpdate();
boolean failed = false;
for (String thePBMacAlgo : PBMAC1Algorithms) {
for (String thePBKDF2Algo : PBKDF2Algorithms) {
System.out.println("Running test with " + thePBMacAlgo
+ " and " + thePBKDF2Algo + ":");
try {
if (!testRunner.doTest(thePBMacAlgo, thePBKDF2Algo)) {
failed = true;
}
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidKeySpecException e) {
failed = true;
e.printStackTrace(System.out);
System.out.println("Test FAILED.");
}
}
}
if (failed) {
throw new RuntimeException("One or more tests failed....");
}
}
/**
* Uses a random generator to initialize a message, instantiate a Mac object
* according to the given PBMAC1 algorithm, initialize the object with a
* SecretKey derived using PBKDF2 algorithm (see PKCS #5 v21, chapter 7.1),
* feed the message into the Mac object all at once and get the output MAC
* as result1. Reset the Mac object, chop the message into three pieces,
* feed into the Mac object sequentially, and get the output MAC as result2.
* Finally, compare result1 and result2 and see if they are the same.
*
* @param theMacAlgo PBMAC algorithm to test
* @param thePBKDF2Algo PBKDF2 algorithm to test
* @return true - the test is passed; false - otherwise.
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws InvalidKeySpecException
*/
protected boolean doTest(String theMacAlgo, String thePBKDF2Algo)
throws NoSuchAlgorithmException, InvalidKeyException,
InvalidKeySpecException {
int OFFSET = 5;
// Some message for which a MAC result will be calculated
byte[] plain = new byte[25];
new SecureRandom().nextBytes(plain);
// Form tail - is one of the three pieces
byte[] tail = new byte[plain.length - OFFSET];
System.arraycopy(plain, OFFSET, tail, 0, tail.length);
// Obtain a SecretKey using PBKDF2
SecretKey key = getSecretKey(thePBKDF2Algo);
// Instantiate Mac object and init it with a SecretKey and calc result1
Mac theMac = Mac.getInstance(theMacAlgo);
theMac.init(key);
byte[] result1 = theMac.doFinal(plain);
if (!isMacLengthExpected(theMacAlgo, result1.length)) {
return false;
}
// Reset Mac and calculate result2
theMac.reset();
theMac.update(plain[0]);
theMac.update(plain, 1, OFFSET - 1);
byte[] result2 = theMac.doFinal(tail);
// Return result
if (!java.util.Arrays.equals(result1, result2)) {
System.out.println("result1 and result2 are not the same:");
System.out.println("result1: " + dumpByteArray(result1));
System.out.println("result2: " + dumpByteArray(result2));
return false;
} else {
System.out.println("Resulted MAC with update and doFinal is same");
}
return true;
}
/**
* Get SecretKey for the given PBKDF2 algorithm.
*
* @param thePBKDF2Algorithm - PBKDF2 algorithm
* @return SecretKey according to thePBKDF2Algorithm
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
protected SecretKey getSecretKey(String thePBKDF2Algorithm)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// Prepare salt
byte[] salt = new byte[64]; // PKCS #5 v2.1 recommendation
new SecureRandom().nextBytes(salt);
// Generate secret key
PBEKeySpec pbeKeySpec = new PBEKeySpec(
"A #pwd# implied to be hidden!".toCharArray(),
salt, 1000, 128);
SecretKeyFactory keyFactory
= SecretKeyFactory.getInstance(thePBKDF2Algorithm);
return keyFactory.generateSecret(pbeKeySpec);
}
/**
* Check if the lengthToCheck is expected length for the given MACAlgo.
*
* @param MACAlgo PBMAC algorithm
* @param lengthToCheck the length of MAC need to check
* @return true - lengthToCheck is expected length for the MACAlgo; false -
* otherwise.
*/
protected boolean isMacLengthExpected(String MACAlgo, int lengthToCheck) {
java.util.regex.Pattern p = java.util.regex.Pattern.compile("(\\d+)",
java.util.regex.Pattern.CASE_INSENSITIVE);
java.util.regex.Matcher m = p.matcher(MACAlgo);
int val = 0;
while (m.find()) { // use the lastly found number
val = Integer.parseInt(m.group(1));
}
// HmacPBESHA1 should return MAC 20 byte length
if ((val == 1) && (lengthToCheck == 20)) {
return true;
}
return (val / 8) == lengthToCheck;
}
/**
* An utility method to dump a byte array for debug output.
*
* @param theByteArray the byte array to dump
* @return string representation of the theByteArray in Hex.
*/
protected String dumpByteArray(byte[] theByteArray) {
StringBuilder buf = new StringBuilder();
for (byte b : theByteArray) {
buf.append(Integer.toHexString(b));
}
return buf.toString();
}
}
|