用于GCM的JAVA中IV的确定性构建

时间:2014-07-11 13:43:51

标签: 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)使用。创建一个IvParameterSpec对象   iv中的字节为IV。

在BouncyCastle或任何其他图书馆中是否有任何可用于确定性构造中的IV。我们是否需要构建自定义实现。还有如何实现IvParameterSpec。它是否遵循规范。

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

1 个答案:

答案 0 :(得分:2)

没有用于创建确定性IV的NIST算法(在8.2.1确定性构造中指定)。所以也没有实现。它只是定义了您需要遵循的一些通用程序,以创建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));
        }        
    }
}