| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.crypto.provider;  | 
 | 
 | 
 | 
import java.security.*;  | 
 | 
import java.security.spec.*;  | 
 | 
import java.util.Arrays;  | 
 | 
import javax.crypto.*;  | 
 | 
import javax.crypto.spec.*;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
abstract class PBES2Core extends CipherSpi { | 
 | 
 | 
 | 
    private static final int DEFAULT_SALT_LENGTH = 20;  | 
 | 
    private static final int DEFAULT_COUNT = 4096;  | 
 | 
 | 
 | 
      | 
 | 
    private final CipherCore cipher;  | 
 | 
    private final int keyLength;   | 
 | 
    private final int blkSize;   | 
 | 
    private final PBKDF2Core kdf;  | 
 | 
    private final String pbeAlgo;  | 
 | 
    private final String cipherAlgo;  | 
 | 
    private int iCount = DEFAULT_COUNT;  | 
 | 
    private byte[] salt = null;  | 
 | 
    private IvParameterSpec ivSpec = null;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    PBES2Core(String kdfAlgo, String cipherAlgo, int keySize)  | 
 | 
        throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
 | 
 | 
        this.cipherAlgo = cipherAlgo;  | 
 | 
        keyLength = keySize * 8;  | 
 | 
        pbeAlgo = "PBEWith" + kdfAlgo + "And" + cipherAlgo + "_" + keyLength;  | 
 | 
 | 
 | 
        if (cipherAlgo.equals("AES")) { | 
 | 
            blkSize = AESConstants.AES_BLOCK_SIZE;  | 
 | 
            cipher = new CipherCore(new AESCrypt(), blkSize);  | 
 | 
 | 
 | 
            switch(kdfAlgo) { | 
 | 
            case "HmacSHA1":  | 
 | 
                kdf = new PBKDF2Core.HmacSHA1();  | 
 | 
                break;  | 
 | 
            case "HmacSHA224":  | 
 | 
                kdf = new PBKDF2Core.HmacSHA224();  | 
 | 
                break;  | 
 | 
            case "HmacSHA256":  | 
 | 
                kdf = new PBKDF2Core.HmacSHA256();  | 
 | 
                break;  | 
 | 
            case "HmacSHA384":  | 
 | 
                kdf = new PBKDF2Core.HmacSHA384();  | 
 | 
                break;  | 
 | 
            case "HmacSHA512":  | 
 | 
                kdf = new PBKDF2Core.HmacSHA512();  | 
 | 
                break;  | 
 | 
            default:  | 
 | 
                throw new NoSuchAlgorithmException(  | 
 | 
                    "No Cipher implementation for " + kdfAlgo);  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            throw new NoSuchAlgorithmException("No Cipher implementation for " + | 
 | 
                                               pbeAlgo);  | 
 | 
        }  | 
 | 
        cipher.setMode("CBC"); | 
 | 
        cipher.setPadding("PKCS5Padding"); | 
 | 
    }  | 
 | 
 | 
 | 
    protected void engineSetMode(String mode) throws NoSuchAlgorithmException { | 
 | 
        if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) { | 
 | 
            throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void engineSetPadding(String paddingScheme)  | 
 | 
        throws NoSuchPaddingException { | 
 | 
        if ((paddingScheme != null) &&  | 
 | 
            (!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) { | 
 | 
            throw new NoSuchPaddingException("Invalid padding scheme: " + | 
 | 
                                             paddingScheme);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int engineGetBlockSize() { | 
 | 
        return blkSize;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int engineGetOutputSize(int inputLen) { | 
 | 
        return cipher.getOutputSize(inputLen);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected byte[] engineGetIV() { | 
 | 
        return cipher.getIV();  | 
 | 
    }  | 
 | 
 | 
 | 
    protected AlgorithmParameters engineGetParameters() { | 
 | 
        AlgorithmParameters params = null;  | 
 | 
        if (salt == null) { | 
 | 
              | 
 | 
            salt = new byte[DEFAULT_SALT_LENGTH];  | 
 | 
            SunJCE.getRandom().nextBytes(salt);  | 
 | 
            iCount = DEFAULT_COUNT;  | 
 | 
        }  | 
 | 
        if (ivSpec == null) { | 
 | 
              | 
 | 
            byte[] ivBytes = new byte[blkSize];  | 
 | 
            SunJCE.getRandom().nextBytes(ivBytes);  | 
 | 
            ivSpec = new IvParameterSpec(ivBytes);  | 
 | 
        }  | 
 | 
        PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount, ivSpec);  | 
 | 
        try { | 
 | 
            params = AlgorithmParameters.getInstance(pbeAlgo,  | 
 | 
                SunJCE.getInstance());  | 
 | 
            params.init(pbeSpec);  | 
 | 
        } catch (NoSuchAlgorithmException nsae) { | 
 | 
              | 
 | 
            throw new RuntimeException("SunJCE called, but not configured"); | 
 | 
        } catch (InvalidParameterSpecException ipse) { | 
 | 
              | 
 | 
            throw new RuntimeException("PBEParameterSpec not supported"); | 
 | 
        }  | 
 | 
        return params;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void engineInit(int opmode, Key key, SecureRandom random)  | 
 | 
        throws InvalidKeyException { | 
 | 
        try { | 
 | 
            engineInit(opmode, key, (AlgorithmParameterSpec) null, random);  | 
 | 
        } catch (InvalidAlgorithmParameterException ie) { | 
 | 
            InvalidKeyException ike =  | 
 | 
                new InvalidKeyException("requires PBE parameters"); | 
 | 
            ike.initCause(ie);  | 
 | 
            throw ike;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void engineInit(int opmode, Key key,  | 
 | 
                              AlgorithmParameterSpec params,  | 
 | 
                              SecureRandom random)  | 
 | 
        throws InvalidKeyException, InvalidAlgorithmParameterException { | 
 | 
 | 
 | 
        if (key == null) { | 
 | 
            throw new InvalidKeyException("Null key"); | 
 | 
        }  | 
 | 
 | 
 | 
        byte[] passwdBytes = key.getEncoded();  | 
 | 
        char[] passwdChars = null;  | 
 | 
        PBEKeySpec pbeSpec;  | 
 | 
        try { | 
 | 
            if ((passwdBytes == null) ||  | 
 | 
                    !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) { | 
 | 
                throw new InvalidKeyException("Missing password"); | 
 | 
            }  | 
 | 
 | 
 | 
            // TBD: consolidate the salt, ic and IV parameter checks below  | 
 | 
 | 
 | 
              | 
 | 
            if (key instanceof javax.crypto.interfaces.PBEKey) { | 
 | 
                salt = ((javax.crypto.interfaces.PBEKey)key).getSalt();  | 
 | 
                if (salt != null && salt.length < 8) { | 
 | 
                    throw new InvalidAlgorithmParameterException(  | 
 | 
                            "Salt must be at least 8 bytes long");  | 
 | 
                }  | 
 | 
                iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount();  | 
 | 
                if (iCount == 0) { | 
 | 
                    iCount = DEFAULT_COUNT;  | 
 | 
                } else if (iCount < 0) { | 
 | 
                    throw new InvalidAlgorithmParameterException(  | 
 | 
                            "Iteration count must be a positive number");  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (params == null) { | 
 | 
                if (salt == null) { | 
 | 
                      | 
 | 
                    salt = new byte[DEFAULT_SALT_LENGTH];  | 
 | 
                    random.nextBytes(salt);  | 
 | 
                    iCount = DEFAULT_COUNT;  | 
 | 
                }  | 
 | 
                if ((opmode == Cipher.ENCRYPT_MODE) ||  | 
 | 
                        (opmode == Cipher.WRAP_MODE)) { | 
 | 
                      | 
 | 
                    byte[] ivBytes = new byte[blkSize];  | 
 | 
                    random.nextBytes(ivBytes);  | 
 | 
                    ivSpec = new IvParameterSpec(ivBytes);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                if (!(params instanceof PBEParameterSpec)) { | 
 | 
                    throw new InvalidAlgorithmParameterException  | 
 | 
                            ("Wrong parameter type: PBE expected"); | 
 | 
                }  | 
 | 
                  | 
 | 
                byte[] specSalt = ((PBEParameterSpec) params).getSalt();  | 
 | 
                if (specSalt != null && specSalt.length < 8) { | 
 | 
                    throw new InvalidAlgorithmParameterException(  | 
 | 
                            "Salt must be at least 8 bytes long");  | 
 | 
                }  | 
 | 
                salt = specSalt;  | 
 | 
                int specICount = ((PBEParameterSpec) params).getIterationCount();  | 
 | 
                if (specICount == 0) { | 
 | 
                    specICount = DEFAULT_COUNT;  | 
 | 
                } else if (specICount < 0) { | 
 | 
                    throw new InvalidAlgorithmParameterException(  | 
 | 
                            "Iteration count must be a positive number");  | 
 | 
                }  | 
 | 
                iCount = specICount;  | 
 | 
 | 
 | 
                AlgorithmParameterSpec specParams =  | 
 | 
                        ((PBEParameterSpec) params).getParameterSpec();  | 
 | 
                if (specParams != null) { | 
 | 
                    if (specParams instanceof IvParameterSpec) { | 
 | 
                        ivSpec = (IvParameterSpec)specParams;  | 
 | 
                    } else { | 
 | 
                        throw new InvalidAlgorithmParameterException(  | 
 | 
                                "Wrong parameter type: IV expected");  | 
 | 
                    }  | 
 | 
                } else if ((opmode == Cipher.ENCRYPT_MODE) ||  | 
 | 
                        (opmode == Cipher.WRAP_MODE)) { | 
 | 
                      | 
 | 
                    byte[] ivBytes = new byte[blkSize];  | 
 | 
                    random.nextBytes(ivBytes);  | 
 | 
                    ivSpec = new IvParameterSpec(ivBytes);  | 
 | 
                } else { | 
 | 
                    throw new InvalidAlgorithmParameterException(  | 
 | 
                            "Missing parameter type: IV expected");  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            passwdChars = new char[passwdBytes.length];  | 
 | 
            for (int i = 0; i < passwdChars.length; i++)  | 
 | 
                passwdChars[i] = (char) (passwdBytes[i] & 0x7f);  | 
 | 
 | 
 | 
            pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength);  | 
 | 
            // password char[] was cloned in PBEKeySpec constructor,  | 
 | 
            // so we can zero it out here  | 
 | 
        } finally { | 
 | 
            if (passwdChars != null) Arrays.fill(passwdChars, '\0');  | 
 | 
            if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00);  | 
 | 
        }  | 
 | 
 | 
 | 
        SecretKey s = null;  | 
 | 
 | 
 | 
        try { | 
 | 
            s = kdf.engineGenerateSecret(pbeSpec);  | 
 | 
 | 
 | 
        } catch (InvalidKeySpecException ikse) { | 
 | 
            InvalidKeyException ike =  | 
 | 
                new InvalidKeyException("Cannot construct PBE key"); | 
 | 
            ike.initCause(ikse);  | 
 | 
            throw ike;  | 
 | 
        }  | 
 | 
        byte[] derivedKey = s.getEncoded();  | 
 | 
        SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);  | 
 | 
 | 
 | 
          | 
 | 
        cipher.init(opmode, cipherKey, ivSpec, random);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void engineInit(int opmode, Key key, AlgorithmParameters params,  | 
 | 
                              SecureRandom random)  | 
 | 
        throws InvalidKeyException, InvalidAlgorithmParameterException { | 
 | 
        AlgorithmParameterSpec pbeSpec = null;  | 
 | 
        if (params != null) { | 
 | 
            try { | 
 | 
                pbeSpec = params.getParameterSpec(PBEParameterSpec.class);  | 
 | 
            } catch (InvalidParameterSpecException ipse) { | 
 | 
                throw new InvalidAlgorithmParameterException(  | 
 | 
                    "Wrong parameter type: PBE expected");  | 
 | 
            }  | 
 | 
        }  | 
 | 
        engineInit(opmode, key, pbeSpec, random);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { | 
 | 
        return cipher.update(input, inputOffset, inputLen);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int engineUpdate(byte[] input, int inputOffset, int inputLen,  | 
 | 
                               byte[] output, int outputOffset)  | 
 | 
        throws ShortBufferException { | 
 | 
        return cipher.update(input, inputOffset, inputLen,  | 
 | 
                             output, outputOffset);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)  | 
 | 
        throws IllegalBlockSizeException, BadPaddingException { | 
 | 
        return cipher.doFinal(input, inputOffset, inputLen);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,  | 
 | 
                                byte[] output, int outputOffset)  | 
 | 
        throws ShortBufferException, IllegalBlockSizeException,  | 
 | 
               BadPaddingException { | 
 | 
        return cipher.doFinal(input, inputOffset, inputLen,  | 
 | 
                              output, outputOffset);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int engineGetKeySize(Key key) throws InvalidKeyException { | 
 | 
        return keyLength;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected byte[] engineWrap(Key key)  | 
 | 
        throws IllegalBlockSizeException, InvalidKeyException { | 
 | 
        return cipher.wrap(key);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,  | 
 | 
                               int wrappedKeyType)  | 
 | 
        throws InvalidKeyException, NoSuchAlgorithmException { | 
 | 
        byte[] encodedKey;  | 
 | 
        return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,  | 
 | 
                             wrappedKeyType);  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA1AndAES_128 extends PBES2Core { | 
 | 
        public HmacSHA1AndAES_128()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA1", "AES", 16); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA224AndAES_128 extends PBES2Core { | 
 | 
        public HmacSHA224AndAES_128()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA224", "AES", 16); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA256AndAES_128 extends PBES2Core { | 
 | 
        public HmacSHA256AndAES_128()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA256", "AES", 16); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA384AndAES_128 extends PBES2Core { | 
 | 
        public HmacSHA384AndAES_128()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA384", "AES", 16); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA512AndAES_128 extends PBES2Core { | 
 | 
        public HmacSHA512AndAES_128()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA512", "AES", 16); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA1AndAES_256 extends PBES2Core { | 
 | 
        public HmacSHA1AndAES_256()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA1", "AES", 32); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA224AndAES_256 extends PBES2Core { | 
 | 
        public HmacSHA224AndAES_256()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA224", "AES", 32); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA256AndAES_256 extends PBES2Core { | 
 | 
        public HmacSHA256AndAES_256()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA256", "AES", 32); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA384AndAES_256 extends PBES2Core { | 
 | 
        public HmacSHA384AndAES_256()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA384", "AES", 32); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public static final class HmacSHA512AndAES_256 extends PBES2Core { | 
 | 
        public HmacSHA512AndAES_256()  | 
 | 
            throws NoSuchAlgorithmException, NoSuchPaddingException { | 
 | 
            super("HmacSHA512", "AES", 32); | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |