crypto-jsを使用した復号処理でハマったこと


ハマったポイント

個人開発でFirebaseから取得した暗号化された値を復号するという処理を実装していた際に、復号した結果が正しく表示されない事態にぶち当たりました。その際に上手くいかなかった原因を備忘のために記載しておきます。

そもそも

「AES?Padding?...(今でもきちんと説明できる自信がない)」

暗号化・復号の概念についてはまったくもって詳しくなかったため、試行錯誤しながらの取り組みでした
基礎や単語についてはこちらのQittaを見て学ばせていただきました
暗号化についてはいずれもっと学びたいです。

やりたかったこと

  • Base64でエンコードされた暗号文を復号(Decrypt)すること。
  • 使用言語はTypescript

やったこと

  1. npm i -D crypto-js @type/crypto-jsでnpmパッケージをインストール
  2. 復号処理を実装

復号処理

import CryptoJS from "crypto-js"

const decrypt = (word, passPhrase) => {
    const key = passPhrase.substring(0,16) // AES-128を使用していたため16文字。これをUTF-8でParseしてWordArrayを生成する。
    const encryptedBase64 = CryptoJS.enc.Base64.parse(word)  // 暗号文をBase64でParseしてWordArrayを生成する。
    const cipherParams = CryptoJS.lib.CipherParams.create({
        ciphertext: encryptedBase64,
        iv: CryptoJS.enc.Utf8.parse(key),
        // mode: CryptoJS.mode.ECB, // ここのmodeはパッケージのexample通りに記述しても型が違うと怒られるので要注意!
        padding: CryptoJS.pad.Pkcs7,
    })

    const plainText = CryptoJS.AES.decrypt(cipherParams, CryptoJS.enc.Utf8.parse(key), {mode: CryptoJS.mode.ECB})
    return plainText.toString(CryptoJS.enc.Utf8)
}

console.log("output", decrypt("hoge", "pass")) // 出力

ハマったポイント

自分がハマったのはkeyでした。
CryptoJS.AES.decryptの型をみると、

decrypt(ciphertext: CipherParams | string, key: WordArray | string, cfg?: CipherOption): WordArray

となっており、keyを以下のようにstringのまま渡していました。

const key = passPhrase.substring(0,16) 
const plainText = CryptoJS.AES.decrypt(cipherParams, key, {mode: CryptoJS.mode.ECB}) // UTF-8でparseしていない。

これによって復号が上手く行かなかったみたいです。
もしかするとWordArrayに変換した暗号文については、WordArrayのkeyを用意する必要があるのかも知れません。

さいごに

暗号化や復号については初めて触れる実装だったので、結構大変でした
自分は原因がわからず、また有効なリソースも見つけられず途方にくれていました。
復号で上手く行かない場合に少しでもこの記事がきっかけになれば嬉しいです。