当前位置: 首页 > 知识库问答 >
问题:

使用SecureRandom生成AES密钥和IV的替代方法

党航
2023-03-14

我正在尝试使用AES加密算法加密文本,将此加密文本保存到文件中,然后稍后重新打开并解密这些文本。下面是我的加密和解密逻辑

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    byte[] stringBytes = clear_text.getBytes();
    byte[] raw = cipher.doFinal(stringBytes);
    return Base64.encodeBase64String(raw);

这就是解密逻辑

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, key, iv);
    byte[] raw = Base64.decodeBase64(encText);
    byte[] stringBytes = cipher.doFinal(raw);
    String clear_text = new String(stringBytes, "UTF8");
    return clear_text;

我收到一个“大小错误”异常。我的猜测是,通过使用 SecureRandom 类,这两种方法在加密或欺骗文本时使用不同的密钥。有没有办法在两个例程中使用相同的密钥?

共有1个答案

澹台臻
2023-03-14

是的,您可以使用相同的密钥;甚至需要使用相同的密钥。但是,您永远不应该使用相同的密钥/ IV组合,因为这不安全。因此,IV通常以密文为前缀。

请注意,以下实现向您展示了如何在没有SecureRandom的情况下生成随机IV,但这有点不明智,因为Cipher类只会在内部使用默认的IV来创建IV。对于CBC,攻击者可能知道IV,但攻击者应该无法将其与随机数据区分开来。

在这个例子中,密钥数据简单地存储在一个“常量”中。将密钥存储在源代码中可能无法提供足够的安全性。相反,它通常用公共密钥、密码加密,存储在USB密钥上,存储在智能卡或HSM等中。等等。然而,密钥管理是一个庞大的主题,所以我不会在这个回答中进一步讨论它。

但是,在 Java 中,您应该使用密钥/密钥从已知数据创建密钥,并使用 Iv 参数为已知的 IV(或随机)创建密钥。

import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidParameterSpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESWithStaticKeyAndRandomIV {

    private static byte[] KEY = new byte[] { (byte) 0x14, (byte) 0x0b,
            (byte) 0x41, (byte) 0xb2, (byte) 0x2a, (byte) 0x29, (byte) 0xbe,
            (byte) 0xb4, (byte) 0x06, (byte) 0x1b, (byte) 0xda, (byte) 0x66,
            (byte) 0xb6, (byte) 0x74, (byte) 0x7e, (byte) 0x14 };

    public static byte[] encrypt(byte[] plaintext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKey key = new SecretKeySpec(KEY, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            AlgorithmParameters params = cipher.getParameters();
            byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();

            byte[] ciphertext = new byte[iv.length
                    + cipher.getOutputSize(plaintext.length)];
            System.arraycopy(iv, 0, ciphertext, 0, iv.length);
            cipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
                    iv.length);
            return ciphertext;
        } catch (InvalidKeyException | NoSuchAlgorithmException
                | NoSuchPaddingException | InvalidParameterSpecException
                | ShortBufferException | IllegalBlockSizeException
                | BadPaddingException e) {
            throw new IllegalStateException(
                    "CBC encryption with standard algorithm should never fail",
                    e);
        }
    }

    public static byte[] decrypt(byte[] ciphertext) throws IllegalBlockSizeException,
            BadPaddingException {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec key = new SecretKeySpec(KEY, "AES");

            if (ciphertext.length < cipher.getBlockSize()) {
                throw new IllegalArgumentException(
                        "Ciphertext too small to contain IV");
            }

            IvParameterSpec ivSpec = new IvParameterSpec(ciphertext, 0,
                    cipher.getBlockSize());
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

            byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length
                    - cipher.getBlockSize())];
            cipher.doFinal(ciphertext, cipher.getBlockSize(), ciphertext.length
                    - cipher.getBlockSize(), plaintext, 0);
            return plaintext;
        } catch (InvalidKeyException | NoSuchAlgorithmException
                | NoSuchPaddingException | ShortBufferException
                | InvalidAlgorithmParameterException e) {
            throw new IllegalStateException(
                    "CBC decryption with standard algorithm should be available",
                    e);
        }
    }

    public static void main(String[] args) throws Exception {
           byte[] plaintext = decrypt(encrypt("owlstead".getBytes(StandardCharsets.UTF_8)));
           System.out.println(new String(plaintext, StandardCharsets.UTF_8));
    }
}

使用密钥存储(您现在必须使用JCEKS):

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidParameterSpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESWithStaticKeyAndRandomIV {

    private static final String KEY_ALIAS = "secret";

    private static byte[] KEY = new byte[] { (byte) 0x14, (byte) 0x0b,
            (byte) 0x41, (byte) 0xb2, (byte) 0x2a, (byte) 0x29, (byte) 0xbe,
            (byte) 0xb4, (byte) 0x06, (byte) 0x1b, (byte) 0xda, (byte) 0x66,
            (byte) 0xb6, (byte) 0x74, (byte) 0x7e, (byte) 0x14 };

    private static ProtectionParameter PASSWORD = new KeyStore.PasswordProtection(
            new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'});

    private final KeyStore store;

    private AESWithStaticKeyAndRandomIV(KeyStore store) {
        this.store = store;
    }

    public byte[] encrypt(byte[] plaintext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKey key;
            try {
                key = ((SecretKeyEntry) store.getEntry(KEY_ALIAS, PASSWORD))
                        .getSecretKey();
            } catch (UnrecoverableEntryException | KeyStoreException e) {
                throw new IllegalStateException("What key?", e);
            }
            cipher.init(Cipher.ENCRYPT_MODE, key);

            AlgorithmParameters params = cipher.getParameters();
            byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();

            byte[] ciphertext = new byte[iv.length
                    + cipher.getOutputSize(plaintext.length)];
            System.arraycopy(iv, 0, ciphertext, 0, iv.length);
            cipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
                    iv.length);
            return ciphertext;
        } catch (InvalidKeyException | NoSuchAlgorithmException
                | NoSuchPaddingException | InvalidParameterSpecException
                | ShortBufferException | IllegalBlockSizeException
                | BadPaddingException e) {
            throw new IllegalStateException(
                    "CBC encryption with standard algorithm should never fail",
                    e);
        }
    }

    public byte[] decrypt(byte[] ciphertext) throws IllegalBlockSizeException,
            BadPaddingException {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKey key;
            try {
                key = ((SecretKeyEntry) store.getEntry(KEY_ALIAS, PASSWORD))
                        .getSecretKey();
            } catch (UnrecoverableEntryException | KeyStoreException e) {
                throw new IllegalStateException("What key?", e);
            }

            if (ciphertext.length < cipher.getBlockSize()) {
                throw new IllegalArgumentException(
                        "Ciphertext too small to contain IV");
            }

            IvParameterSpec ivSpec = new IvParameterSpec(ciphertext, 0,
                    cipher.getBlockSize());
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

            byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length
                    - cipher.getBlockSize())];
            cipher.doFinal(ciphertext, cipher.getBlockSize(), ciphertext.length
                    - cipher.getBlockSize(), plaintext, 0);
            return plaintext;
        } catch (InvalidKeyException | NoSuchAlgorithmException
                | NoSuchPaddingException | ShortBufferException
                | InvalidAlgorithmParameterException e) {
            throw new IllegalStateException(
                    "CBC decryption with standard algorithm should be available",
                    e);
        }
    }

    public static KeyStore createStoreWithSecretKey() {

        try {
            KeyStore keyStore = KeyStore.getInstance("JCEKS");
            keyStore.load(null);
            SecretKey key = new SecretKeySpec(KEY, "AES");
            keyStore.setEntry(KEY_ALIAS, new KeyStore.SecretKeyEntry(key), PASSWORD);
            return keyStore;
        } catch (KeyStoreException | NoSuchAlgorithmException
                | CertificateException | IOException e) {
            throw new IllegalStateException("Unable to create key store", e);
        }
    }

    public static void main(String[] args) throws Exception {
        AESWithStaticKeyAndRandomIV crypt = new AESWithStaticKeyAndRandomIV(
                createStoreWithSecretKey());

        byte[] plaintext = crypt.decrypt(crypt.encrypt("owlstead"
                .getBytes(StandardCharsets.UTF_8)));
        System.out.println(new String(plaintext, StandardCharsets.UTF_8));
    }
}
 类似资料:
  • 使用EVP_BytesToKey()返回错误的key和iv可能出了什么问题? 我试过用iter计数值做实验,但似乎没有一个能产生工作键和IV。我假设命令行默认的iter计数是1。 同样确认的是,如果我用命令行显示的工作键和iv覆盖从EVP_BytesToKey()返回的内容和硬代码无符号char数组,我的其余代码工作正常,解密正确。 有人能帮忙吗?

  • 我正在尝试用Java编写一个简单的密码管理器。我想用AES 256位加密用存储的密码加密文件。此外,我希望用户能够解密的文件与密码。当阅读其他在线帖子时,他们几乎都强调简单地使用密码作为密钥是不安全的,他们提到使用随机盐来增加安全性。但我不明白如何在生成密钥时使用随机盐。如果我从用户的密码和随机的salt创建密钥,那么当他们试图解密他们的文件时,我怎么知道salt是什么呢?这让我完全糊涂了。 目前

  • 我从Python得到一个加密的base64字符串。 格式是AES 256 CBC,但当我尝试使用Android解密时,它将解密字符串返回为nil。 这里我使用https://github.com/fukata/aes-256-cbc-example

  • 问题内容: 关于AES 256加密: 什么是公钥和私钥? 如何生成这两个密钥? 如何使用公众加密数据? 如何使用私有解密数据? 问题答案: 在.Net中,您可以这样创建密钥对: 然后,您可以使用公共密钥对消息进行加密,如下所示: 并使用私钥像这样解密:

  • 问题内容: 我需要将加密的数据从客户端发送到服务器。现在,我正在学习如何使用(要求)加密数据。按照这个公认的答案使用AES进行android加密/解密,我正在执行以下操作: 由于该算法使用,我不确定如果没有,是否可以在甚至其他程序中对此进行解码。 这种加密/解密是否仅在知道的值的情况下工作,或者由于我正在使用,我仍然需要传递其他内容才能解密? 另外,还有更好的方法吗?或者这还好吗? 问题答案: 不

  • 问题内容: 我在下面的(E.1)中使用它来进行我的应用程序,显然我认识并理解了其中的一个巨大的安全漏洞。我对加密越来越感兴趣,并且想更好地理解它,我需要随IV一起生成一个随机密钥,但是不确定如何正确地执行此操作。有人可以向我解释一下熟悉AES加密的工作原理(IV和KEY)吗? )因此,我将来能够更好地理解并可以运用我的知识,本质上,我只是想使代码更安全,谢谢。 (E.1) 问题答案: AES密钥仅