面向GCM的JAVA中IV的确定性构造

面向GCM的JAVA中IV的确定性构造,java,encryption,cryptography,bouncycastle,Java,Encryption,Cryptography,Bouncycastle,我对密码学是个新手。 我在GCM模式下尝试AES加密,以保护我的应用程序的静态数据 我通过了考试。它提到IV的独特性非常重要。它说,如果(key,IV)对被重复,对手就可以伪造密文。 PFB代码示例 public static void main(String[] args) throws Exception { byte[] keyBytes = MessageDigest.getInstance("MD5").digest(

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

我通过了考试。它提到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确定性结构生成

目前使用的是在[

IvParameterSpec(字节[]iv)使用 iv中的字节作为iv

在BouncyCastle或任何其他库中是否有任何可用的实现来获取确定性构造中的IV。或者我们是否需要构建自定义实现。以及如何实现IvParameterSpec。是否遵循规范


另外,请指导使用确定性方法创建IV的定制实现。

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

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


以下是一个根据NIST规范(据我所知)的简单结构。别忘了以一种永远不会重复使用的方式存储计数器

导入java.nio.charset.StandardCharset;
导入java.security.security;
导入java.util.array;
导入javax.crypto.Cipher;
导入javax.crypto.spec.IvParameterSpec;
导入javax.crypto.spec.SecretKeySpec;
导入org.bouncycastle.jce.provider.BouncyCastleProvider;
导入org.bouncycastle.util.encoders.Hex;
公共类计数器{
私有最终整数块大小字节;
专用最终字节[]ivCounter;
公共计数器IVCreator(最终整数块大小字节){
if(块大小字节%2!=0 | |块大小字节<16){
//AKA不要使用DES或3DES
抛出新的IllegalArgumentException(“块大小应为偶数且至少为16字节”);
}
this.blockSizeBytes=blockSizeBytes;
this.ivCounter=新字节[blockSizeBytes/2];
}
公共计数器IVCreator(最终字节[]oldCounter){
如果(oldCounter.length<8){
//AKA不要使用DES或3DES
抛出新的IllegalArgumentException(“计数器应大于8字节”);
}
this.blockSizeBytes=oldCounter.length*2;
this.ivCounter=oldCounter.clone();
}
公共IvParameterSpec createIV(){
递增计数器(ivCounter);
最后一个字节[]iv=Arrays.copyOf(ivCounter,blockSizeBytes);
返回新的IvParameterSpec(iv);
}
公共字节[]getCounter(){
返回ivCounter.clone();
}
专用静态无效增量计数器(最终字节[]计数器){
对于(int i=counter.length-1;i>=0;i--){
计数器[i]++;
如果(计数器[i]!=0){
打破
}
}
}
公共静态void main(最终字符串…args)引发异常{
addProvider(新的BouncyCastleProvider());
字节[]计数器;
Cipher gcm=Cipher.getInstance(“AES/gcm/NoPadding”);
SecretKeySpec aesKey=new SecretKeySpec(新字节[Cipher.getMaxAllowedKeyLength(“AES/GCM/NoPadding”)/byte.SIZE],“AES”);
{
CounterIVCreator=新的CounterIVCreator(gcm.getBlockSize());
IvParameterSpec ivSpec=ivCreator.createIV();
gcm.init(Cipher.ENCRYPT_模式,aesKey,ivSpec);
gcm.updateAAD(ivSpec.getIV());
byte[]ciphertext=gcm.doFinal(“owlstead.getBytes(StandardCharsets.UTF_8));
System.out.println(十六进制到十六进制字符串(密文));
gcm.init(Cipher.DECRYPT_模式,aesKey,ivSpec);
gcm.updateAAD(ivSpec.getIV());
字节[]明文=gcm.doFinal(密文);
System.out.println(新字符串(纯文本,StandardCharsets.UTF_8));
oldCounter=ivCreator.getCounter();
}
//第二部分,创建一个完全不同的密文
{
CounterIVCreator=新的CounterIVCreator(旧计数器);
IvParameterSpec ivSpec=ivCreator.createIV();
gcm.init(Cipher.ENCRYPT_模式,aesKey,ivSpec);
gcm.updateAAD(ivSpec.getIV());
byte[]ciphertext=gcm.doFinal(“owlstead.getBytes(StandardCharsets.UTF_8));
System.out.println(十六进制到十六进制字符串(密文));
gcm.init(Cipher.DECRYPT_模式,aesKey,ivSpec);
gcm.updateAAD(ivSpec.getIV());
字节[]明文=gcm.doFinal(密文);
System.out.println(新字符串(纯文本,StandardCharsets.UTF_8));
}        
}
}

您必须自己实现它。特别是,您必须决定如何可靠地存储和更新每设备计数器。请注意,对于不包含GCM操作模式的Java版本,您应该只需要Bouncy Castle提供程序。Oracle Java 8确实包含这样的实现,它应该是comp以上代码不适用。您好,这适合您的需要吗?
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));
        }        
    }
}