자바는 다른 프로그래밍 언어와는 다르게 Hash를 자주 사용합니다.
그러면 어느 부분에서 사용되는지 한 번 알아보겠습니다!!
해시 함수(hash function) 또는 해시 알고리즘(hash algorithm) 또는 해시함수알고리즘(hash函數algorithm)은 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다. 해시 함수에 의해 얻어지는 값은 해시 값, 해시 코드, 해시 체크섬 또는 간단하게 해시라고 한다
해시란 보통 입력값을 임의의 고정된 길이의 데이터로 바꿔주는 함수입니다.
위 그림과 같이 key 값으로 이름이 들어왔다면 이를 고정된 길이의 문자열/정수 등으로 바꿔주는 역할을 수행하는 함수입니다.
보통 key 값을 임의의 데이터로 변경하는 과정을 해싱한다고 표현하죠
해시 알고리즘, 특히 좋은 해시 알고리즘은 다음과 같은 특징들을 가지고 있습니다
여기서 하나 알고 넘어가야하는 개념이 하나 있습니다.
바로 해시 충돌입니다.
해시 충돌이란 서로 다른 원문에 대한 해싱에 대해 같은 결과를 리턴해줄 때 발생합니다.
서로 다른 두 객체의 해시 값이 같은 상황이라고 할 수 있죠
일전에 봤던 그림입니다.
현재의 해시 함수는 John Smith
와 Sandra Dee
의 해싱의 결괏값이 같습니다.
이와 같은 경우 해시가 충돌한다고 합니다.
자바에서 사용하는 여러 해시 알고리즘들은 다음과 같습니다.
MD5 알고리즘은 널리 사용되는 암호 해시 함수입니다.
128비트(16바이트) 의 해시 값을 생성해 내는 함수입니다.
//import statements
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
// A Java program that uses the MD5 to do the hashing public class MD5 {
public static String getMd5(String input) {
try {
// invoking the static getInstance() method of the MessageDigest class
// Notice it has MD5 in its parameter.
MessageDigest msgDst = MessageDigest.getInstance("MD5");
// the digest() method is invoked to compute the message digest
// from an input digest() and it returns an array of byte
byte[] msgArr = msgDst.digest(input.getBytes());
// getting signum representation from byte array msgArr
BigInteger bi = new BigInteger(1, msgArr);
// Converting into hex value
String hshtxt = bi.toString(16);
while (hshtxt.length() < 32) {
hshtxt = "0" + hshtxt;
}
return hshtxt;
}
// for handling the exception
catch (NoSuchAlgorithmException abc) {
throw new RuntimeException(abc);
}
}
// main method code
public static void main(String argvs[]) throws NoSuchAlgorithmException {
String str = "JavaTpoint";
String hash = getMd5(str);
str = "'JavaTpoint'";
System.out.println("The HashCode Generated for " + str + " is: " + hash);
}
}
5f4dcc3b5aa765d61d8327deb882cf99
하지만 MD5 알고리즘은 보안상 취약하고, 해시 충돌이 빈번하게 일어나는 단점을 가지고 있습니다.
SHA 계열 알고리즘은 MD5보다 개선된 알고리즘으로 뒤에 붙은 숫자를 기준으로 원하는 길이의 문자열을 생성해주는 해싱 알고리즘입니다.
// import statements import java.security.NoSuchAlgorithmException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;
// A Java program to find the SHA-256 hash value public class SHAExample {
public static byte[] obtainSHA(String s) throws NoSuchAlgorithmException {
// Static getInstance() method is invoked with the hashing SHA-256
MessageDigest msgDgst = MessageDigest.getInstance("SHA-256");
// the digest() method is invoked
// to compute the message digest of the input
// and returns an array of byte
return msgDgst.digest(s.getBytes(StandardCharsets.UTF_8));
}
public static String toHexStr(byte[] hash) {
// Converting the byte array in the signum representation
BigInteger no = new BigInteger(1, hash);
// Converting the message digest into the hex value
StringBuilder hexStr = new StringBuilder(no.toString(16));
// Padding with tbe leading zeros
while (hexStr.length() < 32) {
hexStr.insert(0, '0');
}
return hexStr.toString();
}
// main method
public static void main(String argvs[]) {
try {
System.out.println("The HashCode produced by SHA-256 algorithm for strings: ");
String str = "JavaTpoint";
String hash = toHexStr(obtainSHA(str));
System.out.println("\n" + str + " : " + hash);
str = "India is a great country.";
hash = toHexStr(obtainSHA(str));
System.out.println("\n" + str + " : " + hash);
}
// handling exception
// usually raised when some absurd algorithm is used for doing the hash work
catch (NoSuchAlgorithmException obj) {
System.out.println("An exception is generated for the incorrect algorithm: " + obj);
}
}
}
The HashCode produced by SHA-256 algorithm for strings:
JavaTpoint : f9142e5ca706378c1c7f9daf6782dcff8197ef1ecfd4075b63dae2f40186afa6
India is a great country. :e28335e1124e47ebf4c0b6cb87a34737b70d539241641c60c1969602a7b46cf9
좋아 보이지만 더 좋은 해싱 알고리즘이 존재합니다.
바로 PBKDF2
, BCrypt
, SCrypt
입니다.
PBKDF2 사용 예제
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = factory.generateSecret(spec).getEncoded();
BCrypt
와 SCrypt
알고리즘은 PBKDF2
알고리즘과 다르게 순수 자바어플리케이션에서는 사용할 수 없습니다.
위 알고리즘들은 Spring Security 라이브러리에서 사용할 수 있습니다.
PasswordEncoder
의 구현체로서 각각 존재하게 되고 이들을 사용할 수 있습니다.
두 자료구조 모두 Map
의 구현체입니다.
둘 다 Key - Value
형태로 데이터를 저장하는데, 각각은 세부적으로 어떤 차이점이 있는지 알아보겠습니다.
HashMap
은 Java에서 사용하는 대표적인 Key-Value 형식의 자료구조입니다.
웬만하면 Map의 구현체로 HashMap
이 사용되죠.
그리고 데이터를 처리하는 속도가 매우 빠릅니다!
하지만 thread-safe 하지 않아서 멀티 쓰레드 환경에서 사용하기에는 적합하지 않습니다.
싱글 쓰레드
환경이 적합하죠
참고로 HashMap
에서 hash값은 객체의 hashCode를 이용합니다!
put(K key, V value)
putVal()
hash()
HashTable
은 HashMap
과는 다르게 동기화를 보장해주는 자료구조 입니다!
그렇기 때문에 속도는 HashMap
에 비하면 살짝 느리지만 멀티 쓰레드
환경에선 사용하기에 적합합니다!!
synchronized 키워드로 멀티쓰레드 환경에서 동기화를 보장한다.
그런데 동기화를 보장해주는 Map
의 구현체가 또 존재하는데요
바로 ConcurrentHashMap
입니다!
HashTable
과 마찬가지로 ConcurrentHashMap
또한 멀티 쓰레드 환경에서 동기화를 보장합니다
그렇다면 HashTable
과의 차이점은 과연 무엇일까요??
바로 ConcurrentHashMap
은 동기화가 아닌 세분화된 락을 사용해 성능이 더 좋습니다.
마지막으로 hashCode
입니다.
hashCode
는 자바의 가장 최상단 클래스인 Object
클래스 내부에 정의되어있는 메소드이자, 객체를 식별할 수 있는 정수형 코드입니다.
그리고 hashcode는 두 가지 특성을 가지고 있습니다.