我是密码学领域的新手。 我在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创建定制实现。
答案 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));
}
}
}