質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.63%

PHPで暗号化,JS(Node.js)で復号ができない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 4
  • VIEW 2,188

hirosnow

score 10

初めまして。
復号で嵌っているので、お助けいただけますと嬉しいです。

前提・実現したいこと

使用言語:PHP(7.2.13),JS(Node.js(10.14.1))
実現したいこと:PHPで暗号化した文をJS側で復号したい

発生している問題・エラーメッセージ

修正前
(node:11944) UnhandledPromiseRejectionWarning: Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
修正後
(node:18364) UnhandledPromiseRejectionWarning: Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

該当のソースコード

平文:テスト太郎

修正前
  base64_encode(openssl_encrypt($planeText, 'aes-256-ecb', 'testtes'));
  const crypto = require("crypto");

  const planeText = 'cEtkTUxJMngwK0VJMWNHa0NWRE1Xdz09';//平文をPHPで暗号化した文字列
  const buffer = new Buffer(planeText, 'base64');
  const ascii = buffer.toString('ascii');
  console.log('ascii: '+ ascii);

  const passowrd = 'testtes';
  const alg = 'aes-256-ecb'
  const encoding = 'base64' 

  const decipher = crypto.createDecipher(alg, passowrd);
  let dec = decipher.update(ascii, encoding, 'utf8');
  dec += decipher.final('utf8');

  console.log('decrypted: ' + dec);
修正後
  openssl_encrypt($planeText, 'aes-256-ecb', 'testtes', 1);
  const crypto = require("crypto");

  const planeText = 'A4A74C2C8DB1D3E108D5C1A40950CC5B';//平文をPHPで暗号化した文字列
  const buffer = new Buffer(planeText, 'base64');
  const ascii = buffer.toString('ascii');
  console.log('ascii: '+ ascii);

  const passowrd = 'testtes';
  const alg = 'aes-256-ecb'
  const encoding = 'binary'

  const decipher = crypto.createDecipher(alg, passowrd);
  let dec = decipher.update(ascii, encoding, 'utf8');
  dec += decipher.final('utf8');

  console.log('decrypted: ' + dec);

試したこと

https://qiita.com/hm0429/items/2acee723170b32b91304
https://qiita.com/mokemokechicken/items/7dfb4d2a9800426ead9d
https://qiita.com/tomi_linka/items/e39e767f77eaca4e82f5
その他もろもろ見ましたが、うまくいきませんでした。
そもそもPHP側で暗号化した場合とJS側で暗号化した際に文字列が言語ごとで異なる理由がわかりません。
(復号方法が違うからなんでしょうが・・・)

また、PHPで暗号化する際に'base64'せずに'binary'にすることができましたが、修正後でもエラーが出ており上手く動きません。

補足情報(FW/ツールのバージョンなど)

暗号化する際に『aes-256-ecb』を使用するのはよくないことは承知しております。
ですが、今回は上記を利用したいと考えております。

何卒宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2019/05/08 16:46

    Node環境手元になく検証できないのでこちらでコメントしますが、Base64って言語によって仕様がまちまちで実装もそれぞれ細かいところを見ると違うようです。それは原因に思います。
    https://izariuo440.hatenadiary.org/entry/20110127/1296138201
    ※それに厳密にいうとあくまで「エンコード(別の形式への変換)」であって暗号化ではないと思います・・

    キャンセル

  • hirosnow

    2019/05/08 16:58

    ご回答いただきましてありがとうございます。
    PHPのopenssl_encryptに関して、上に書いた方法だと自動的にBase64に変換されてしまうと思うのですが、binary型で出力する方法はご存知でしょうか。

    キャンセル

  • m.ts10806

    2019/05/08 17:00

    ※私のコメントは回答ではなく「質問への追記・修正依頼」のコメントです。

    >PHPのopenssl_encryptに関して、上に書いた方法だと自動的にBase64に変換されてしまうと思うのですが、binary型で出力する方法はご存知でしょうか。

    要件になりますので質問本文に追記してください。
    タイトルや質問内容も変わってきませんか?調整してみてください。

    キャンセル

  • hirosnow

    2019/05/08 17:04

    失礼いたしました。
    本文とタイトル変更・追記したいと思います。
    お手数おかけいたしました。

    キャンセル

回答 3

checkベストアンサー

+2

PHPでは次の変換が行われています。
平文 => 暗号化されたバイナリ => base64テキスト

node側でその逆の変換がちょっと変ですね。
変数の名前も変なので、その処理でどの状態になるのか多分十分理解できていないのだと思います。

逆の処理としては、
base64テキスト => base64デコードしたBuffer(=暗号化されたバイナリ) => 平文

なんですが、平文への復号時に下のようになっています。

 let dec = decipher.update(ascii, encoding, 'utf8');

ここでencodingがbase64なので、base64デコードを二重に行う形になってそうな気がします。
encoding='binary'にしたり、指定しないと(デフォルトの状態だと)どうなりますか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/08 17:53

    ご回答いただきありがとうございます。
    node.jsの'encoding'を'binary'に変更、指定なし、にしたところどちらも
    ##################################################
    (node:13408) UnhandledPromiseRejectionWarning: Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
    ##################################################
    とエラーが表示されました。
    また、試しに'テスト太郎'をnode.jsで暗号化したのですが、'E9SdJxUl2WBSbFV7jnZYvg=='と暗号化されPHPと違う文字列になり混乱しております。
    (文字列が同じになるはずでは?と考えておりました。)

    キャンセル

  • 2019/05/08 21:34

    Nodeでencrypto(+base64 encode)した文字列を、もとのplain textに複合することはできますか?
    違う文字列になることは確かに奇妙ですが、paddingとかsaltとか考えられる可能性はあるので、そこまで致命的な違いだとは思いません。

    とりあえず、エラーの内容が変化したことは一つの僥倖ではあって、それをググってみるのも一つの手ですね。

    ちなみに、この辺を読むと、decipher.update()はencrypto+base64のエンコードをまとめて処理できるようなので、Bufferを通さずに直接渡してみるのはどうでしょう。
    https://github.com/PeculiarVentures/node-webcrypto-ossl/issues/112#issuecomment-418991594

    キャンセル

  • 2019/05/08 21:56

    ご回答いただきましてありがとうございます。
    >>Nodeでencrypto(+base64 encode)した文字列を、もとのplain textに複合することはできますか?
    Nodeで暗号化した文字列を元の文字列に複合できるか?という認識で合っていますでしょうか。
    それであれば問題なくできました。

    ご指摘いただいた通り、paddingやsaltなどの関係があるか確認をしたいと思います。

    教えて頂いたサイトとエラーが変化したことについては改めて明日確認してみたいと思います。
    他のサイトに書かれているのは'aes-256-cbc'で暗号化しているものが多いので、そちらも確認します。

    ご丁寧に回答いただき、ありがとうございました。

    キャンセル

0

直接の回答ではないのですが、このスクリプトの目的は何でしょうか? 単なるJavaScrptの練習などであればよいのですが、もしも実用を目指しているのであれば、そもそもの考え方に問題があります。
というのは、ブラウザが受け取る HTMLとJavaScriptが盗聴された場合、暗号化データと鍵がセットで盗まれてしまうので、盗聴者は容易に復号できるからです。とくにプログラムの解読などがっつりやらなくても、デバッガ上で適当なところにブレークポイントを置いて、復号されたタイミングでデータを取得すればよいでしょう。

上記を承知の上で、練習としてやっているならよいのですが、念の為お知らせします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/08 21:31

    decryptはNode.jsを想定しているようなので、サーバサイドでの処理になるはずです。たぶん問題ないかと。

    キャンセル

  • 2019/05/08 21:35

    確かにそうですね。失礼しました

    キャンセル

0

自己解決しました。
結局、PHPで暗号化→Nodeで復号はできませんでしたので、MySQL上で暗号化&復号することにしました。

MySQL暗号化&復号:https://qiita.com/mhagita/items/899483f08347fddd9567
TypeORM:https://typeorm.io/#/select-query-builder/getting-raw-results
を参考にしました。

PHPでMySQLにINSERTする際、AES形式で暗号化した文字列を格納
Nodeで取り出す際にTypeORMを使用して、SELECT文を操作することで復号した文字列を取り出せました。

推測ではありますが、PHPのopensslのバージョンとNodeのopensslのバージョンがあっていなかったか、
そもそも言語間で互換性がなかったかということで整理をしています。
(CBCでは復号化できているサイトが多いので、後者は違うかな?)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る