0N/A/*
3002N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.crypto.provider;
0N/A
0N/Aimport java.io.UnsupportedEncodingException;
0N/Aimport java.math.BigInteger;
0N/Aimport java.security.*;
0N/Aimport java.security.spec.*;
0N/Aimport java.util.Arrays;
0N/Aimport javax.crypto.*;
0N/Aimport javax.crypto.spec.*;
0N/A
0N/A/**
0N/A * This class implements password-base encryption algorithm with
0N/A * SHA1 digest and the following Ciphers in CBC mode
0N/A * - DESede cipher and
0N/A * - RC2 Cipher with 40-bit effective key length
0N/A * as defined by PKCS #12 version 1.0 standard.
0N/A *
0N/A * @author Valerie Peng
0N/A * @see javax.crypto.CipherSpi
0N/A */
0N/Afinal class PKCS12PBECipherCore {
0N/A private CipherCore cipher;
0N/A private int blockSize;
0N/A private int keySize;
0N/A private String algo = null;
0N/A private byte[] salt = null;
0N/A private int iCount = 0;
0N/A
0N/A private static final int DEFAULT_SALT_LENGTH = 20;
0N/A private static final int DEFAULT_COUNT = 1024;
0N/A
0N/A static final int CIPHER_KEY = 1;
0N/A static final int CIPHER_IV = 2;
0N/A static final int MAC_KEY = 3;
0N/A
0N/A static byte[] derive(char[] chars, byte[] salt,
0N/A int ic, int n, int type) {
4784N/A // Add in trailing NULL terminator. Special case:
4784N/A // no terminator if password is "\0".
0N/A int length = chars.length*2;
4784N/A if (length == 2 && chars[0] == 0) {
4784N/A chars = new char[0];
4784N/A length = 0;
4784N/A } else {
0N/A length += 2;
0N/A }
4784N/A
0N/A byte[] passwd = new byte[length];
0N/A for (int i = 0, j = 0; i < chars.length; i++, j+=2) {
0N/A passwd[j] = (byte) ((chars[i] >>> 8) & 0xFF);
0N/A passwd[j+1] = (byte) (chars[i] & 0xFF);
0N/A }
0N/A int v = 512 / 8;
0N/A int u = 160 / 8;
0N/A int c = roundup(n, u) / u;
0N/A byte[] D = new byte[v];
0N/A int s = roundup(salt.length, v);
0N/A int p = roundup(passwd.length, v);
0N/A byte[] I = new byte[s + p];
0N/A byte[] key = new byte[n];
0N/A
0N/A Arrays.fill(D, (byte)type);
0N/A concat(salt, I, 0, s);
0N/A concat(passwd, I, s, p);
0N/A
0N/A try {
0N/A MessageDigest sha = MessageDigest.getInstance("SHA1");
0N/A byte[] Ai;
0N/A byte[] B = new byte[v];
0N/A byte[] tmp = new byte[v];
0N/A
0N/A int i = 0;
0N/A for (; ; i++, n -= u) {
0N/A sha.update(D);
0N/A sha.update(I);
0N/A Ai = sha.digest();
0N/A for (int r = 1; r < ic; r++)
0N/A Ai = sha.digest(Ai);
0N/A System.arraycopy(Ai, 0, key, u * i, Math.min(n, u));
0N/A if (i + 1 == c)
0N/A break;
0N/A concat(Ai, B, 0, B.length);
0N/A BigInteger B1;
0N/A B1 = new BigInteger(1, B).add(BigInteger.ONE);
0N/A
0N/A for (int j = 0; j < I.length; j += v) {
0N/A BigInteger Ij;
0N/A int trunc;
0N/A
0N/A if (tmp.length != v)
0N/A tmp = new byte[v];
0N/A System.arraycopy(I, j, tmp, 0, v);
0N/A Ij = new BigInteger(1, tmp);
0N/A Ij = Ij.add(B1);
0N/A tmp = Ij.toByteArray();
0N/A trunc = tmp.length - v;
0N/A if (trunc >= 0) {
0N/A System.arraycopy(tmp, trunc, I, j, v);
0N/A } else if (trunc < 0) {
0N/A Arrays.fill(I, j, j + (-trunc), (byte)0);
0N/A System.arraycopy(tmp, 0, I, j + (-trunc), tmp.length);
0N/A }
0N/A }
0N/A }
0N/A } catch (Exception e) {
0N/A throw new RuntimeException("internal error: " + e);
0N/A }
0N/A return key;
0N/A }
0N/A
0N/A private static int roundup(int x, int y) {
0N/A return ((x + (y - 1)) / y) * y;
0N/A }
0N/A
0N/A private static void concat(byte[] src, byte[] dst, int start, int len) {
4784N/A if (src.length == 0) {
4784N/A return;
4784N/A }
0N/A int loop = len / src.length;
0N/A int off, i;
0N/A for (i = 0, off = 0; i < loop; i++, off += src.length)
0N/A System.arraycopy(src, 0, dst, off + start, src.length);
0N/A System.arraycopy(src, 0, dst, off + start, len - off);
0N/A }
0N/A
0N/A PKCS12PBECipherCore(String symmCipherAlg, int defKeySize)
0N/A throws NoSuchAlgorithmException {
0N/A algo = symmCipherAlg;
0N/A SymmetricCipher symmCipher = null;
0N/A if (algo.equals("DESede")) {
0N/A symmCipher = new DESedeCrypt();
0N/A } else if (algo.equals("RC2")) {
0N/A symmCipher = new RC2Crypt();
0N/A } else {
0N/A throw new NoSuchAlgorithmException("No Cipher implementation " +
0N/A "for PBEWithSHA1And" + algo);
0N/A }
0N/A blockSize = symmCipher.getBlockSize();
0N/A cipher = new CipherCore(symmCipher, blockSize);
0N/A cipher.setMode("CBC");
0N/A try {
0N/A cipher.setPadding("PKCS5Padding");
0N/A } catch (NoSuchPaddingException nspe) {
0N/A // should not happen
0N/A }
0N/A keySize = defKeySize;
0N/A }
0N/A
0N/A void implSetMode(String mode) throws NoSuchAlgorithmException {
0N/A if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
0N/A throw new NoSuchAlgorithmException("Invalid cipher mode: "
0N/A + mode);
0N/A }
0N/A }
0N/A
0N/A void implSetPadding(String padding) throws NoSuchPaddingException {
0N/A if ((padding != null) &&
0N/A (!padding.equalsIgnoreCase("PKCS5Padding"))) {
0N/A throw new NoSuchPaddingException("Invalid padding scheme: " +
0N/A padding);
0N/A }
0N/A }
0N/A
0N/A int implGetBlockSize() {
0N/A return blockSize;
0N/A }
0N/A
0N/A int implGetOutputSize(int inLen) {
0N/A return cipher.getOutputSize(inLen);
0N/A }
0N/A
0N/A byte[] implGetIV() {
0N/A return cipher.getIV();
0N/A }
0N/A
0N/A AlgorithmParameters implGetParameters() {
0N/A AlgorithmParameters params = null;
0N/A if (salt == null) {
0N/A // Cipher is not initialized with parameters;
0N/A // follow the recommendation in PKCS12 v1.0
0N/A // section B.4 to generate salt and iCount.
0N/A salt = new byte[DEFAULT_SALT_LENGTH];
0N/A SunJCE.RANDOM.nextBytes(salt);
0N/A iCount = DEFAULT_COUNT;
0N/A }
0N/A PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
0N/A try {
0N/A params = AlgorithmParameters.getInstance("PBEWithSHA1And" +
0N/A (algo.equalsIgnoreCase("RC2")?"RC2_40":algo), "SunJCE");
0N/A } catch (GeneralSecurityException gse) {
0N/A // should never happen
3002N/A throw new RuntimeException(
3002N/A "SunJCE provider is not configured properly");
0N/A }
0N/A try {
0N/A params.init(pbeSpec);
0N/A } catch (InvalidParameterSpecException ipse) {
0N/A // should never happen
0N/A throw new RuntimeException("PBEParameterSpec not supported");
0N/A }
0N/A return params;
0N/A }
0N/A
0N/A void implInit(int opmode, Key key, AlgorithmParameterSpec params,
0N/A SecureRandom random) throws InvalidKeyException,
0N/A InvalidAlgorithmParameterException {
0N/A char[] passwdChars = null;
0N/A salt = null;
0N/A iCount = 0;
0N/A if (key instanceof javax.crypto.interfaces.PBEKey) {
0N/A javax.crypto.interfaces.PBEKey pbeKey =
0N/A (javax.crypto.interfaces.PBEKey) key;
0N/A passwdChars = pbeKey.getPassword();
0N/A salt = pbeKey.getSalt(); // maybe null if unspecified
0N/A iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
0N/A } else if (key instanceof SecretKey) {
0N/A byte[] passwdBytes = key.getEncoded();
0N/A if ((passwdBytes == null) ||
0N/A !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
0N/A throw new InvalidKeyException("Missing password");
0N/A }
0N/A passwdChars = new char[passwdBytes.length];
0N/A for (int i=0; i<passwdChars.length; i++) {
0N/A passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
0N/A }
0N/A } else {
0N/A throw new InvalidKeyException("SecretKey of PBE type required");
0N/A }
0N/A
0N/A if (((opmode == Cipher.DECRYPT_MODE) ||
0N/A (opmode == Cipher.UNWRAP_MODE)) &&
0N/A ((params == null) && ((salt == null) || (iCount == 0)))) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("Parameters missing");
0N/A }
0N/A
0N/A if (params == null) {
0N/A // generate default for salt and iteration count if necessary
0N/A if (salt == null) {
0N/A salt = new byte[DEFAULT_SALT_LENGTH];
0N/A if (random != null) {
0N/A random.nextBytes(salt);
0N/A } else {
0N/A SunJCE.RANDOM.nextBytes(salt);
0N/A }
0N/A }
0N/A if (iCount == 0) iCount = DEFAULT_COUNT;
0N/A } else if (!(params instanceof PBEParameterSpec)) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("PBEParameterSpec type required");
0N/A } else {
0N/A PBEParameterSpec pbeParams = (PBEParameterSpec) params;
0N/A // make sure the parameter values are consistent
0N/A if (salt != null) {
0N/A if (!Arrays.equals(salt, pbeParams.getSalt())) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("Inconsistent value of salt between key and params");
0N/A }
0N/A } else {
0N/A salt = pbeParams.getSalt();
0N/A }
0N/A if (iCount != 0) {
0N/A if (iCount != pbeParams.getIterationCount()) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("Different iteration count between key and params");
0N/A }
0N/A } else {
0N/A iCount = pbeParams.getIterationCount();
0N/A }
0N/A }
0N/A // salt is recommended to be ideally as long as the output
0N/A // of the hash function. However, it may be too strict to
0N/A // force this; so instead, we'll just require the minimum
0N/A // salt length to be 8-byte which is what PKCS#5 recommends
0N/A // and openssl does.
0N/A if (salt.length < 8) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("Salt must be at least 8 bytes long");
0N/A }
0N/A if (iCount <= 0) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("IterationCount must be a positive number");
0N/A }
0N/A byte[] derivedKey = derive(passwdChars, salt, iCount,
0N/A keySize, CIPHER_KEY);
0N/A SecretKey cipherKey = new SecretKeySpec(derivedKey, algo);
0N/A byte[] derivedIv = derive(passwdChars, salt, iCount, 8,
0N/A CIPHER_IV);
0N/A IvParameterSpec ivSpec = new IvParameterSpec(derivedIv, 0, 8);
0N/A
0N/A // initialize the underlying cipher
0N/A cipher.init(opmode, cipherKey, ivSpec, random);
0N/A }
0N/A
0N/A void implInit(int opmode, Key key, AlgorithmParameters params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A AlgorithmParameterSpec paramSpec = null;
0N/A if (params != null) {
0N/A try {
0N/A paramSpec = params.getParameterSpec(PBEParameterSpec.class);
0N/A } catch (InvalidParameterSpecException ipse) {
3002N/A throw new InvalidAlgorithmParameterException(
3002N/A "requires PBE parameters");
0N/A }
0N/A }
0N/A implInit(opmode, key, paramSpec, random);
0N/A }
0N/A
0N/A void implInit(int opmode, Key key, SecureRandom random)
0N/A throws InvalidKeyException {
0N/A try {
0N/A implInit(opmode, key, (AlgorithmParameterSpec) null, random);
0N/A } catch (InvalidAlgorithmParameterException iape) {
0N/A throw new InvalidKeyException("requires PBE parameters");
0N/A }
0N/A }
0N/A
0N/A byte[] implUpdate(byte[] in, int inOff, int inLen) {
0N/A return cipher.update(in, inOff, inLen);
0N/A }
0N/A
0N/A int implUpdate(byte[] in, int inOff, int inLen, byte[] out, int outOff)
0N/A throws ShortBufferException {
0N/A return cipher.update(in, inOff, inLen, out, outOff);
0N/A }
0N/A
0N/A byte[] implDoFinal(byte[] in, int inOff, int inLen)
0N/A throws IllegalBlockSizeException, BadPaddingException {
0N/A return cipher.doFinal(in, inOff, inLen);
0N/A }
0N/A
0N/A int implDoFinal(byte[] in, int inOff, int inLen, byte[] out, int outOff)
0N/A throws ShortBufferException, IllegalBlockSizeException,
0N/A BadPaddingException {
0N/A return cipher.doFinal(in, inOff, inLen, out, outOff);
0N/A }
0N/A
0N/A int implGetKeySize(Key key) throws InvalidKeyException {
0N/A return keySize;
0N/A }
0N/A
0N/A byte[] implWrap(Key key) throws IllegalBlockSizeException,
0N/A InvalidKeyException {
0N/A return cipher.wrap(key);
0N/A }
0N/A
0N/A Key implUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
0N/A int wrappedKeyType)
0N/A throws InvalidKeyException, NoSuchAlgorithmException {
0N/A return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
0N/A wrappedKeyType);
0N/A }
0N/A
0N/A public static final class PBEWithSHA1AndDESede extends CipherSpi {
0N/A private final PKCS12PBECipherCore core;
0N/A public PBEWithSHA1AndDESede() throws NoSuchAlgorithmException {
0N/A core = new PKCS12PBECipherCore("DESede", 24);
0N/A }
0N/A protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
0N/A throws IllegalBlockSizeException, BadPaddingException {
0N/A return core.implDoFinal(in, inOff, inLen);
0N/A }
0N/A protected int engineDoFinal(byte[] in, int inOff, int inLen,
0N/A byte[] out, int outOff)
0N/A throws ShortBufferException, IllegalBlockSizeException,
0N/A BadPaddingException {
0N/A return core.implDoFinal(in, inOff, inLen, out, outOff);
0N/A }
0N/A protected int engineGetBlockSize() {
0N/A return core.implGetBlockSize();
0N/A }
0N/A protected byte[] engineGetIV() {
0N/A return core.implGetIV();
0N/A }
0N/A protected int engineGetKeySize(Key key) throws InvalidKeyException {
0N/A return core.implGetKeySize(key);
0N/A }
0N/A protected int engineGetOutputSize(int inLen) {
0N/A return core.implGetOutputSize(inLen);
0N/A }
0N/A protected AlgorithmParameters engineGetParameters() {
0N/A return core.implGetParameters();
0N/A }
0N/A protected void engineInit(int opmode, Key key,
0N/A AlgorithmParameterSpec params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A core.implInit(opmode, key, params, random);
0N/A }
0N/A protected void engineInit(int opmode, Key key,
0N/A AlgorithmParameters params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A core.implInit(opmode, key, params, random);
0N/A }
0N/A protected void engineInit(int opmode, Key key, SecureRandom random)
0N/A throws InvalidKeyException {
0N/A core.implInit(opmode, key, random);
0N/A }
0N/A protected void engineSetMode(String mode)
0N/A throws NoSuchAlgorithmException {
0N/A core.implSetMode(mode);
0N/A }
0N/A protected void engineSetPadding(String paddingScheme)
0N/A throws NoSuchPaddingException {
0N/A core.implSetPadding(paddingScheme);
0N/A }
0N/A protected Key engineUnwrap(byte[] wrappedKey,
0N/A String wrappedKeyAlgorithm,
0N/A int wrappedKeyType)
0N/A throws InvalidKeyException, NoSuchAlgorithmException {
0N/A return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
0N/A wrappedKeyType);
0N/A }
0N/A protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
0N/A return core.implUpdate(in, inOff, inLen);
0N/A }
0N/A protected int engineUpdate(byte[] in, int inOff, int inLen,
0N/A byte[] out, int outOff)
0N/A throws ShortBufferException {
0N/A return core.implUpdate(in, inOff, inLen, out, outOff);
0N/A }
0N/A protected byte[] engineWrap(Key key)
0N/A throws IllegalBlockSizeException, InvalidKeyException {
0N/A return core.implWrap(key);
0N/A }
0N/A }
0N/A
0N/A public static final class PBEWithSHA1AndRC2_40 extends CipherSpi {
0N/A private final PKCS12PBECipherCore core;
0N/A public PBEWithSHA1AndRC2_40() throws NoSuchAlgorithmException {
0N/A core = new PKCS12PBECipherCore("RC2", 5);
0N/A }
0N/A protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
0N/A throws IllegalBlockSizeException, BadPaddingException {
0N/A return core.implDoFinal(in, inOff, inLen);
0N/A }
0N/A protected int engineDoFinal(byte[] in, int inOff, int inLen,
0N/A byte[] out, int outOff)
0N/A throws ShortBufferException, IllegalBlockSizeException,
0N/A BadPaddingException {
0N/A return core.implDoFinal(in, inOff, inLen, out, outOff);
0N/A }
0N/A protected int engineGetBlockSize() {
0N/A return core.implGetBlockSize();
0N/A }
0N/A protected byte[] engineGetIV() {
0N/A return core.implGetIV();
0N/A }
0N/A protected int engineGetKeySize(Key key) throws InvalidKeyException {
0N/A return core.implGetKeySize(key);
0N/A }
0N/A protected int engineGetOutputSize(int inLen) {
0N/A return core.implGetOutputSize(inLen);
0N/A }
0N/A protected AlgorithmParameters engineGetParameters() {
0N/A return core.implGetParameters();
0N/A }
0N/A protected void engineInit(int opmode, Key key,
0N/A AlgorithmParameterSpec params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A core.implInit(opmode, key, params, random);
0N/A }
0N/A protected void engineInit(int opmode, Key key,
0N/A AlgorithmParameters params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A core.implInit(opmode, key, params, random);
0N/A }
0N/A protected void engineInit(int opmode, Key key, SecureRandom random)
0N/A throws InvalidKeyException {
0N/A core.implInit(opmode, key, random);
0N/A }
0N/A protected void engineSetMode(String mode)
0N/A throws NoSuchAlgorithmException {
0N/A core.implSetMode(mode);
0N/A }
0N/A protected void engineSetPadding(String paddingScheme)
0N/A throws NoSuchPaddingException {
0N/A core.implSetPadding(paddingScheme);
0N/A }
0N/A protected Key engineUnwrap(byte[] wrappedKey,
0N/A String wrappedKeyAlgorithm,
0N/A int wrappedKeyType)
0N/A throws InvalidKeyException, NoSuchAlgorithmException {
0N/A return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
0N/A wrappedKeyType);
0N/A }
0N/A protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
0N/A return core.implUpdate(in, inOff, inLen);
0N/A }
0N/A protected int engineUpdate(byte[] in, int inOff, int inLen,
0N/A byte[] out, int outOff)
0N/A throws ShortBufferException {
0N/A return core.implUpdate(in, inOff, inLen, out, outOff);
0N/A }
0N/A protected byte[] engineWrap(Key key)
0N/A throws IllegalBlockSizeException, InvalidKeyException {
0N/A return core.implWrap(key);
0N/A }
0N/A }
0N/A}