블로피시 암호에 기반을 둔 암호화 해시 함수
Blowfish는 키(key)방식의 대칭형 블록 암호이다.
소프트웨어에서 양호한 암호화 속도를 제공하지만 현재는 고급 암호화 표준이 더 많은 눈길을 끌고 있다.
단방향 해시함수의 취약점을 보완하기위하여 등장
단방향 해시함수의 취약점
단방향 해시함수 보완
솔팅(Salting)
키 스트레칭(Key stretching)
bcrypt의 해시문자열은 다음과 같다
$2b$[cost]$[22 character salt][31 character hash]
ex)
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
\/ \/ \____________________/\_____________________________/
Alg Cost Salt Hash
Alg : 2a = 해시알고리즘 식별자 (bcrypt)
Cost : 10 = Cost factor : 2**10 =⇒ 1.024 rounds
Salt : N9qo8uLOickgx2ZMRZoMye = 16바이트(128비트) 솔트
Hash : IjZAgcfl7p92ldGxad68LJZdL17lhWy
:24 바이트 해시
Function bcrypt
Input:
cost: Number (4..31) log2(Iterations). e.g. 12 ==> 212 = 4,096 iterations
salt: array of Bytes (16 bytes) random salt
password: array of Bytes (1..72 bytes) UTF-8 encoded password
Output:
hash: array of Bytes (24 bytes)
//Initialize Blowfish state with expensive key setup algorithm
//P: array of 18 subkeys (UInt32[18])
//S: Four substitution boxes (S-boxes), S0...S3. Each S-box is 1,024 bytes (UInt32[256])
P, S ← EksBlowfishSetup(cost, salt, password)
//Repeatedly encrypt the text "OrpheanBeholderScryDoubt" 64 times
ctext ← "OrpheanBeholderScryDoubt" //24 bytes ==> three 64-bit blocks
repeat (64)
ctext ← EncryptECB(P, S, ctext) //encrypt using standard Blowfish in ECB mode
//24-byte ctext is resulting password hash
return Concatenate(cost, salt, ctext)
사용방법
bcrypt.hashpw(비밀번호.encode, gensalt())
이 함수는 암호나 다른 어떤 string도 hashing해준다. 두 가지 인자를 받는다.
A string(bytes)
Salt : bcrypt.gensalt()를 통해 자동으로 랜덤값을 받을수있다.
예제
import bcrypt
password = '1234'
hash_password = bcrypt.hashpw(password.encode('utf-8'),bcrypt.gensalt())
hash_password
'2a$12acusMujY4QblouspQ.681uJaTU1T.rfsxOalWzUx90yyrV46im.1G'
예제 타입
password = str
hash_password = str
qassword.encode(’utf-8’) = byte
해시패스워드 과정
패스워드를 1번 인자로 받고 encode매서드로 byte타입으로 변경후 gensalt를 통해 문자열 추가후 암호화
💡 현재 3.9버전에서는 decode를 안써도 byte타입에서 str타입으로 변환 =⇒ 라이브러리의 문제로 보임 3.9 이하의 버전에서는 hash_password = bcrypt.hashpw(password.encode('utf-8'),bcrypt.gensalt()).decode(’utf-8’) 으로 타입변환을 해주어야한다. 이유는 추후 비밀번호를 비교하는과정에서 byte타입이 아닌 str 타입으로 비교하기 때문에비밀번호가 데이터 베이스에 저장된 값과 동일한지 확인해볼수 있는 함수이다.
b = '1234'
bcrypt.checkpw(b.encode('utf-8'), hash_password)
True
bcrypt.checkpw(b.encode('utf-8'), hash_password)
True
d = '12345'
bcrypt.checkpw(d.encode('utf-8'), hash_password)
False
이미 위에서 실행한
import bcrypt
password = '1234'
hash_password = bcrypt.hashpw(password.encode('utf-8'),bcrypt.gensalt())
hash_password
'2a$12acusMujY4QblouspQ.681uJaTU1T.rfsxOalWzUx90yyrV46im.1G'
이곳의 hash_password에 salt값이 있기 때문에 hashpw인자에서 gensalt()를 추가 실행할 필요가 없다.
로그인 시에는 저장 되어있는 salt값과 매칭이 되는지 확인 하기 때문에 password를 만들때만 gensalt한다.