私は、暗号化/復号化AES-GCMおよびJDK 1.8 CipherOutputStreamを使用しようとしている、しかし、復号時のBadPaddingExceptionを取得しています。私は、暗号化と復号化時に同じIVと秘密鍵を使用していますが、必ず何が間違って起こっているではありません。以下のコードを参照してください。
static String AES_GCM_MODE = "AES/GCM/NoPadding";
SecretKey secretKey;
public SymmetricFileEncryption(){
Security.insertProviderAt( new BouncyCastleProvider(), 1);
setSecretKey();
}
public static void main(String[] args) throws Exception {
File inputFile = new File("test.txt");
File outputFile = new File("test-crypt.txt");
File out = new File("test-decrypt.txt");
SymmetricFileEncryption sym = new SymmetricFileEncryption();
sym.encrypt(inputFile, outputFile);
sym.decrypt(outputFile, out);
}
public Cipher getEncryptionCipher() throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
Cipher cipher = Cipher.getInstance(AES_GCM_MODE, "BC");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getInitializationVector());
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), new IvParameterSpec(getInitializationVector()) );
return cipher;
}
private Cipher getDecryptionCipher(File inputFile) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IOException, NoSuchProviderException {
//initialize cipher
Cipher cipher = Cipher.getInstance(AES_GCM_MODE, "BC");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getInitializationVector());
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(),new IvParameterSpec(getInitializationVector()) );
return cipher;
}
public void encrypt(File inputFile, File outputFile) throws Exception {
Cipher cipher = getEncryptionCipher();
FileOutputStream fos = null;
CipherOutputStream cos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(inputFile);
fos = new FileOutputStream(outputFile);
cos = new CipherOutputStream(fos, cipher);
byte[] data = new byte[16];
int read = fis.read(data);
while (read != -1) {
cos.write(data, 0, read);
read = fis.read(data);
}
cos.flush();
}catch (Exception e){
e.printStackTrace();
}
finally {
fos.close();
cos.close();
fis.close();
}
String iv = new String(cipher.getIV());
}
public void decrypt(File inputFile, File outputFile) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, NoSuchProviderException {
Cipher cipher = getDecryptionCipher(inputFile);
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
CipherInputStream cipherInputStream = null;
try{
inputStream = new FileInputStream(inputFile);
cipherInputStream = new CipherInputStream(inputStream, cipher);
outputStream = new FileOutputStream(outputFile);
byte[] data = new byte[16];
int read = cipherInputStream.read(data);
while(read != -1){
outputStream.write(data);
read = cipherInputStream.read(data);
}
outputStream.flush();
}catch (Exception e){
e.printStackTrace();
}
finally {
cipherInputStream.close();
inputStream.close();
outputStream.close();
}
}
public void setSecretKey(){
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
secretKey = new SecretKeySpec(key, "AES");
}
public SecretKey getSecretKey(){
return secretKey;
}
public byte[] getInitializationVector(){
String ivstr = "1234567890ab"; //12 bytes
byte[] iv = ivstr.getBytes();//new byte[12];
return iv;
}
線INT読み取り= cipherInputStream.read(データ)に復号化中にエラーが次のコードの結果、上記。
javax.crypto.BadPaddingException: mac check in GCM failed
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:128)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:246)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:222)
at com.rocketsoftware.abr.encryption.SymmetricFileEncryption.decrypt(SymmetricFileEncryption.java:107)
中:暗号化は正常に動作しない
encrypt
、CipherOutputStream#close
と呼ばれなければならない前にFileOutputStream#close
。これは、あるCipherOutputStream#close
呼び出しCipher#doFinal
タグを生成し、暗号文に追加します。この部分だけに書き込むことができるFileOutputStream
場合-instanceFileOutputStream#close
まだ呼び出されていません。ところで、CipherOutputStream#flush
呼び出す必要はありません。復号化に問題がある:では
decrypt
、outputStream.write(data)
置き換える必要がありますoutputStream.write(data, 0, read)
。それ以外の場合は通常、あまりにも多くのデータが書き込まれますFileOutputStream
-instance。クラス
javax.crypto.CipherInputStream
およびjavax.crypto.CipherOutputStream
認証を実行する偽陽性を、したがって、あるではないのドキュメント(Javaの12)からGCMモードに適した、例えばCipherInputStream
:このクラスは、BadPaddingExceptionおよび復号化時に失敗した整合性チェックによってスローされた他の例外をキャッチすることがあります。これらの例外は、スロー再されていないので、クライアントは、整合性チェックが失敗したことを知らされないことがあります。この動作のため、このクラスは、操作の認証済みモード(例えばGCM)での復号化での使用に適しないかもしれません。認証済みの暗号化を必要とするアプリケーションは、このクラスを使用する代わりに直接暗号APIを使用することができます。
そのため、暗号APIのいずれかは、ドキュメント、またははBouncyCastle-実装で推奨されているように、直接使用されなければならない
org.bouncycastle.crypto.io.CipherInputStream
とorg.bouncycastle.crypto.io.CipherOutputStream
暗号化のために、例えば:import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.io.CipherOutputStream; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; ... public void encrypt(File inputFile, File outputFile) throws Exception { AEADBlockCipher cipher = getEncryptionCipher(); // Following code as before (but with fixes described above) ... } public AEADBlockCipher getEncryptionCipher() throws Exception { AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); cipher.init(true, // encryption new AEADParameters( new KeyParameter(getSecretKey().getEncoded()), 128, // tag length getInitializationVector(), "Optional Associated Data".getBytes())); return cipher; } ...
そして、復号用のアナログ。
認証が失敗した場合でも、復号化が行われるので、開発者は、結果を破棄し、この場合に使用されていないことを確認しなければならないことに注意してください。