java - 用于 GCM 的 JAVA 中 IV 的确定性构造

标签 java encryption cryptography bouncycastle

我是密码学领域的新手。 我正在尝试在 GCM 模式下进行 AES 加密,以保护我的应用程序的静态数据。

我浏览了 NIST recommendations for GCM mode .它提到IV的唯一性非常重要。它表示,如果重复 (key, IV) 对,则对手可以构造密文伪造。 PFB代码示例

public static void main(String[] args) throws Exception {
                            byte[] keyBytes = MessageDigest.getInstance("MD5").digest(
                                                            "som3C0o7p@s5".getBytes());
                            SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");                       
                            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                            IvParameterSpec ivSpec = new IvParameterSpec(new byte[96]);
                            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
                            byte[] block = new byte[96];
                            int i;
                            long st, et;

                            cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

                            BufferedInputStream bIn = new BufferedInputStream(
                                                            new ProgressMonitorInputStream(null, "Encrypting ...",
                                                                                            new FileInputStream("input.txt")));
                            CipherInputStream cIn = new CipherInputStream(bIn, cipher);
                            BufferedOutputStream bOut = new BufferedOutputStream(
                                                            new FileOutputStream("output.txt"));

                            int ch;
                            while ((i = cIn.read(block)) != -1) {
                                            bOut.write(block, 0, i);
                            }
                            cIn.close();
                            bOut.close();

                            Thread.sleep(5000);


                cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
    InputStream is = new FileInputStream("output.txt");
    OutputStream os = new FileOutputStream("output2.txt");
    os = new CipherOutputStream(os, cipher);
    while ((i = is.read(block)) != -1)
    {
        os.write(block, 0, i);
    }
    is.close();
    os.close();
            }

我能够加密和解密 input.txt 文件中的文本。 ECM 的 IV 需要是唯一的,我希望使用 NIST 800-38D 中指定的 Iv 的确定性构造生成它。

目前使用 [ http://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/IvParameterSpec.html][1] 中可用的 IV 代

IvParameterSpec(byte[] iv) Creates an IvParameterSpec object using the bytes in iv as the IV.

BouncyCaSTLe 或任何其他库中是否有任何可用的实现来获得确定性构造中的 IV。或者我们是否需要构建自定义实现。还有 IvParameterSpec 的实现方式。是否符合规范。

另请指导使用确定性方法创建 IV 创建的自定义实现。

最佳答案

没有用于创建确定性 IV(在 8.2.1 确定性构造中指定)的 NIST 算法。所以也没有实现。它只是定义了一些您需要遵循的通用程序来创建一个 NIST 可以接受的程序。

如果您已经拥有消息的唯一性,那么您最好在唯一 ID 上创建哈希并使用最左边的 8 个字节作为“计数器”,而不是使用以下基于计数器的方法。


以下是根据 NIST 规范(据我所知)的简单结构。不要忘记以从不重复使用的方式存储计数器。

import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

public class CounterIVCreator {

    private final int blockSizeBytes;
    private final byte[] ivCounter;

    public CounterIVCreator(final int blockSizeBytes) {
        if (blockSizeBytes % 2 != 0 || blockSizeBytes < 16) {
            // AKA don't use DES or 3DES
            throw new IllegalArgumentException("Block size should be even and at least 16 bytes");
        }

        this.blockSizeBytes = blockSizeBytes;
        this.ivCounter = new byte[blockSizeBytes / 2];
    }

    public CounterIVCreator(final byte[] oldCounter) {
        if (oldCounter.length < 8) {
            // AKA don't use DES or 3DES
            throw new IllegalArgumentException("Counter should be larger than 8 bytes");
        }

        this.blockSizeBytes = oldCounter.length * 2;
        this.ivCounter = oldCounter.clone();
    }


    public IvParameterSpec createIV() {
        increaseCounter(ivCounter);
        final byte[] iv = Arrays.copyOf(ivCounter, blockSizeBytes);
        return new IvParameterSpec(iv);
    }

    public byte[] getCounter() {
        return ivCounter.clone();
    }

    private static void increaseCounter(final byte[] counter) {
        for (int i = counter.length - 1; i >= 0; i--) {
            counter[i]++;
            if (counter[i] != 0) {
                break;
            }
        }
    }

    public static void main(final String ... args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        byte[] oldCounter;

        Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec aesKey = new SecretKeySpec(new byte[Cipher.getMaxAllowedKeyLength("AES/GCM/NoPadding") / Byte.SIZE], "AES");

        {
            CounterIVCreator ivCreator = new CounterIVCreator(gcm.getBlockSize());
            IvParameterSpec ivSpec = ivCreator.createIV();
            gcm.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
            gcm.updateAAD(ivSpec.getIV());
            byte[] ciphertext = gcm.doFinal("owlstead".getBytes(StandardCharsets.UTF_8));
            System.out.println(Hex.toHexString(ciphertext));
            gcm.init(Cipher.DECRYPT_MODE, aesKey, ivSpec);
            gcm.updateAAD(ivSpec.getIV());
            byte[] plaintext = gcm.doFinal(ciphertext);
            System.out.println(new String(plaintext, StandardCharsets.UTF_8));

            oldCounter = ivCreator.getCounter();
        }

        // part deux, creates an entirely different ciphertext
        {
            CounterIVCreator ivCreator = new CounterIVCreator(oldCounter);
            IvParameterSpec ivSpec = ivCreator.createIV();
            gcm.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
            gcm.updateAAD(ivSpec.getIV());
            byte[] ciphertext = gcm.doFinal("owlstead".getBytes(StandardCharsets.UTF_8));
            System.out.println(Hex.toHexString(ciphertext));
            gcm.init(Cipher.DECRYPT_MODE, aesKey, ivSpec);
            gcm.updateAAD(ivSpec.getIV());
            byte[] plaintext = gcm.doFinal(ciphertext);
            System.out.println(new String(plaintext, StandardCharsets.UTF_8));
        }        
    }
}

关于java - 用于 GCM 的 JAVA 中 IV 的确定性构造,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24699228/

相关文章:

java - 调试 AES-CMAC,生成错误答案

android - Flutter - 如果我们有 RSA 公钥,如何解密 RSA 私钥加密字符串?

C# Windows.Security.Cryptography.Core 引用

mysql - 是否可以逆向 mysql aes_crypt 来找到 key ?

.Net SHA256Managed 产生无效散列

Java:新手 "javac: invalid flag"编译错误

java - Android 比较 boolean 值

java - 使用 join 的 Spring Data JPA 规范的不同结果

java - 华为设备上的文本对齐问题

java - 安装了 JCE Unlimited Strength,但不支持 AES 256