現在、Java TLS サーバーに取り組んでいます。(KeyExchange署名に関して数日前にここに投稿しました)
現在、AES-GCM を使用してエンコードされた TLS メッセージを復号化しようとしています。サーバーは既に CBC を処理していますが、POODLE に対して脆弱であるため、代わりに GCM を実行したいと考えています。できる限り説明しようと思います:)
このコードでは、Java 8u91、Netty 3.9.0 を使用しています。私たちは BouncyCastle を使用しておらず、そのつもりもありません。JDK を使い続けたいと考えています。
コード !
/**
* Deciphers the fragment and returns the deciphered version of it
*
* @param fragment
* to decrypt
* @return the decrypted fragment
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
private ChannelBuffer decodeCiphered(ChannelBuffer fragment, short contentType, byte[] version) throws InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
if (readCipher != null) {
byte[] ciphered = fragment.array();
if (session.cipherSuite.cipher.type == CipherType.stream) {
/* ... */
} else if (session.cipherSuite.cipher.type == CipherType.block) {
/* ... */
} else if (session.cipherSuite.cipher.type == CipherType.aead) {
byte[] nonce = concat(clientWriteIV.getIV(), fragment.copy(0, 8).array());
// ClientWriteIV + explicit nonce (first 8 bytes of encrypted data)
readCipher.init(Cipher.DECRYPT_MODE, clientWriteKey,
new GCMParameterSpec(session.cipherSuite.cipher.block * 8, nonce));
ChannelBuffer aad = ChannelBuffers.buffer(13);
//seq_num(8B) + TLSCompressed.type(1B) + TLSCompressed.version(2B) + TLSCompressed.length(2B)
aad.writeLong(writeSequence.longValue());
aad.writeByte(contentType);
aad.writeBytes(version);
aad.writeShort(fragment.readableBytes() - 8 - 16);
readCipher.updateAAD(aad.array());
ciphered = readCipher.doFinal(ciphered, 8, ciphered.length - 8);
fragment = ChannelBuffers.wrappedBuffer(ciphered);
}
return fragment;
}
/**
* Generates the different needed keys.
*/
private void generateKeys() {
byte[] keyBlock = null;
if (session.cipherSuite.cipher.type == CipherType.aead) {
keyBlock = new byte[2 * session.cipherSuite.cipher.key
+ 8];
try {
prf(keyBlock, session.masterSecret, KEY_EXPANSION_LABEL, concat(serverRandom, clientRandom));
} catch (GeneralSecurityException e) {
}
clientWriteKey = new SecretKeySpec(keyBlock,0 , session.cipherSuite.cipher.key, session.cipherSuite.cipher.kalgo);
serverWriteKey = new SecretKeySpec(keyBlock, session.cipherSuite.cipher.key, session.cipherSuite.cipher.key, session.cipherSuite.cipher.kalgo);
clientWriteIV = new IvParameterSpec(keyBlock, 2 * session.cipherSuite.cipher.key, 4);
serverWriteIV = new IvParameterSpec(keyBlock, 2 * session.cipherSuite.cipher.key + 4, 4);
} else {
/* ...
For CBC
*/
}
}
次のエラーが常に発生します。
javax.crypto.AEADBadTagException: Tag mismatch!
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:524)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1023)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:960)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2223)
at com.seemy.codec.tls.TlsCodec.decodeCiphered(TlsCodec.java:525)
通常、これは AAD が正しくないことを意味しているようです。しかし、RFC 5246 セクション 6.2.3.3からわかる限り、AAD は次のようにする必要があります。
The additional authenticated data, which we denote as additional_data, is defined as follows:
additional_data = seq_num + TLSCompressed.type +
TLSCompressed.version + TLSCompressed.length;
where "+" denotes concatenation.
このメッセージを解読するために私が間違っていることを見つけるのに迷っています。「タグの不一致」エラーは、何が問題なのかを見つけるのにあまり役に立たないので、ここの誰かが私を助けてくれることを願っています:)
ありがとう。良い一日を !
編集 1: contentVersion にバイトではなくショートを入れていましたが、結果は同じです。タグの不一致エラーしか表示されません...
編集 2:
@dave_thompson_085 が示唆するように、explicit_nonce を除外する代わりにdoFinal
呼び出されるようになりました。(ciphered, 8, ciphered.length-8)
(ciphered, 0, ciphered.length)
また、より精査して aad と nonce の値をチェックしました。
nonce の explicit_nonce 部分は、Wireshark で見ているパケットに対応しています。client_write_IV が適切に生成されていないのではないかと心配しています:|
aad に関しては、少し奇妙なことを発見しました。長さは 0 です。
クライアントから取得しているメッセージは、40 バイトの長さから、explicit_nonce 部分の 8 を引いて、SHA-256 の 32 である MAC の長さを引いた長さです (tls_ecdhe_rsa_with_aes_128_gcm_sha256 を動作させようとしています)。
したがって、tls_ecdhe_rsa_with_aes_256_gcm_sha384 を試すと、長さは -16 になります。これは私には間違っているようです。
編集 3:
再び @dave_thompson_085 が提案したとおりに実行し、次のような長さになるようにコードを変更しましaad.writeShort(fragment.readableBytes() - 8 - 16);
た。aad.writeShort(fragment.readableBytes()-8-session.cipherSuite.mac.len);
クロムと話すときはうまくいくようです(独自の暗号化されたメッセージを送信した後に失敗しますが、自分で修正できない場合は後で別の投稿が必要になります)が、open_sslクライアントで試してみるとタグの不一致が発生しますまた。Firefox と Safari は私に protocol_version アラートを送信するだけです…</p>