node.js-AES-256-golangとnode/phpの間でCBC暗号化が一致していません
暗号化がphpやnodeと比較してgoで異なる理由を理解しようとすると、少し問題が発生します。私は誰かが私が違いを理解するのを手伝ってくれることを望んでいました。これがデータであると仮定しましょう:
平文: hello big worldshello big worlds
キー: jJr44P3WSM5F8AC573racFpzU5zj7Rg5
iv: 97iEhhtgVjoVwdUw
base64で結果として得られる暗号化は次のとおりです。
ノードとPHPの戻り値:
OTdpRWhodGdWam9Wd2RVd0OgJ+Z7pSCVioYq41721jarxqLKXN3PcnnY6/AOrHeEfsTxXfCgm2uUi+vmCAdpvw==
Go return:
OTdpRWhodGdWam9Wd2RVd0OgJ+Z7pSCVioYq41721jarxqLKXN3PcnnY6/AOrHeE
ご覧のとおり、それらはほとんど同じであり、私を夢中にさせています。以下の暗号化コードをざっと見て、問題が何であるかについてのヒントを教えていただけますか?
GO:
func EncryptString(plainstring string、keystring string、encFormat int、ivOverride bool)(string){
//安全な場所から秘密鍵をロードし、複数の場所で再利用します
//NewCipher呼び出し。 (明らかに、このサンプルキーは何にも使用しないでください
//本物。)パスフレーズをキーに変換する場合は、適切なものを使用してください
//bcryptやscryptのようなパッケージ。
キー:= [] byte(keystring)
平文:= [] byte(plainstring)
// CBCモードはブロックで機能するため、平文をパディングする必要がある場合があります
//次のブロック全体。このようなパディングの例については、を参照してください。
//https://tools.ietf.org/html/rfc5246#section-6.2.3.2。ここで
//平文がすでに正しい長さであると想定します。
if len(plaintext)%aes.BlockSize!= 0 {
panic( "平文はブロックサイズの倍数ではありません")
}
ブロック、エラー:= aes.NewCipher(key)
err!=nil{の場合
パニック(エラー)
}
// IVは一意である必要がありますが、安全ではありません。したがって、それは一般的です
//暗号文の先頭に含めます。
暗号文:= make([] byte、aes.BlockSize + len(plaintext))
iv:=暗号文[:aes.BlockSize]
if _、err:= io.ReadFull(bytes.NewReader([] byte( "97iEhhtgVjoVwdUw"))、iv); err!= nil {
パニック(エラー)
}
mode:= cipher.NewCBCEncrypter(block、iv)
mode.CryptBlocks(ciphertext [aes.BlockSize:]、plaintext)
//暗号文は認証されなければならないことを覚えておくことが重要です
//(つまり、crypto / hmacを使用して)暗号化して
//安全である。
base64.StdEncoding.EncodeToString(ciphertext)を返します
}
ノード:
encodeString:function(string、key、fmt = null、ivOverride = false){
//初期化ベクトルを作成します
iv;
if(!ivOverride){
iv = crypto.randomBytes(IV_NUM_BYTES).toString('hex')。slice(0,16);
} そうしないと {
iv = IV_OVERRIDE_VALUE; // 97iEhhtgVjoVwdUw
}
//そして暗号化
暗号化を許可=crypto.createCipheriv('aes-256-cbc'、key、iv);
暗号化されたデータ=encryptor.update(string、'utf8'、'binary')+ encryptor.final('binary');
encodeData = iv +'' + encodeData;
cryptoData = Buffer.from(encryptedData、'binary')。toString('base64');
暗号化されたデータを返します。
}
encryptor.final('binary')
を削除すると、2つの結果は同じ暗号化になりますが、phpには.final()が含まれていません。これが組み込まれているように見えるPhp uses open_ssl_encrypt()
。goに同等のものを追加する方法はありますか?アドバイスを探しています。ありがとう
答え :
解決策:
わかりました。goの出力を他の出力と一致させることができましたが、すべての詳細、特にPHPとNodeのバージョンが100%明確ではありません。それらのように動作します(Goバージョンからの出力は、後で説明するように、私の心の中では「正しい」結果のように見えます)。
私の最初の観察では、NodeとPHPからの出力は、Goバージョンよりも約1ブロック長長く、テールエンドのみが異なります。これは、どういうわけか、それらのバージョンがgoバージョンよりも多くパディングされていることを私に教えてくれました。
そこで、PHPとNodeで使用されているデフォルトのパディングスキームである PKCS#7に従ってGoバージョンをパディングしてみました。基本的に、5バイトでパディングする必要がある場合、各パディングバイトは0x05に等しく、6バイトは0x06でパディングされるなどです。Goのデフォルトの aes.BlockSize
は次のようになります。 16なので、入力文字列を160x10バイトで埋めてみました。これが正解につながりました!
正直なところ、入力がすでにブロックアラインされている場合は入力をまったくパディングしないことは理解できる動作ですが、ノードとPHPはRFC 5652に準拠しており、ブロック全体を追加する必要がある場合でも、常にパディングを追加します(編集を参照)。パディング。
出力を一致させるためのGoコードは次のとおりです。
パッケージメイン
輸入 (
「バイト」
「crypto/aes」
「暗号/暗号」
「encoding/base64」
「fmt」
「io」
)。
//ウィキペディアに基づく:https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7
func PadToBlockSize(入力文字列)文字列{
paddingNeeded:= aes.BlockSize-(len(input)%aes.BlockSize)
paddingNeeded> =256{の場合
panic( "例のためにこのケースを処理するには怠惰です:)")
}
paddingNeeded ==0{の場合
paddingNeeded={-コード-1}
}
//非効率的ですが、これも例にすぎません。
for i:= 0; i
編集:
実際には、NodeとPHPは RFC 5652 に正しく準拠しているようです。これにより、すべての入力をパディングする必要があります。入力がブロックアラインされた場合でも、パディングが常に存在するという事実は、復号化を明確にします。パディングの手順はユーザーに任せてください。
同様の質問
私たちのウェブサイトで同様の質問で答えを見つけてください。