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

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

ただいまの
回答率

88.61%

opensslコマンドで暗号化された文字列をJavaで復号したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,333

fishStory

score 10

opensslコマンドで暗号化された文字列をJavaで復号したい

前提・実現したいこと

PostgreSQLのDBに、Pythonツールで暗号化したパスワードが格納されています。
JavaでそのパスワードをDBから取得し、復号して平文に戻したいです。

Pythonツールでは以下、Linuxコマンドのopensslを使って暗号化しています。

Base64
===============================================================================
[root]# echo "hoge" | openssl enc -e -des -base64 -k "test"
U2FsdGVkX196J+27IM+QRwos2qbm9tpu
===============================================================================


Linux上でコマンドを使用すれば複合できることは確認しています。

===============================================================================
[root]# echo "U2FsdGVkX196J+27IM+QRwos2qbm9tpu" | openssl enc -d -des -base64 -k "test"
hoge
===============================================================================

環境

  • Eclipse IDE for Enterprise Java Developers.
    Version: 2019-12 (4.14.0)
  • OS windows8(64bit)
  • JavaSE 1.8
  • Springboot

開発は上記Windows環境で実施していますが、最終的にはwarにしてLinuxで動かします。

作成したJavaソース

以下が私が作成したJavaのソースになります。
暗号にはECBモード,CBCモードなどがあるようですが、OpenSSLで暗号化した場合どのモードを使用すればよいかもよくわかっていません。
以下ではECBを使用しています。

package com.example.demo;

import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class test {

    public static void main(String[] args) throws Exception {

        //キー
        String key = "test";
        //複号対象文字列
        String enc = "2FsdGVkX196J+27IM+QRwos2qbm9tpu";


        byte[] enc_decode = Base64.getDecoder().decode(enc.getBytes());

        SecretKeySpec skey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");    
        cipher.init(Cipher.DECRYPT_MODE, skey);
        byte[] decrypted = cipher.doFinal(enc_decode);

        System.out.println(decrypted); ←ここで「hoge」と出てほしい
    }
}

    

実行結果

上記のソースを実行すると、エラーがでます。
Springbootのフレームワークを使用しており、Springスタータープロジェクトの配下に作成したクラスのためSpringBootアプリケーションから実行した結果です。

Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 4 bytes
at java.base/com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:90)
at java.base/com.sun.crypto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:95)
at java.base/com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at java.base/com.sun.crypto.provider.CipherCore.init(CipherCore.java:467)
at java.base/com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313)
at java.base/javax.crypto.Cipher.implInit(Cipher.java:839)
at java.base/javax.crypto.Cipher.chooseProvider(Cipher.java:901)
at java.base/javax.crypto.Cipher.init(Cipher.java:1286)
at java.base/javax.crypto.Cipher.init(Cipher.java:1223)
at com.example.demo.test.main(test.java:23)

「Invalid AES key length:」をGoogle検索して関係ありそうな以下も試してみましたが、効果はありませんでした。

  • C:\pleiades\java\8\jre\lib\security\java.security
    → 「crypto.policy=unlimited」のコメントを外して有効にする

参考:ttps://itdogkuwaccho.hatenadiary.com/entry/2017/11/12/125140

以上、よろしくお願いします。

2020/2/17追記

DES形式でソースを修正しました。

    public static void main(String[] args) {

        String key = "foobar00";
        String origin = "hoge";
        String enc = "U2FsdGVkX19eAkcLovbInztwfaMnjKwZ";


        try {
            /*
             * 鍵
             */
            // 秘密鍵を準備
            byte[] kagi = key.getBytes();
            DESKeySpec dk = new DESKeySpec(kagi);
            Arrays.fill(kagi, (byte)0x00); // セキュリティ情報を上書きして削除
            SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
            SecretKey sk = kf.generateSecret(dk);


            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");    
            cipher.init(Cipher.DECRYPT_MODE, sk);
            Cipher c = Cipher.getInstance("DES");

            //暗号化されたStringをデコード
            byte[] enc_decode = Base64.getDecoder().decode(enc.getBytes());

            //復号            
            byte[] output2 = c.doFinal(enc_decode);


            // 表示
            System.out.println("The string was ");
            System.out.println(new String(output2));


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    

実行結果

上記のソースを実行すると、下記エラーがでます。

javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at java.base/com.sun.crypto.provider.DESCipher.engineDoFinal(DESCipher.java:314)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
at com.example.demo.test2.main(test2.java:133)

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+3

注意 私は何もわかっていません。

GitHubを適当に探していたらこちらが見つかりました。
fgtrjhyu/openssl-des-decord-in-java: decode in java: echo -n "Hello,world" | openssl enc -des -a -k MyKey

例示の2つでは正しく取れているようです。

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Base64.Decoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

class Main {
    public static void main(String[] args) throws Exception {
        String key = "test";
        String enc = "U2FsdGVkX196J+27IM+QRwos2qbm9tpu";
        String text = OpenSSLDecryptor.DES_MD5.decrypt(enc, key);
        System.out.println(text);

        key = "foobar00";
        enc = "U2FsdGVkX19eAkcLovbInztwfaMnjKwZ";
        text = OpenSSLDecryptor.DES_MD5.decrypt(enc, key);
        System.out.println(text);
    }
}

class OpenSSLDecryptor {

    public static final OpenSSLDecryptor DES_MD5 = new OpenSSLDecryptor("DES", "MD5", 8);
    private static final byte[] Salted__ = "Salted__".getBytes();

    public static byte[] decrypt(byte[] msg, byte[] pass, Cipher cip, String alg, MessageDigest md, int bs) throws Exception {
        if (!eq(msg, 0, Salted__, 0, sizeof(Salted__))) {
            throw new IllegalArgumentException("this ciphertext is not salted.");
        }
        md.reset();
        md.update(pass);
        md.update(msg, Salted__.length, 8);
        byte[] digest = md.digest();
        cip.init(Cipher.DECRYPT_MODE, new SecretKeySpec(digest, 0, bs, alg), new IvParameterSpec(digest, bs, bs));
        return cip.doFinal(msg, 16, sizeof(msg) - 16);
    }

    private static boolean eq(byte[] xs, int x, byte[] ys, int y, int len) {
        return numeqs(xs, x, ys, y, len) == len;
    }

    private static int numeqs(byte[] xs, int x, byte[] ys, int y, int len) {
        int i = 0;
        final int lsz = sizeof(xs), rsz = sizeof(ys);
        for (; (x < lsz) && (y < rsz) && (xs[x] == ys[y]) && (i < len); ++i, ++x, ++y)
            ;
        return i;
    }

    private static int sizeof(byte[] a) {
        return a != null ? a.length : 0;
    }

    private final String alg;
    private final int bs;
    private final String md;

    public OpenSSLDecryptor(String alg, String md, int bs) {
        this.alg = alg;
        this.md = md;
        this.bs = bs;
    }

    public String decrypt(String message, String passphrase) throws Exception {
        return decrypt(message, passphrase, Charset.defaultCharset());
    }

    public String decrypt(String message, String passphrase, Charset cs) throws Exception {
        Cipher cip = Cipher.getInstance(String.format("%s/CBC/PKCS5Padding", this.alg));
        MessageDigest md = MessageDigest.getInstance(this.md);
        Decoder base64 = Base64.getDecoder();
        return new String(decrypt(base64.decode(message), passphrase.getBytes(cs), cip, this.alg, md, bs), cs);
    }
}


出力

hoge

hoge

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/02/18 18:02

    回答ありがとうございます。
    いただいたソースを使用したところ、無事復号することができました。
    本当に感謝しています!!!

    キャンセル

0

試してないので回答はかけないんですが ご参考までに。

 Invalid AES key length: 4 bytes

文字通り読むと、AES 鍵の長さが不正 ということですので 4バイト(32ビット)しかない キーの長さがよくないんでしょう。AES の 鍵長は128ビットと決まっています。

ただ、それよりも、DESで暗号化したものをAESで符号化していることに問題がありそうです。
DES暗号化と符号化のサンプルコードを検索してみて 試してください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/02/17 13:26

    回答ありがとうございます。確かにDESを指定しているのにAESで複号化しているのはおかしいかったです。調べてコードを書き直してみたのですが、「javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.」のエラーが出ます。
    キーが不正だと怒られているようなのですが、暗号化されたStringをデコードしている箇所が間違っているのか、秘密鍵を準備しているところが間違っているのでしょうか。

    作成しなおしたソースを追加で記載します。

    キャンセル

  • 2020/02/18 00:44

    エラーの原因はキーが合ってないんだと思います。

    軽く調べてみましたが、色々と間違ってそうで、私の手には負えなそうなのがわかりました。すみませんが、力になれそうもありません。

    openssl の暗号化の仕様ですが、出力されるデータは Salt 、IV のデータがヘッダーとして出力されてます。その後に、暗号化されたデータが続きます。そのデータがBASE64エンコードされてます。

    Javaのプログラムで復号化するには、BASE64デコードした バイト列から、Slat、IV ヘッダーを読み取って、キー(バイト列)を生成して、その生成したキーを使って 複合する感じですが、どうやって 実現すればいいか わかりませんでした。

    「-des」の場合は調べても分かりませんでしたが、「-aes-128-cbc」の場合はサンプルコードがあったので調べてみてください。
    https://alpha.mixi.co.jp/entry/2007/10639/

    キャンセル

  • 2020/02/18 18:01

    回答ありがとうございます。
    もう少し調べてみます。

    キャンセル

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

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

関連した質問

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