JAVA中基于GCM的IV确定性构造

3
我是一个新手,对密码学领域不太了解。 我试着在我的应用程序中尝试使用GCM模式的AES加密来保护静止数据。 我查阅了NIST GCM模式建议。它提到IV的独特性非常重要。如果(密钥,IV)对重复,攻击者可以构造密文伪造。 以下是代码示例:
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) 使用iv中的字节创建IvParameterSpec对象。
是否有BouncyCastle或其他库中的实现可用以获得确定性构造的IV,还是需要构建自定义实现?另外,请指导如何创建使用确定性方法的自定义IV实现。

1
你将不得不自己实现它。特别是,你必须决定如何可靠地存储和更新每个设备的计数器。 - Oleg Estekhin
1个回答

3

NIST没有为创建确定性IV(在8.2.1确定性构造中指定)的算法,因此也没有实现。它只定义了一些一般程序,您需要遵循这些程序来创建一个符合NIST要求的IV。

如果您已经拥有某些消息的唯一标识符,则最好创建一个哈希值并使用左侧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));
        }        
    }
}

请注意,仅需要在不包含GCM操作模式的Java版本中使用Bouncy Castle提供程序。 Oracle Java 8 确实 包含这样的实现,应与上述代码兼容。 - Maarten Bodewes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接