c7c5826bb2
Reviewed-by: xuelei
317 lines
13 KiB
Java
317 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2015, 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 static java.lang.System.out;
|
|
|
|
import java.lang.Integer;
|
|
import java.lang.String;
|
|
import java.lang.System;
|
|
import java.security.AlgorithmParameters;
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.Key;
|
|
import java.security.KeyPair;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.KeyPairGenerator;
|
|
import java.security.Provider;
|
|
import java.security.Security;
|
|
import java.security.spec.AlgorithmParameterSpec;
|
|
import java.security.spec.InvalidKeySpecException;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
import javax.crypto.NoSuchPaddingException;
|
|
import javax.crypto.SecretKey;
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.KeyGenerator;
|
|
import javax.crypto.SecretKeyFactory;
|
|
import javax.crypto.spec.PBEKeySpec;
|
|
import javax.crypto.spec.PBEParameterSpec;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8048599
|
|
* @summary Tests for key wrap and unwrap operations
|
|
*/
|
|
|
|
public class TestCipherKeyWrapperTest {
|
|
private static final String SUN_JCE = "SunJCE";
|
|
// Blowfish Variable key length: 32 bits to 448 bits
|
|
private static final int BLOWFISH_MIN_KEYSIZE = 32;
|
|
private static final int BLOWFISH_MAX_KEYSIZE = 448;
|
|
private static final int LINIMITED_KEYSIZE = 128;
|
|
private static final String NOPADDING = "NoPaDDing";
|
|
private static final String[] PBE_ALGORITHM_AR = { "pbeWithMD5ANDdes",
|
|
"PBEWithMD5AndDES/CBC/PKCS5Padding", "PBEWithMD5AndTripleDES",
|
|
"PBEWithMD5AndTripleDES/CBC/PKCS5Padding", "PBEwithSHA1AndDESede",
|
|
"PBEwithSHA1AndDESede/CBC/PKCS5Padding", "PBEwithSHA1AndRC2_40",
|
|
"PBEwithSHA1Andrc2_40/CBC/PKCS5Padding", "PBEWithSHA1AndRC2_128",
|
|
"PBEWithSHA1andRC2_128/CBC/PKCS5Padding", "PBEWithSHA1AndRC4_40",
|
|
"PBEWithsha1AndRC4_40/ECB/NoPadding", "PBEWithSHA1AndRC4_128",
|
|
"pbeWithSHA1AndRC4_128/ECB/NoPadding", "PBEWithHmacSHA1AndAES_128",
|
|
"PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA256AndAES_128",
|
|
"PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA512AndAES_128",
|
|
"PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_256",
|
|
"PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_256",
|
|
"PBEWithHmacSHA512AndAES_256" };
|
|
private static final String[] MODEL_AR = { "ECb", "pCbC", "cbC", "cFB",
|
|
"cFB24", "cFB40", "OfB48", "OFB64" };
|
|
private static final String[] PADDING_AR = { NOPADDING, "PKCS5Padding" };
|
|
|
|
private enum AlgorithmWrapper {
|
|
AESWrap("AES", "AESWrap", -1),
|
|
AESWrap_128("AES", "AESWrap_128", 128),
|
|
AESWrap_192("AES", "AESWrap_192", 192),
|
|
AESWrap_256("AES", "AESWrap_256", 256),
|
|
DESedeWrap("desede", "DESedeWrap", -1),
|
|
NegtiveWrap("AES", "DESedeWrap", -1);
|
|
|
|
private final String algorithm;
|
|
private final String wrapper;
|
|
private final int keySize;
|
|
|
|
private AlgorithmWrapper(String algorithm, String wrapper, int kSize) {
|
|
this.algorithm = algorithm;
|
|
this.wrapper = wrapper;
|
|
this.keySize = kSize;
|
|
}
|
|
|
|
public String getAlgorithm() {
|
|
return algorithm;
|
|
}
|
|
|
|
public String getWrapper() {
|
|
return wrapper;
|
|
}
|
|
|
|
public int getKeySize() {
|
|
return keySize;
|
|
}
|
|
|
|
};
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
TestCipherKeyWrapperTest test = new TestCipherKeyWrapperTest();
|
|
// AESWrap and DESedeWrap test
|
|
for (AlgorithmWrapper algoWrapper : AlgorithmWrapper.values()) {
|
|
String algo = algoWrapper.getAlgorithm();
|
|
String wrapper = algoWrapper.getWrapper();
|
|
try {
|
|
int keySize = algoWrapper.getKeySize();
|
|
// only run the tests on longer key lengths if unlimited
|
|
// version of JCE jurisdiction policy files are installed
|
|
if (!(Cipher.getMaxAllowedKeyLength(algo) == Integer.MAX_VALUE)
|
|
&& keySize > LINIMITED_KEYSIZE) {
|
|
out.println(algo + " will not run if unlimited version of"
|
|
+ " JCE jurisdiction policy files are installed");
|
|
continue;
|
|
}
|
|
test.wrapperAesDESedeKeyTest(algo, wrapper, keySize);
|
|
if (algoWrapper == AlgorithmWrapper.NegtiveWrap) {
|
|
throw new RuntimeException("Expected not throw when algo"
|
|
+ " and wrapAlgo are not match:" + algo);
|
|
}
|
|
} catch (InvalidKeyException e) {
|
|
if (algoWrapper == AlgorithmWrapper.NegtiveWrap) {
|
|
out.println("Expepted exception when algo"
|
|
+ " and wrapAlgo are not match:" + algo);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
test.wrapperBlowfishKeyTest();
|
|
// PBE and public wrapper test.
|
|
String[] publicPrivateAlgos = new String[] { "DiffieHellman", "DSA",
|
|
"RSA" };
|
|
Provider provider = Security.getProvider(SUN_JCE);
|
|
if (provider == null) {
|
|
throw new RuntimeException("SUN_JCE provider not exist");
|
|
}
|
|
|
|
test.wrapperPBEKeyTest(provider);
|
|
// Public and private key wrap test
|
|
test.wrapperPublicPriviteKeyTest(provider, publicPrivateAlgos);
|
|
}
|
|
|
|
private void wrapperAesDESedeKeyTest(String algo, String wrapAlgo,
|
|
int keySize) throws InvalidKeyException, NoSuchAlgorithmException,
|
|
NoSuchPaddingException, IllegalBlockSizeException,
|
|
InvalidAlgorithmParameterException {
|
|
// Initialization
|
|
KeyGenerator kg = KeyGenerator.getInstance(algo);
|
|
if (keySize != -1) {
|
|
kg.init(keySize);
|
|
}
|
|
SecretKey key = kg.generateKey();
|
|
wrapTest(algo, wrapAlgo, key, key, Cipher.SECRET_KEY, false);
|
|
}
|
|
|
|
private void wrapperBlowfishKeyTest() throws InvalidKeyException,
|
|
NoSuchAlgorithmException, NoSuchPaddingException,
|
|
IllegalBlockSizeException, InvalidAlgorithmParameterException {
|
|
// how many kinds of padding mode
|
|
int padKinds;
|
|
// Keysize should be multiple of 8 bytes.
|
|
int KeyCutter = 8;
|
|
int kSize = BLOWFISH_MIN_KEYSIZE;
|
|
String algorithm = "Blowfish";
|
|
int maxAllowKeyLength = Cipher.getMaxAllowedKeyLength(algorithm);
|
|
boolean unLimitPolicy = maxAllowKeyLength == Integer.MAX_VALUE;
|
|
SecretKey key = null;
|
|
while (kSize <= BLOWFISH_MAX_KEYSIZE) {
|
|
for (String mode : MODEL_AR) {
|
|
// PKCS5padding is meaningful only for ECB, CBC, PCBC
|
|
if (mode.equalsIgnoreCase(MODEL_AR[0])
|
|
|| mode.equalsIgnoreCase(MODEL_AR[1])
|
|
|| mode.equalsIgnoreCase(MODEL_AR[2])) {
|
|
padKinds = PADDING_AR.length;
|
|
} else {
|
|
padKinds = 1;
|
|
}
|
|
// Initialization
|
|
KeyGenerator kg = KeyGenerator.getInstance(algorithm);
|
|
for (int k = 0; k < padKinds; k++) {
|
|
String transformation = algorithm + "/" + mode + "/"
|
|
+ PADDING_AR[k];
|
|
if (NOPADDING.equals(PADDING_AR[k]) && kSize % 64 != 0) {
|
|
out.println(transformation
|
|
+ " will not run if input length not multiple"
|
|
+ " of 8 bytes when padding is " + NOPADDING);
|
|
continue;
|
|
}
|
|
kg.init(kSize);
|
|
key = kg.generateKey();
|
|
// only run the tests on longer key lengths if unlimited
|
|
// version of JCE jurisdiction policy files are installed
|
|
if (!unLimitPolicy && kSize > LINIMITED_KEYSIZE) {
|
|
out.println("keyStrength > 128 within " + algorithm
|
|
+ " will not run under global policy");
|
|
} else {
|
|
wrapTest(transformation, transformation, key, key,
|
|
Cipher.SECRET_KEY, false);
|
|
}
|
|
}
|
|
}
|
|
if (kSize <= LINIMITED_KEYSIZE) {
|
|
KeyCutter = 8;
|
|
} else {
|
|
KeyCutter = 48;
|
|
}
|
|
kSize += KeyCutter;
|
|
}
|
|
}
|
|
|
|
private void wrapperPBEKeyTest(Provider p) throws InvalidKeySpecException,
|
|
InvalidKeyException, NoSuchPaddingException,
|
|
IllegalBlockSizeException, InvalidAlgorithmParameterException,
|
|
NoSuchAlgorithmException {
|
|
for (String alg : PBE_ALGORITHM_AR) {
|
|
String baseAlgo = alg.split("/")[0].toUpperCase();
|
|
// only run the tests on longer key lengths if unlimited version
|
|
// of JCE jurisdiction policy files are installed
|
|
|
|
if (Cipher.getMaxAllowedKeyLength(alg) < Integer.MAX_VALUE
|
|
&& (baseAlgo.endsWith("TRIPLEDES") || alg
|
|
.endsWith("AES_256"))) {
|
|
out.println("keyStrength > 128 within " + alg
|
|
+ " will not run under global policy");
|
|
continue;
|
|
}
|
|
SecretKeyFactory skf = SecretKeyFactory.getInstance(baseAlgo, p);
|
|
SecretKey key = skf.generateSecret(new PBEKeySpec("Secret Lover"
|
|
.toCharArray()));
|
|
wrapTest(alg, alg, key, key, Cipher.SECRET_KEY, true);
|
|
}
|
|
}
|
|
|
|
private void wrapperPublicPriviteKeyTest(Provider p, String[] algorithms)
|
|
throws NoSuchAlgorithmException, InvalidKeyException,
|
|
NoSuchPaddingException, IllegalBlockSizeException,
|
|
InvalidAlgorithmParameterException {
|
|
for (String algo : algorithms) {
|
|
// Key pair generated
|
|
System.out.println("Generate key pair (algorithm: " + algo
|
|
+ ", provider: " + p.getName() + ")");
|
|
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algo);
|
|
kpg.initialize(512);
|
|
KeyPair kp = kpg.genKeyPair();
|
|
// key generated
|
|
String algoWrap = "DES";
|
|
KeyGenerator kg = KeyGenerator.getInstance(algoWrap, p);
|
|
Key key = kg.generateKey();
|
|
wrapTest(algo, algoWrap, key, kp.getPrivate(), Cipher.PRIVATE_KEY,
|
|
false);
|
|
wrapTest(algo, algoWrap, key, kp.getPublic(), Cipher.PUBLIC_KEY,
|
|
false);
|
|
}
|
|
}
|
|
|
|
private void wrapTest(String transformation, String wrapAlgo, Key initKey,
|
|
Key wrapKey, int keyType, boolean isPBE)
|
|
throws NoSuchAlgorithmException, NoSuchPaddingException,
|
|
InvalidKeyException, IllegalBlockSizeException,
|
|
InvalidAlgorithmParameterException {
|
|
String algo = transformation.split("/")[0];
|
|
boolean isAESBlowfish = algo.indexOf("AES") != -1
|
|
|| algo.indexOf("Blowfish") != -1;
|
|
AlgorithmParameters aps = null;
|
|
AlgorithmParameterSpec pbeParams = null;
|
|
if (isPBE) {
|
|
byte[] salt = new byte[8];
|
|
int iterCnt = 1000;
|
|
new Random().nextBytes(salt);
|
|
pbeParams = new PBEParameterSpec(salt, iterCnt);
|
|
}
|
|
// Wrap & UnWrap operation
|
|
Cipher wrapCI = Cipher.getInstance(wrapAlgo);
|
|
if (isPBE && !isAESBlowfish) {
|
|
wrapCI.init(Cipher.WRAP_MODE, initKey, pbeParams);
|
|
} else if (isAESBlowfish) {
|
|
wrapCI.init(Cipher.WRAP_MODE, initKey);
|
|
aps = wrapCI.getParameters();
|
|
} else {
|
|
wrapCI.init(Cipher.WRAP_MODE, initKey);
|
|
}
|
|
out.println("keysize : " + wrapKey.getEncoded().length);
|
|
byte[] keyWrapper = wrapCI.wrap(wrapKey);
|
|
if (isPBE && !isAESBlowfish) {
|
|
wrapCI.init(Cipher.UNWRAP_MODE, initKey, pbeParams);
|
|
} else if (isAESBlowfish) {
|
|
wrapCI.init(Cipher.UNWRAP_MODE, initKey, aps);
|
|
} else {
|
|
wrapCI.init(Cipher.UNWRAP_MODE, initKey);
|
|
}
|
|
Key unwrappedKey = wrapCI.unwrap(keyWrapper, algo, keyType);
|
|
// Comparison
|
|
if (!Arrays.equals(wrapKey.getEncoded(), unwrappedKey.getEncoded())) {
|
|
throw new RuntimeException("Comparation failed testing "
|
|
+ transformation + ":" + wrapAlgo + ":" + keyType);
|
|
}
|
|
}
|
|
}
|