SHA1へのJava文字列


158

私はJavaで単純な文字列からSHA1へのコンバーターを作成しようとしていますが、これは私が得たものです...

public static String toSHA1(byte[] convertme) {
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    } 
    return new String(md.digest(convertme));
}

それを渡すとtoSHA1("password".getBytes())[�a�ɹ??�%l�3~��.おそらくUTF-8のような単純なエンコードの修正であることがわかりますが、誰かが私が欲しいものを取得するために何をすべきか教えてもらえ5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8ますか?それとも私はこれを完全に間違っていますか?


アルゴリズムにはSHA1ハイフンがありません。違いがあるかどうかはわかりません。
スクラムマイスター、

を呼び出すときに文字エンコードを指定することをお勧めしますgetBytes()。たとえば、usetoSHA1("password".getBytes("UTF-8"))
Qwerky

回答:


183

UPDATE
を使用できApacheのコモンズコーデック、あなたのためにこの仕事をする(バージョン1.7以上)を。

DigestUtils.sha1Hex(stringToConvertToSHexRepresentation)

この提案をしてくれた@ Jon Onstottに感謝します。


古い答え
バイト配列を16進文字列に変換します。リアルのハウツーはあなたにどのように伝えます

return byteArrayToHexString(md.digest(convertme))

および(Realのハウツーからコピー)

public static String byteArrayToHexString(byte[] b) {
  String result = "";
  for (int i=0; i < b.length; i++) {
    result +=
          Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
  }
  return result;
}

ところで、Base64を使用すると、よりコンパクトな表現が得られる場合があります。Apache Commons Codec API 1.4には、すべての苦痛を取り除くためのこの素晴らしいユーティリティがあります。ここを参照


4
base64とsha1は非常に異なります。代替として推奨しないでください。
Ryan A.

13
@RyanA .:私が理解しているように、SHA1ハッシュを16進エンコードする代わりに(完全にSHA1の代わりとしてではなく)base64を提案しています。
helmbert 2013

まだ試していませんが、これがどのように機能するか説明していただけますか?
ジバイ2013

11
DigestUtils.sha1Hex("my string")ホイールを再発明する代わりに、などのライブラリを使用しないのはなぜですか(手動で16進数に変換する方法を知るのは興味深いですが)。
Jon Onstott、2015年

3
この回答が書かれたとき、DigestUtils(1.7は2012年9月にリリースされました)にはその機能がありませんでした。これを指摘してくれてありがとう。+1
Nishant 2015年

67

これは文字列をsha1に変換する私の解決策です。それは私のAndroidアプリでうまく機能します:

private static String encryptPassword(String password)
{
    String sha1 = "";
    try
    {
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(password.getBytes("UTF-8"));
        sha1 = byteToHex(crypt.digest());
    }
    catch(NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch(UnsupportedEncodingException e)
    {
        e.printStackTrace();
    }
    return sha1;
}

private static String byteToHex(final byte[] hash)
{
    Formatter formatter = new Formatter();
    for (byte b : hash)
    {
        formatter.format("%02x", b);
    }
    String result = formatter.toString();
    formatter.close();
    return result;
}

7
これがjava.util.Formatterであり、警告を回避するために最後にformatter.close()が必要であることを指定したい場合があります。
Eric Chen

Shoudln't encryptPassword("test")echo test|sha1sumLinuxターミナルでは同じ結果が出力されますか?彼らはしません。
TulainsCórdova2014

@TulainsCórdovaコンソールの呼び出しについて:を使用する場合echo test、改行を含む出力はにパイプされsha1sumます。末尾の改行なしでプレーンな文字列をハッシュしたい場合は、を使用できますecho -n test | sha1sum-nパラメータが作るecho改行を省略します。
MrSnrub 2018

問題は少なくなりますが、一般的にencryptPassword()は、認証データの保存に使用されているように聞こえます。シードは適用されないため、コーディングは辞書攻撃に対して脆弱であることに注意してください。これがアプリケーションの問題かどうか、セキュリティ環境を確認してください!
EagleRainbow


32

SHA-1(および他のすべてのハッシュアルゴリズム)はバイナリデータを返します。つまり、(Javaでは)がを生成するということbyte[]です。このbyte配列は特定の文字を表していません。つまり、単純にそれをStringあなたがしたような形に変えることはできません。

場合は、あなたが必要とするString、あなたはその形式に持っているbyte[]ように表すことができる方法で、String(そうでない場合は、単に続けるbyte[]の周りを)。

byte[]印刷可能な文字として任意のものを表す2つの一般的な方法は、BASE64または単純な16進文字列(つまり、それぞれbyteを2つの16進数で表す)です。hex-Stringを作成しようとしているようです。

別の落とし穴もあります:あなたはSHA-1のJavaのを取得したい場合String、あなたはそれを変換する必要があるStringbyte[]最初の(SHA-1の入力があるとしてbyte[]も)。単にmyString.getBytes()示したように使用する場合、プラットフォームのデフォルトのエンコーディングを使用するため、それを実行する環境に依存します(たとえば、OSの言語/ロケール設定に基づいて異なるデータを返す可能性があります)。

より良い解決策は、String次のbyte[]ように-to- 変換に使用するエンコーディングを指定することですmyString.getBytes("UTF-8")。ここでは、UTF-8(またはすべてのUnicode文字を表すことができる別のエンコーディング)を選択するのが最も安全です。


27

これは、文字列を16進形式に変換するときに使用できる単純なソリューションです。

private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest crypt = MessageDigest.getInstance("SHA-1");
    crypt.reset();
    crypt.update(password.getBytes("UTF-8"));

    return new BigInteger(1, crypt.digest()).toString(16);
}

警告:「0」で始まるハッシュのハッシュ生成は正しくありません。39文字の文字列を受け取ります。
philn 2016年

@philn解決策を提案できますか?
Nikita Koksharov、

1
十分な先行ゼロを持つbyte []から大きな整数を作成すると、これらの0は失われると思います。したがって、16進文字列表現「0」は存在せず、39文字以下のハッシュになります。上記のpetrnohejlsソリューションを使用しましたが、うまくいきます...
philn

25

Apache Commonsコーデックライブラリを使用するだけです。彼らはDigestUtilsと呼ばれるユーティリティクラスを持っています

詳細に入る必要はありません。


51
私は同意しません、詳細に入るのは全体の要点のようなものです
2011

12
問題は、詳細に入る時間があるかどうかです。重要なのは、通常、時間どおりにそれを達成することです。
DaTroop、

DigestUtilsはバイト配列を返すため、文字列表現を取得するには、Hex.encodeHexStringで実行する必要があります。Java:それは2014年であり、1ステップのshaメソッドはまだありません
ryber

5
1ステップSHA-1メソッド:String result = DigestUtils.sha1Hex("An input string"); o)
Jon Onstott

18

前述のように、Apache Commonsコーデックを使用します。Springの人にも推奨されています(SpringドキュメントのDigestUtilsを参照)。例えば:

DigestUtils.sha1Hex(b);

間違いなく、ここでは最高評価の回答を使用しません。


6

Base64エンコーディングを使用する必要があるため、正しく印刷されません。Java 8では、Base64エンコーダークラスを使用してエンコードできます。

public static String toSHA1(byte[] convertme) {
    md = MessageDigest.getInstance("SHA-1");
    return Base64.getEncoder().encodeToString((md.digest(convertme));
}

結果

これにより、期待される出力が得られます 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8


1
@DevenvそれはSHA-1です。3つのドットは、sha1に変換する元のコードを維持することを意味します。OPの元の問題は、文字列を正しく印刷することでした。
Eduardo Dennis

4

メッセージダイジェスト(ハッシュ)は、byte [] in byte [] outです。

メッセージダイジェストは、生のバイト配列を取り、生のバイト配列(別名byte[])を返す関数として定義されます。たとえば、SHA-1(Secure Hash Algorithm 1)のダイジェストサイズは160ビットまたは20バイトです。生のバイト配列は通常、UTF-8のような文字エンコーディングとして解釈できません。これは、すべての順序のすべてのバイトがそのエンコーディングとして正当であるとは限らないためです。したがって、それらをwithに変換します:String

new String(md.digest(subject), StandardCharsets.UTF_8)

いくつかの不正なシーケンスを作成するか、未定義のUnicodeマッピングへのコードポインターを持っている可能性があります。

[�a�ɹ??�%l3~��.

バイナリからテキストへのエンコード

そのために、バイナリからテキストへのエンコーディングが使用されます。ハッシュの場合、最もよく使用されるのはHEXエンコーディングまたはBase16です。基本的にバイトの値持つことができる0255(又は-128127のHEX表現に相当する符号付きの)0x00- 0xFF。したがって、16進数は必要な出力の長さを2倍にします。つまり、20バイトの出力では、40文字の16進数文字列が作成されます。例:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

16進エンコーディングを使用する必要はないことに注意してください。base64のようなものを使用することもできます。人間が読みやすく、パディングを必要とせずに出力長が定義されているため、16進数がよく使用されます。

JDK機能のみでバイト配列を16進数に変換できます。

new BigInteger(1, token).toString(16)

ただし、BigInteger指定されたバイト配列はバイト文字列ではなく数値として解釈されます。つまり、先行ゼロは出力されず、結果の文字列は40文字より短い場合があります。

ライブラリを使用してHEXにエンコードする

あなたは今可能性がコピーして、スタックオーバーフローからテストされていないバイトツー進法を貼り付けるかのような大規模な依存関係を使用しグアバ

ほとんどのバイト関連の問題を解決するために、これらのケースを処理するユーティリティを実装しました:bytes-java(Github)

メッセージダイジェストバイト配列を変換するには、次のようにします。

String hex = Bytes.wrap(md.digest(subject)).encodeHex();

または、組み込みのハッシュ機能を使用することもできます

String hex =  Bytes.from(subject).hashSha1().encodeHex();

2

SHA1ハッシュのBase 64表現:

String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));

1

これが機能しない理由は、を呼び出すときにString(md.digest(convertme))、暗号化されたバイトのシーケンスを文字列として解釈するようにJavaに指示しているためです。必要なのは、バイトを16進文字に変換することです。


0

バイト配列を16進文字列に変換します。

public static String toSHA1(byte[] convertme) {
    final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    byte[] buf = md.digest(convertme);
    char[] chars = new char[2 * buf.length];
    for (int i = 0; i < buf.length; ++i) {
        chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
        chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
    }
    return new String(chars);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.