web-dev-qa-db-ja.com

Java StringのSHA-1ダイジェストの16進表現を計算する

ユーザーパスワードをデータベースにsha1ハッシュとして保存しています。

残念ながら、私は奇妙な答えを得ています。

私はこのように文字列を保存しています:

MessageDigest cript = MessageDigest.getInstance("SHA-1");
              cript.reset();
              cript.update(userPass.getBytes("utf8"));
              this.password = new String(cript.digest());

このようなものが欲しかった->

aff-> "0c05aa56405c447e6678b7f3127febde5c3a9238"

のではなく

aff->�V@\D〜fx����:�8

57

これは、cript.digest()がバイト文字列を返し、文字列として出力しようとしているために発生しています。これを印刷可能な16進文字列に変換します。

簡単な解決策:Apacheの commons-codec library を使用します。

String password = new String(Hex.encodeHex(cript.digest()),
                             CharSet.forName("UTF-8"));
40
Jason Nichols

Apache共通コーデックライブラリの使用:

DigestUtils.sha1Hex("aff")

結果は0c05aa56405c447e6678b7f3127febde5c3a9238です

それでおしまい :)

102
altumano

ハッシュアルゴリズムの1つの反復は安全ではありません。速すぎます。ハッシュを何度も繰り返すことで、キーの強化を実行する必要があります。

さらに、パスワードをソルトしていません。これにより、「Rainbow tables」などの事前に計算された辞書に対する脆弱性が作成されます。

これを正しく実行するために独自のコードをロールバックする(または、いくつかの大ざっぱなサードパーティのブロートウェアを使用する)代わりに、Javaランタイムに組み込まれたコードを使用できます。 this answer 詳細については。

パスワードを正しくハッシュすると、_byte[]_が得られます。これを16進数Stringに変換する簡単な方法は、BigIntegerクラスを使用することです。

_String passwordHash = new BigInteger(1, cript.digest()).toString(16);
_

文字列が常に40文字であることを確認したい場合は、左側にゼロを追加する必要があります(String.format()でこれを行うことができます)。

24
erickson

プロジェクトに追加の依存関係を追加したくない場合は、使用することもできます

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(message.getBytes("utf8"));
byte[] digestBytes = digest.digest();
String digestStr = javax.xml.bind.DatatypeConverter.printHexBinary(digestBytes);
10
Manish Sahni

Crypt.digest()メソッドはbyte []を返します。このバイト配列は正しいSHA-1の合計ですが、暗号化ハッシュは通常16進形式で人間に表示されます。ハッシュの各バイトは、2桁の16進数になります。

バイトを16進数に安全に変換するには、次を使用します。

// %1$ == arg 1
// 02  == pad with 0's
// x   == convert to hex
String hex = String.format("%1$02x", byteValue);

このコードスニペットは、charを16進数に変換するために使用できます

/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 
import Java.io.*;

public class UnicodeFormatter  {

   static public String byteToHex(byte b) {
      // Returns hex String representation of byte b
      char hexDigit[] = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
      };
      char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
      return new String(array);
   }

   static public String charToHex(char c) {
      // Returns hex String representation of char c
      byte hi = (byte) (c >>> 8);
      byte lo = (byte) (c & 0xff);
      return byteToHex(hi) + byteToHex(lo);
   }
}

Javaのバイトを操作すると、エラーが発生しやすくなります。すべてを二重にチェックし、奇妙なケースもテストします。

また、SHA-1よりも強力なものの使用を検討する必要があります。 http://csrc.nist.gov/groups/ST/hash/statement.html

5
cyber-monk

Google Guava の場合:

Maven:

<dependency>
   <artifactId>guava</artifactId>
   <groupId>com.google.guava</groupId>
   <version>14.0.1</version>
</dependency>

サンプル:

HashCode hashCode = Hashing.sha1().newHasher()
   .putString(password, Charsets.UTF_8)
   .hash();            

String hash = BaseEncoding.base16().lowerCase().encode(hashCode.asBytes());
3
Tho

Springを使用する場合、非常に簡単です。

MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1");
String hash = encoder.encodePassword(password, "salt goes here");
2
Vedran

不可逆的なパスワードの保存には、単純な標準ハッシュアルゴリズム以上のものが含まれます。

  1. ブルートフォース攻撃を遅くするために複数のラウンドを行う
  2. パスワードに加えて、レコードごとの「塩」をハッシュアルゴリズムへの入力として使用して、辞書攻撃を実行しにくくし、出力の衝突を回避します。
  3. ハッシュアルゴリズムへの入力として、アプリケーション構成設定である「pepper」を使用して、不明な「pepper」を含む盗まれたデータベースダンプを役に立たなくします。
  4. 入力をパディングして、いくつかのハッシュアルゴリズムの弱点を回避します。ハッシュを変更することにより、パスワードを知らなくてもパスワードに文字を追加できます。

詳細については、例えば.

また、 http://en.wikipedia.org/wiki/Password-authenticated_key_agreement メソッドを使用して、クリアテキストのパスワードをサーバーに渡さないようにすることもできます。

2
Stefan L

Byte []をbase64文字列に変換するのはどうですか?

    byte[] chkSumBytArr = digest.digest();
    BASE64Encoder encoder = new BASE64Encoder();
    String base64CheckSum = encoder.encode(chkSumBytArr);
1
Mahes

UTF-8を使用するには、次のようにします。

_userPass.getBytes("UTF-8");
_

また、ダイジェストからBase64 Stringを取得するには、次のようなことができます。

_this.password = new BASE64Encoder().encode(cript.digest());
_

MessageDigest.digest()はバイト配列を返すため、Apacheの Hex Encoding (より単純な)を使用して文字列に変換できます。

例えば。

_this.password = Hex.encodeHexString(cript.digest());
_
1
Buhake Sindi

digest()は、デフォルトのエンコーディングを使用して文字列に変換しているバイト配列を返します。あなたがしたいのは、base64エンコードです。

1
PaulJWilliams

echo -n "aff" | sha1sumは正しい出力を生成します(エコーはデフォルトで改行を挿入します)

1
Krystal
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        messageDigest.reset();
        messageDigest.update(password.getBytes("UTF-8"));
        String sha1String = new BigInteger(1, messageDigest.digest()).toString(16);
1
Pankaj Kumar

このコードも使用できます(crackstation.netから):

private static String toHex(byte[] array) { BigInteger bi = new BigInteger(1, array); String hex = bi.toString(16); int paddingLength = (array.length * 2) - hex.length(); if(paddingLength > 0) return String.format("%0" + paddingLength + "d", 0) + hex; else return hex; }

1
Mahdi

最初に結果を16進エンコードする必要があります。 MessageDigestは、人間が読み取れるハッシュではなく、「生の」ハッシュを返します。

編集:

@thejhは、動作するはずのコードへのリンクを提供しました。個人的には、 Bouncycastle または Apache Commons Codec を使用して仕事をすることをお勧めします。 Bouncycastleは、他の暗号関連の操作を行う場合に適しています。