Development Tip

Java는 문자열의 SHA-1 다이제스트에 대한 16 진 표현을 계산합니다.

yourdevel 2020. 12. 9. 21:53
반응형

Java는 문자열의 SHA-1 다이제스트에 대한 16 진 표현을 계산합니다.


사용자 암호를 db에 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 라이브러리 사용 :

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

Apache 공통 코덱 라이브러리 사용 :

DigestUtils.sha1Hex("aff")

결과는 0c05aa56405c447e6678b7f3127febde5c3a9238입니다.

그게 다야 :)


해시 알고리즘의 한 번 반복은 안전하지 않습니다. 너무 빠릅니다. 해시를 여러 번 반복하여 키 강화를 수행해야합니다.

또한 암호를 솔트하지 않습니다. 이것은 "레인보우 테이블"과 같이 미리 계산 된 사전에 취약성을 만듭니다.

이를 올바르게 수행하기 위해 자체 코드를 롤링 (또는 스케치 된 타사 블로 트웨어 사용)하는 대신 Java 런타임에 내장 된 코드를 사용할 수 있습니다. 자세한 내용은 이 답변 을 참조하십시오.

암호를 올바르게 해시하면 byte[]. 이것을 16 진수로 변환하는 쉬운 방법 StringBigInteger클래스를 사용하는 것입니다.

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

문자열이 항상 40 자인지 확인하려면 왼쪽에 0을 사용하여 패딩을 수행해야 할 수 있습니다 (를 사용하여 수행 할 수 있음 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 진수 형식으로 표시됩니다. 해시의 각 바이트는 두 개의 16 진수가됩니다.

바이트를 16 진수로 안전하게 변환하려면 다음을 사용하십시오.

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

이 코드 조각은 문자를 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


구글 구아바 :

메이븐 :

<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");

암호를 되돌릴 수없는 저장과 관련된 단순한 표준 해시 알고리즘 이상의 것이 있습니다.

  1. 무차별 대입 공격을 느리게 만들기 위해 여러 라운드를 수행하십시오.
  2. Use a per-record "salt" as input to the hash algorithm besides the password to make dictionary attacks less feasible and avoid output collisions.
  3. Use "pepper", an application-configuration-setting as input to the hash algorithm to make a stolen database-dump with an unknown "pepper" useless.
  4. Pad the input to avoid weaknesses in some hash algorithms e.g. where you could append a character to the password without knowing the password, by modifying the hash.

For more info, see e.g.

You could also use a http://en.wikipedia.org/wiki/Password-authenticated_key_agreement method to avoid passing the password in cleartext to the server at all.


digest() returns a byte array, which you're converting to a string using the default encoding. What you want to do is base64 encode it.


To use UTF-8, do this:

userPass.getBytes("UTF-8");

And to get a Base64 String from the digest, you can do something like this:

this.password = new BASE64Encoder().encode(cript.digest());

Since MessageDigest.digest() returns a byte array, you can convert it to String using Apache's Hex Encoding (simpler).

E.g.

this.password = Hex.encodeHexString(cript.digest());

How about converting byte[] to base64 string?

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

you can use this code too(from 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; }


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

echo -n "aff" | sha1sum produce the correct output (echo inserts a newline by default)


You need to hex encode the result first. MessageDigest returns a "raw" hash, rather than a human readable one.

Edit:

@thejh provided a link to code which should work. Personally, I'd suggest using either Bouncycastle or Apache Commons Codec to do the job. Bouncycastle would be good if you want to do any other crypto-related operations.

참고URL : https://stackoverflow.com/questions/4400774/java-calculate-hex-representation-of-a-sha-1-digest-of-a-string

반응형