blob: 5a5a6100b600950e8a74879087a4d2b4895c7bf0 [file] [log] [blame]
/*
* Copyright (c) 2003, 2008, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package sun.security.pkcs11;
import java.util.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import static sun.security.pkcs11.TemplateManager.*;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
/**
* SecretKeyFactory implementation class. This class currently supports
* DES, DESede, AES, ARCFOUR, and Blowfish.
*
* @author Andreas Sterbenz
* @since 1.5
*/
final class P11SecretKeyFactory extends SecretKeyFactorySpi {
// token instance
private final Token token;
// algorithm name
private final String algorithm;
P11SecretKeyFactory(Token token, String algorithm) {
super();
this.token = token;
this.algorithm = algorithm;
}
private static final Map<String,Long> keyTypes;
static {
keyTypes = new HashMap<String,Long>();
addKeyType("RC4", CKK_RC4);
addKeyType("ARCFOUR", CKK_RC4);
addKeyType("DES", CKK_DES);
addKeyType("DESede", CKK_DES3);
addKeyType("AES", CKK_AES);
addKeyType("Blowfish", CKK_BLOWFISH);
// we don't implement RC2 or IDEA, but we want to be able to generate
// keys for those SSL/TLS ciphersuites.
addKeyType("RC2", CKK_RC2);
addKeyType("IDEA", CKK_IDEA);
addKeyType("TlsPremasterSecret", PCKK_TLSPREMASTER);
addKeyType("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER);
addKeyType("TlsMasterSecret", PCKK_TLSMASTER);
addKeyType("Generic", CKK_GENERIC_SECRET);
}
private static void addKeyType(String name, long id) {
Long l = Long.valueOf(id);
keyTypes.put(name, l);
keyTypes.put(name.toUpperCase(Locale.ENGLISH), l);
}
static long getKeyType(String algorithm) {
Long l = keyTypes.get(algorithm);
if (l == null) {
algorithm = algorithm.toUpperCase(Locale.ENGLISH);
l = keyTypes.get(algorithm);
if (l == null) {
if (algorithm.startsWith("HMAC")) {
return PCKK_HMAC;
} else if (algorithm.startsWith("SSLMAC")) {
return PCKK_SSLMAC;
}
}
}
return (l != null) ? l.longValue() : -1;
}
/**
* Convert an arbitrary key of algorithm into a P11Key of provider.
* Used in engineTranslateKey(), P11Cipher.init(), and P11Mac.init().
*/
static P11Key convertKey(Token token, Key key, String algo)
throws InvalidKeyException {
return convertKey(token, key, algo, null);
}
/**
* Convert an arbitrary key of algorithm w/ custom attributes into a
* P11Key of provider.
* Used in P11KeyStore.storeSkey.
*/
static P11Key convertKey(Token token, Key key, String algo,
CK_ATTRIBUTE[] extraAttrs)
throws InvalidKeyException {
token.ensureValid();
if (key == null) {
throw new InvalidKeyException("Key must not be null");
}
if (key instanceof SecretKey == false) {
throw new InvalidKeyException("Key must be a SecretKey");
}
long algoType;
if (algo == null) {
algo = key.getAlgorithm();
algoType = getKeyType(algo);
} else {
algoType = getKeyType(algo);
long keyAlgorithmType = getKeyType(key.getAlgorithm());
if (algoType != keyAlgorithmType) {
if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) {
// ignore key algorithm for MACs
} else {
throw new InvalidKeyException
("Key algorithm must be " + algo);
}
}
}
if (key instanceof P11Key) {
P11Key p11Key = (P11Key)key;
if (p11Key.token == token) {
if (extraAttrs != null) {
Session session = null;
try {
session = token.getObjSession();
long newKeyID = token.p11.C_CopyObject(session.id(),
p11Key.keyID, extraAttrs);
p11Key = (P11Key) (P11Key.secretKey(session,
newKeyID, p11Key.algorithm, p11Key.keyLength,
extraAttrs));
} catch (PKCS11Exception p11e) {
throw new InvalidKeyException
("Cannot duplicate the PKCS11 key", p11e);
} finally {
token.releaseSession(session);
}
}
return p11Key;
}
}
P11Key p11Key = token.secretCache.get(key);
if (p11Key != null) {
return p11Key;
}
if ("RAW".equalsIgnoreCase(key.getFormat()) == false) {
throw new InvalidKeyException("Encoded format must be RAW");
}
byte[] encoded = key.getEncoded();
p11Key = createKey(token, encoded, algo, algoType, extraAttrs);
token.secretCache.put(key, p11Key);
return p11Key;
}
static void fixDESParity(byte[] key, int offset) {
for (int i = 0; i < 8; i++) {
int b = key[offset] & 0xfe;
b |= (Integer.bitCount(b) & 1) ^ 1;
key[offset++] = (byte)b;
}
}
private static P11Key createKey(Token token, byte[] encoded,
String algorithm, long keyType, CK_ATTRIBUTE[] extraAttrs)
throws InvalidKeyException {
int n = encoded.length << 3;
int keyLength = n;
try {
switch ((int)keyType) {
case (int)CKK_DES:
keyLength =
P11KeyGenerator.checkKeySize(CKM_DES_KEY_GEN, n, token);
fixDESParity(encoded, 0);
break;
case (int)CKK_DES3:
keyLength =
P11KeyGenerator.checkKeySize(CKM_DES3_KEY_GEN, n, token);
fixDESParity(encoded, 0);
fixDESParity(encoded, 8);
if (keyLength == 112) {
keyType = CKK_DES2;
} else {
keyType = CKK_DES3;
fixDESParity(encoded, 16);
}
break;
case (int)CKK_AES:
keyLength =
P11KeyGenerator.checkKeySize(CKM_AES_KEY_GEN, n, token);
break;
case (int)CKK_RC4:
keyLength =
P11KeyGenerator.checkKeySize(CKM_RC4_KEY_GEN, n, token);
break;
case (int)CKK_BLOWFISH:
keyLength =
P11KeyGenerator.checkKeySize(CKM_BLOWFISH_KEY_GEN, n,
token);
break;
case (int)CKK_GENERIC_SECRET:
case (int)PCKK_TLSPREMASTER:
case (int)PCKK_TLSRSAPREMASTER:
case (int)PCKK_TLSMASTER:
keyType = CKK_GENERIC_SECRET;
break;
case (int)PCKK_SSLMAC:
case (int)PCKK_HMAC:
if (n == 0) {
throw new InvalidKeyException
("MAC keys must not be empty");
}
keyType = CKK_GENERIC_SECRET;
break;
default:
throw new InvalidKeyException("Unknown algorithm " +
algorithm);
}
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("Invalid key for " + algorithm,
iape);
} catch (ProviderException pe) {
throw new InvalidKeyException("Could not create key", pe);
}
Session session = null;
try {
CK_ATTRIBUTE[] attributes;
if (extraAttrs != null) {
attributes = new CK_ATTRIBUTE[3 + extraAttrs.length];
System.arraycopy(extraAttrs, 0, attributes, 3,
extraAttrs.length);
} else {
attributes = new CK_ATTRIBUTE[3];
}
attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType);
attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded);
attributes = token.getAttributes
(O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
session = token.getObjSession();
long keyID = token.p11.C_CreateObject(session.id(), attributes);
P11Key p11Key = (P11Key)P11Key.secretKey
(session, keyID, algorithm, keyLength, attributes);
return p11Key;
} catch (PKCS11Exception e) {
throw new InvalidKeyException("Could not create key", e);
} finally {
token.releaseSession(session);
}
}
// see JCE spec
protected SecretKey engineGenerateSecret(KeySpec keySpec)
throws InvalidKeySpecException {
token.ensureValid();
if (keySpec == null) {
throw new InvalidKeySpecException("KeySpec must not be null");
}
if (keySpec instanceof SecretKeySpec) {
try {
Key key = convertKey(token, (SecretKey)keySpec, algorithm);
return (SecretKey)key;
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
} else if (algorithm.equalsIgnoreCase("DES")) {
if (keySpec instanceof DESKeySpec) {
byte[] keyBytes = ((DESKeySpec)keySpec).getKey();
keySpec = new SecretKeySpec(keyBytes, "DES");
return engineGenerateSecret(keySpec);
}
} else if (algorithm.equalsIgnoreCase("DESede")) {
if (keySpec instanceof DESedeKeySpec) {
byte[] keyBytes = ((DESedeKeySpec)keySpec).getKey();
keySpec = new SecretKeySpec(keyBytes, "DESede");
return engineGenerateSecret(keySpec);
}
}
throw new InvalidKeySpecException
("Unsupported spec: " + keySpec.getClass().getName());
}
private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException {
try {
key = engineTranslateKey(key);
if ("RAW".equalsIgnoreCase(key.getFormat()) == false) {
throw new InvalidKeySpecException
("Could not obtain key bytes");
}
byte[] k = key.getEncoded();
return k;
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
}
// see JCE spec
protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec)
throws InvalidKeySpecException {
token.ensureValid();
if ((key == null) || (keySpec == null)) {
throw new InvalidKeySpecException
("key and keySpec must not be null");
}
if (SecretKeySpec.class.isAssignableFrom(keySpec)) {
return new SecretKeySpec(getKeyBytes(key), algorithm);
} else if (algorithm.equalsIgnoreCase("DES")) {
try {
if (DESKeySpec.class.isAssignableFrom(keySpec)) {
return new DESKeySpec(getKeyBytes(key));
}
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
} else if (algorithm.equalsIgnoreCase("DESede")) {
try {
if (DESedeKeySpec.class.isAssignableFrom(keySpec)) {
return new DESedeKeySpec(getKeyBytes(key));
}
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
}
throw new InvalidKeySpecException
("Unsupported spec: " + keySpec.getName());
}
// see JCE spec
protected SecretKey engineTranslateKey(SecretKey key)
throws InvalidKeyException {
return (SecretKey)convertKey(token, key, algorithm);
}
}