ユーザーパスワードをデータベースに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
これは、cript.digest()がバイト文字列を返し、文字列として出力しようとしているために発生しています。これを印刷可能な16進文字列に変換します。
簡単な解決策:Apacheの commons-codec library を使用します。
String password = new String(Hex.encodeHex(cript.digest()),
CharSet.forName("UTF-8"));
Apache共通コーデックライブラリの使用:
DigestUtils.sha1Hex("aff")
結果は0c05aa56405c447e6678b7f3127febde5c3a9238です
それでおしまい :)
ハッシュアルゴリズムの1つの反復は安全ではありません。速すぎます。ハッシュを何度も繰り返すことで、キーの強化を実行する必要があります。
さらに、パスワードをソルトしていません。これにより、「Rainbow tables」などの事前に計算された辞書に対する脆弱性が作成されます。
これを正しく実行するために独自のコードをロールバックする(または、いくつかの大ざっぱなサードパーティのブロートウェアを使用する)代わりに、Javaランタイムに組み込まれたコードを使用できます。 this answer 詳細については。
パスワードを正しくハッシュすると、_byte[]
_が得られます。これを16進数String
に変換する簡単な方法は、BigInteger
クラスを使用することです。
_String passwordHash = new BigInteger(1, cript.digest()).toString(16);
_
文字列が常に40文字であることを確認したい場合は、左側にゼロを追加する必要があります(String.format()
でこれを行うことができます)。
プロジェクトに追加の依存関係を追加したくない場合は、使用することもできます
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(message.getBytes("utf8"));
byte[] digestBytes = digest.digest();
String digestStr = javax.xml.bind.DatatypeConverter.printHexBinary(digestBytes);
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
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());
Springを使用する場合、非常に簡単です。
MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1");
String hash = encoder.encodePassword(password, "salt goes here");
不可逆的なパスワードの保存には、単純な標準ハッシュアルゴリズム以上のものが含まれます。
詳細については、例えば.
また、 http://en.wikipedia.org/wiki/Password-authenticated_key_agreement メソッドを使用して、クリアテキストのパスワードをサーバーに渡さないようにすることもできます。
Byte []をbase64文字列に変換するのはどうですか?
byte[] chkSumBytArr = digest.digest();
BASE64Encoder encoder = new BASE64Encoder();
String base64CheckSum = encoder.encode(chkSumBytArr);
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());
_
digest()は、デフォルトのエンコーディングを使用して文字列に変換しているバイト配列を返します。あなたがしたいのは、base64エンコードです。
echo -n "aff" | sha1sumは正しい出力を生成します(エコーはデフォルトで改行を挿入します)
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.reset();
messageDigest.update(password.getBytes("UTF-8"));
String sha1String = new BigInteger(1, messageDigest.digest()).toString(16);
このコードも使用できます(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; }
最初に結果を16進エンコードする必要があります。 MessageDigest
は、人間が読み取れるハッシュではなく、「生の」ハッシュを返します。
編集:
@thejhは、動作するはずのコードへのリンクを提供しました。個人的には、 Bouncycastle または Apache Commons Codec を使用して仕事をすることをお勧めします。 Bouncycastleは、他の暗号関連の操作を行う場合に適しています。