如何在Java中为Serpent生成PBEKey?

时间:2011-08-05 08:24:46

标签: java cryptography bouncycastle

我正在尝试为AES,Serpent和TwoFish支持PBE。目前我可以使用BC生成Java中的AES PBEKey,如下所示:

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC", provider);
PBEKeySpec pbeKeySpec = new PBEKeySpec("Password12".toCharArray());
SecretKey key = factory.generateSecret(pbeKeySpec);

但是我无法弄清楚如何为Serpent生成PBEKey,所以我认为它不可能开箱即用。我将如何实现这一目标?有什么钩子我可以注册我自己的SecretKeyFactory来处理Serpent键吗?

巧合的是,我注意到使用AES PBEKey(如上所述)使用Serpent / TwoFish加密/解密“工作”,但我不知道会产生什么影响。我可以直接使用AES PBEKey吗?

1 个答案:

答案 0 :(得分:1)

在与PaŭloEbermann(上图)讨论后,我将以下解决方案放在一起。它为AES256生成一个PBE密钥,然后简单地将生成的密钥所需的字节数复制到一个新的SecretKeySpec()中,这允许我指定所需的算法和密钥长度。目前我正在使用密码并在每次加密调用时创建随机IV。我的假设是IV是不必要的,因为随机盐被应用于每个加密的消息,但我不是100%肯定所以我无论如何我添加了IV。我希望有人可以确认或否认这个假设,因为如果不需要IV,那么加密()的输出大小没有正当理由。理想情况下,我可以生成一个可变长度的PBEKey,没有算法关系(按照PKCS5),但看起来我受限于所选提供商提供的可用PBE方案中定义的密钥大小。因此,此实现必然使用BouncyCastle,因为我无法找到从标准JCE提供程序提供至少256位密钥的PBE方案。

/**
 * parts of this code were copied from the StandardPBEByteEncryptor class from the Jasypt (www.jasypt.org) project
 */
public class PBESample {
    private final String KEY_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
    private final String MODE_PADDING = "/CBC/PKCS5Padding";
    private final int DEFAULT_SALT_SIZE_BYTES = 16;

    private final SecureRandom rand;

    private final String passwd = "(Password){12}<.....>!";

    public PBESample() throws Exception {
        rand = SecureRandom.getInstance("SHA1PRNG");
    }

    private byte[] generateSalt(int size) {
        byte[] salt = new byte[size];
        rand.nextBytes(salt);

        return salt;
    }

    private SecretKey generateKey(String algorithm, int keySize, byte[] salt) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException{
        SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
        PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray(), salt, 100000);
        SecretKey tmpKey = factory.generateSecret(pbeKeySpec);
        byte[] keyBytes = new byte[keySize / 8];
        System.arraycopy(tmpKey.getEncoded(), 0, keyBytes, 0, keyBytes.length);

        return new SecretKeySpec(keyBytes, algorithm);
    }

    private byte[] generateIV(Cipher cipher) {
        byte[] iv = new byte[cipher.getBlockSize()];
        rand.nextBytes(iv);

        return iv;
    }

    private byte[] appendArrays(byte[] firstArray, byte[] secondArray) {
        final byte[] result = new byte[firstArray.length + secondArray.length];

        System.arraycopy(firstArray, 0, result, 0, firstArray.length);
        System.arraycopy(secondArray, 0, result, firstArray.length, secondArray.length);

            return result;
    }


    public byte[] encrypt(String algorithm, int keySize, final byte[] message) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm + MODE_PADDING);

        // The salt size for the chosen algorithm is set to be equal 
        // to the algorithm's block size (if it is a block algorithm).
        int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;
        int algorithmBlockSize = cipher.getBlockSize();
        if (algorithmBlockSize > 0) {
            saltSizeBytes = algorithmBlockSize;
        }

        // Create salt
        final byte[] salt = generateSalt(saltSizeBytes);

        SecretKey key = generateKey(algorithm, keySize, salt);

        // create a new IV for each encryption
        final IvParameterSpec ivParamSpec = new IvParameterSpec(generateIV(cipher));

        // Perform encryption using the Cipher
        cipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec);
        byte[] encryptedMessage = cipher.doFinal(message);

        // append the IV and salt
        encryptedMessage = appendArrays(ivParamSpec.getIV(), encryptedMessage);
        encryptedMessage = appendArrays(salt, encryptedMessage);

        return encryptedMessage;
    }

    public byte[] decrypt(String algorithm, int keySize, final byte[] encryptedMessage) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm + MODE_PADDING);

        // determine the salt size for the first layer of encryption
        int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;
        int algorithmBlockSize = cipher.getBlockSize();
        if (algorithmBlockSize > 0) {
            saltSizeBytes = algorithmBlockSize;
        }

        byte[] decryptedMessage = new byte[encryptedMessage.length];
        System.arraycopy(encryptedMessage, 0, decryptedMessage, 0, encryptedMessage.length);

        // extract the salt and IV from the incoming message
        byte[] salt = null;
        byte[] iv = null;
        byte[] encryptedMessageKernel = null;
        final int saltStart = 0;
        final int saltSize = (saltSizeBytes < decryptedMessage.length ? saltSizeBytes : decryptedMessage.length);
        final int ivStart = (saltSizeBytes < decryptedMessage.length ? saltSizeBytes : decryptedMessage.length);
        final int ivSize = cipher.getBlockSize();
        final int encMesKernelStart = (saltSizeBytes + ivSize < decryptedMessage.length ? saltSizeBytes + ivSize : decryptedMessage.length);
        final int encMesKernelSize = (saltSizeBytes + ivSize < decryptedMessage.length ? (decryptedMessage.length - saltSizeBytes - ivSize) : 0);

        salt = new byte[saltSize];
        iv = new byte[ivSize];
        encryptedMessageKernel = new byte[encMesKernelSize];

        System.arraycopy(decryptedMessage, saltStart, salt, 0, saltSize);
        System.arraycopy(decryptedMessage, ivStart, iv, 0, ivSize);
        System.arraycopy(decryptedMessage, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);

        SecretKey key = generateKey(algorithm, keySize, salt);

        IvParameterSpec ivParamSpec = new IvParameterSpec(iv);

        // Perform decryption using the Cipher
        cipher.init(Cipher.DECRYPT_MODE, key, ivParamSpec);
        decryptedMessage = cipher.doFinal(encryptedMessageKernel);

        // Return the results
        return decryptedMessage;
    }

    public static void main(String[] args) throws Exception {
        // allow the use of the BC JCE
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        final String message = "Secret Message";
        PBESample engine = new PBESample();

        byte[] encryptedMessage = engine.encrypt("AES", 128, message.getBytes());
        byte[] decryptedMessage = engine.decrypt("AES", 128, encryptedMessage);
        if (message.equals(new String(decryptedMessage))) {
            System.out.println("AES OK");
        }

        encryptedMessage = engine.encrypt("Serpent", 256, message.getBytes());
        decryptedMessage = engine.decrypt("Serpent", 256, encryptedMessage);
        if (message.equals(new String(decryptedMessage))) {
            System.out.println("Serpent OK");
        }

        encryptedMessage = engine.encrypt("TwoFish", 256, message.getBytes());
        decryptedMessage = engine.decrypt("TwoFish", 256, encryptedMessage);
        if (message.equals(new String(decryptedMessage))) {
            System.out.println("TwoFish OK");
        }
    }
}