
public interface PasswordEncoder {
String encode(CharSeqeunce rawPassword);
boolean matches(CharSeqeunce rawPassword, String encodedPassword);
default boolean upgradeEncoding(String encodedPassword);
}
주어진 문자열을 이용해 해시를 제공하거나 암호화를 수행한 결과를 반환.
주어진 문자열과 기존에 인코딩된 문자열이 일치하는지 확인하는 메서드

String,StringBuilder, StringBuffer와 같은 클래스가 상속받는 인터페이스
문자열을 다루는 여러 추상 메서드를 가지고 있다.
charAt, length, isEmpty등
upgradeEncoding은 기본구현이 항상 false를 리턴한다.
시간이 지나면서 더 강력한 알고리즘으로 암호화할 필요성이 생겨 true값을 반환하게 되면 다시 인코딩 한다.
인코딩을 하지않고 단순 텍스트를 비교하는 암호 인코더 구현
public class PlainTextPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.equals(encodedPassword);
}
}
SHA-512 해싱 알고리즘을 이용한 암호인코더 구현
public class Sha512PasswordEncoder implements PAsswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return hashWithSHA512(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String hashedPassword = encode(rawPassword);
return encodedPassword.equals(hashedPassword);
}
}
스프링 시큐리티에서 제공하는 PasswordEncoder
NoOpPasswordEncoder
암호인코딩x 일반텍스트 유지
싱글톤 설계이기 때문에 getInstance메서드를 통해 호출
StandardPasswordEncoder
SHA-256 해쉬 알고리즘 사용 약한 해싱 알고리즘이기 때문에 이제는 안쓴다.
Pbkdf2PasswordEncoder
PBKDF2이용
생성자 옵션3개 존재
인코딩에 사용되는 secretKey, 인코딩 반복 횟수(기본값 185000), 해시 값의 길이(기본값 256비트)
반복 횟수와 해시 값의 길이에 비례해서 암호는 강력해지지만 소비하는 리소스 증가로 성능 저하가 발생.
BCryptPasswordEncoder
bcrypt 해싱 함수 사용
생성자 옵션2개 존재
강도 계수-해싱 작업의 반복횟수에 영향을 줌.
secureRandom인스턴스-솔트값 생성 MD5나 SHA와는 달리 고유 솔트값을 생성해 해슁 작업에 사용하기 때문에 같은 비밀번호더라도 솔트값에 따라 해쉬값이 다르게 나옴.
내부적으로 BCryptPasswordEncoder가 솔트값을 관리함.
SCrpytPasswordEncoder
scrypt 해싱 함수 사용
5개의 생성자 매개변수 존재
순서대로 CPU비용, 메모리 비용, 병렬화 계수, 키 길이, 솔트 길이이다.
DelegatingPasswordEncoder
여러 PasswordEncoder구현을 Map에 저장해 접두사를 통해 원하는 구현에 작업을 위임
@Configuration
public class ProjectConfig {
@Bean
public PasswordEncoder passwordEncoder() {
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
return new DelegatingPasswordEncoder("bcrypt", encoders);
}
}
DelegatingPasswordEncoder("bcrypt", encoders);
첫번째 인자가 bcrypt이기 때문에 접두사가 없을 때 기본값은 bcrypt가 된다.
스프링시큐리티는 편의를 위해 표준 PasswordEncoder구현에 대한 맵을 가진 DelegatingPasswordEncoder를 생성해주는 메서드가 존재
PasswordEncoderFactories.createDelegatingPasswordEncoder();
인코딩, 암호화, 해싱에 대한 정의
인코딩
주어진 입력에 대한 변환
암호화
출력을 얻기 위해 입력값과 키를 사용하는 특별한 유형의 인코딩
x가 입력, k가 키, y가 결과라고 했을때 함수로 나타내면 다음과 같다.
(x, k) → y
암호화 결과로 반대의 과정을 수행하는 것을 복호화 과정이라 한다.
(y, k) → x
복호화 할때 사용하는 키가 암호화 할때의 키와 같으면 대칭키라고 한다.
만일 다른 키를 사용한다면 비대칭키라고 한다.
해싱
복호화 과정은 불가능한 특별한 유형의 인코딩
따라서 y를 가지고 x를 알수는 없다. 또 다른 x를 가지고 y값을 비교하여 x의 일치를 확인한다.
일부 해쉬 함수는 솔트값을 이용하여 더 강한 암호화를 지원한다.
키생성기
암호화나 해싱을 위해서는 키가 필요하다
스프링시큐리티에는 문자열, 바이트를 반환하는 2가지 키생성기 인터페이스가 존재한다.
직접 문자열 키 생성기 인터페이스를 구현해도 되지만 문자열 키 생성기를 얻기 위해서는 KeyGenerators클래스에서 string메서드를 호출한다.
StringKeyGenerator keyGenerator = KeyGenerators.string();
String key = KeyGenerater.generateKey();
8바이트 길이의 데이터를 Base64로 인코딩해서 문자열로 변환후 반환.
BtyeKeyGenerator keyGenerator = KeyGenerators.secureRandom();
btye[] key = KeyGenerator.generateKey();
secureRandom 의 매개변수로 키의 길이를 지정할 수 있음.
데이터를 암호화 및 복호화하는 객체
키생성기와 마찬가지로 btyes, string을 위한 2가지 암호기가 존재.
메서드는 암호기가 수행해야하는 역활인 encrypt, decrypt를 가지고 있음.
Encryptors라는 팩토리 클래스에서 여러 암호기를 제공함
String salt = KeyGenerators.string().generateKey();
String key = "secret"; //예시
String valueToEncrypt = "hello";
BytesEncryptor e = Encryptors.standard(password, salt);
byte[] encrypted = e.encrypt(valueToEncrypt.getBytes());
byte[] decrypted = e.decrypt(encrypted);
//결과
//decrypted와 valueToEncrypt.getBtyes()의 값은 같다.
standard메서드를 호출하면 키 길이가 256바이트인 AES암호화를 이용한다.