AES 加密 IV

2022-01-10 00:00:00 byte encryption cryptography aes java

我在我的应用程序中使用下面的 (E.1),显然我认识到并理解其中存在一个巨大的明显安全漏洞.我对加密越来越感兴趣并想更好地理解它,我需要生成一个随机密钥和一个 IV,但我不确定如何正确地这样做KEY) 所以我以后能更好的理解和应用我的知识,本质上我只是想让代码更安全,谢谢.

I am using this below (E.1) for my application, there is obviously a huge glaring security hole in this that I recognize and understand. I have grown interested in encryption and want to understand it better, I need to generate a random key along with an IV but am unsure how to do so properly Can someone explain to me whom is familiar with AES encryption how this works (IV & KEY) So I am better able to understand in the future and can apply my knowledge, essentially I just want to make the code more secure, thank you.

(E.1)

    byte[] key = "mykey".getBytes("UTF-8");

    private byte[] getKeyBytes(final byte[] key) throws Exception {
        byte[] keyBytes = new byte[16];
        System.arraycopy(key, 0, keyBytes, 0, Math.min(key.length, keyBytes.length));
        return keyBytes;
    }

    public Cipher getCipherEncrypt(final byte[] key) throws Exception {
        byte[] keyBytes = getKeyBytes(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher;
    }

    public void encrypt(File in, File output, byte[] key) throws Exception {

        Cipher cipher = getCipherEncrypt(key);
        FileOutputStream fos = null;
        CipherOutputStream cos = null;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(in);
            fos = new FileOutputStream(output);
            cos = new CipherOutputStream(fos, cipher);
            byte[] data = new byte[1024];
            int read = fis.read(data);
            while (read != -1) {
                cos.write(data, 0, read);
                read = fis.read(data);
             System.out.println(new String(data, "UTF-8").trim());

            }
            cos.flush();

        } finally {

            System.out.println("performed encrypt method now closing streams:
" + output.toString());
            cos.close();
            fos.close();
            fis.close();


        }
    }


public void watchMeEncrypt(){

encrypt(file, new File ("example.txt),key);

推荐答案

AES 密钥仅由随机字节组成.对于 CBC 模式,IV 模式也应该是随机的(至少对攻击者而言).因此,通常您可以简单地使用 SecureRandom 实例来创建密钥和 IV.然后IV可以包含在密文中;通常它只是简单地放在它前面.

An AES key simply consists of random bytes. For CBC mode the IV mode should also be randomized (at least to an attacker). So in general you can simply use a SecureRandom instance to create the key and IV. The IV can then be included with the ciphertext; usually it is simply put in front of it.

对于 Java,最好使用 KeyGenerator.如果您查看它在 SUN 提供程序中的实现,它可能相当于同一件事.然而,使用 KeyGenerator 更兼容各种类型的密钥和提供程序.很可能这是在例如生成密钥的要求.智能卡和 HSM.

With Java it is better to use a KeyGenerator though. If you look at the implementation of it in the SUN provider it will probably amount to the same thing. However using a KeyGenerator is more compatible with various kinds of keys and providers. It may well be that it is a requirement for generating keys in e.g. smart cards and HSM's.

让我们展示一个包含三个简单方法的类:

So lets show a class with three simple methods:

package nl.owlstead.stackoverflow;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Optional;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class CreateKeyAndIVForAES_CBC {

    public static SecretKey createKey(final String algorithm, final int keysize, final Optional<Provider> provider, final Optional<SecureRandom> rng) throws NoSuchAlgorithmException {
        final KeyGenerator keyGenerator;
        if (provider.isPresent()) {
            keyGenerator = KeyGenerator.getInstance(algorithm, provider.get());
        } else {
            keyGenerator = KeyGenerator.getInstance(algorithm);
        }

        if (rng.isPresent()) {
            keyGenerator.init(keysize, rng.get());
        } else {
            // not really needed for the Sun provider which handles null OK
            keyGenerator.init(keysize);
        }

        return keyGenerator.generateKey();
    }

    public static IvParameterSpec createIV(final int ivSizeBytes, final Optional<SecureRandom> rng) {
        final byte[] iv = new byte[ivSizeBytes];
        final SecureRandom theRNG = rng.orElse(new SecureRandom());
        theRNG.nextBytes(iv);
        return new IvParameterSpec(iv);
    }

    public static IvParameterSpec readIV(final int ivSizeBytes, final InputStream is) throws IOException {
        final byte[] iv = new byte[ivSizeBytes];
        int offset = 0;
        while (offset < ivSizeBytes) {
            final int read = is.read(iv, offset, ivSizeBytes - offset);
            if (read == -1) {
                throw new IOException("Too few bytes for IV in input stream");
            }
            offset += read;
        }
        return new IvParameterSpec(iv);
    }

    public static void main(String[] args) throws Exception {
        final SecureRandom rng = new SecureRandom();
        // you somehow need to distribute this key
        final SecretKey aesKey = createKey("AES", 128, Optional.empty(), Optional.of(rng));
        final byte[] plaintext = "owlstead".getBytes(UTF_8);

        final byte[] ciphertext;
        {
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();

            final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final IvParameterSpec ivForCBC = createIV(aesCBC.getBlockSize(), Optional.of(rng));
            aesCBC.init(Cipher.ENCRYPT_MODE, aesKey, ivForCBC);

            baos.write(ivForCBC.getIV());

            try (final CipherOutputStream cos = new CipherOutputStream(baos, aesCBC)) {
                cos.write(plaintext);
            }

            ciphertext = baos.toByteArray();
        }

        final byte[] decrypted;
        {
            final ByteArrayInputStream bais = new ByteArrayInputStream(ciphertext);

            final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final IvParameterSpec ivForCBC = readIV(aesCBC.getBlockSize(), bais);
            aesCBC.init(Cipher.DECRYPT_MODE, aesKey, ivForCBC);

            final byte[] buf = new byte[1_024];
            try (final CipherInputStream cis = new CipherInputStream(bais, aesCBC);
                    final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                int read;
                while ((read = cis.read(buf)) != -1) {
                    baos.write(buf, 0, read);
                }
                decrypted = baos.toByteArray();
            }
        }

        System.out.println(new String(decrypted, UTF_8));
    }
}

请注意,您可能并不总是希望带外"生成和分发 AES 密钥.以下是其他一些生成密钥的方法(从第 2 部分开始).您可能还想查看加密操作的更高级异常处理.

Note that you may not always want to generate and distribute an AES key "out-of-band". Here are a few other methods of generating a key (part #2 onwards). You may also want to take a look at more advanced exception handling for the cryptographic operation.

相关文章