SB相关问题
想对SB中的密码进行解密,就去了解了一下怎么解密。
SB中的加密方式,是哈希算法,听说这种算法是不可逆的。
那SB是怎么对密码进行验证的。
百度一下,我就知道
百度出来了两个名词,加密算法和哈希算法。
他们都说加密算法和哈希算法是不同的概念,因为加密算法,可解密,而哈希算法,不可逆。
但是SB就是用哈希算法加密的,所以说,哈希算法是加密算法。没毛病。
actually,加密算法,包罗万象。
加密算法包括哈希算法,对称加密算法,非对称加密算法。
即:
哈希算法是加密算法,但是加密算法不是哈希算法。
严格意义上讲,使用MD5、SHA等算法获取数据摘要值的过程应该称为哈希而不是加密,常用于签名,但是大家习惯用加密来说,也就是加密算法了。
常用的三类算法:
哈希加密:MD5、SHA1、HMAC
对称加密:DES、3DES、AES(加密解密使用同一个密钥)
非对称加密:RSA(私钥加密,公钥解密)
MD5安全性低,说是哈希算法不可逆,但解密也轻轻松松的。
言归正传,搜索了很多SB是怎么解密的,都是纯文字,说的有鼻子有眼的,也不知道是不是在忽悠我,还是直接看源码,比较有安全感。
PasswordEncoder接口
BCryptPasswordEncoder类实现了PasswordEncoder接口,接口里有两个方法,encode用于加密,matches用于匹配验证。
package org.springframework.security.crypto.password;
public interface PasswordEncoder {
String encode(CharSequence var1);
boolean matches(CharSequence var1, String var2);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
BCryptPasswordEncoder类实现
endode
// 按照一定规则生成盐,然后执行hashpw(明文,盐)方法。
// 记住这个方法,到匹配校对的时候,还会用到它,第一个参数是前端传入的明文,第二个参数是按照规则生成的盐。
public String encode(CharSequence rawPassword) {
String salt;
if (this.strength > 0) {
if (this.random != null) {
salt = BCrypt.gensalt(this.strength, this.random);
} else {
salt = BCrypt.gensalt(this.strength);
}
} else {
salt = BCrypt.gensalt();
}
return BCrypt.hashpw(rawPassword.toString(), salt);
}
// 生成盐
public static String gensalt(int log_rounds, SecureRandom random) {
if (log_rounds >= 4 && log_rounds <= 31) {
StringBuilder rs = new StringBuilder();
byte[] rnd = new byte[16];
random.nextBytes(rnd);
rs.append("$2a$");
if (log_rounds < 10) {
rs.append("0");
}
rs.append(log_rounds);
rs.append("$");
encode_base64(rnd, rnd.length, rs);
return rs.toString();
} else {
throw new IllegalArgumentException("Bad number of rounds");
}
}
// hashpw(明文,盐)方法中,对salt进行二次加工,生成real_salt,这个是最终的盐,之后生成加密字符串。
public static String hashpw(String password, String salt) throws IllegalArgumentException {
char minor = 0;
int off = false;
StringBuilder rs = new StringBuilder();
if (salt == null) {
throw new IllegalArgumentException("salt cannot be null");
} else {
int saltLength = salt.length();
if (saltLength < 28) {
throw new IllegalArgumentException("Invalid salt");
} else if (salt.charAt(0) == '$' && salt.charAt(1) == '2') {
byte off;
if (salt.charAt(2) == '$') {
off = 3;
} else {
minor = salt.charAt(2);
if (minor != 'a' || salt.charAt(3) != '$') {
throw new IllegalArgumentException("Invalid salt revision");
}
off = 4;
}
if (saltLength - off < 25) {
throw new IllegalArgumentException("Invalid salt");
} else if (salt.charAt(off + 2) > '$') {
throw new IllegalArgumentException("Missing salt rounds");
} else {
int rounds = Integer.parseInt(salt.substring(off, off + 2));
String real_salt = salt.substring(off + 3, off + 25);
byte[] passwordb;
try {
passwordb = (password + (minor >= 'a' ? "\u0000" : "")).getBytes("UTF-8");
} catch (UnsupportedEncodingException var13) {
throw new AssertionError("UTF-8 is not supported");
}
byte[] saltb = decode_base64(real_salt, 16);
BCrypt B = new BCrypt();
byte[] hashed = B.crypt_raw(passwordb, saltb, rounds);
rs.append("$2");
if (minor >= 'a') {
rs.append(minor);
}
rs.append("$");
if (rounds < 10) {
rs.append("0");
}
rs.append(rounds);
rs.append("$");
encode_base64(saltb, saltb.length, rs);
encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
return rs.toString();
}
} else {
throw new IllegalArgumentException("Invalid salt version");
}
}
}
matches
// 匹配时,传入参数:前端传入的明文,数据库中的密文。
// 首先校验密文是否BCrypt加密以及是否为空,之后执行checkpw(明文,密文)方法。
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword != null && encodedPassword.length() != 0) {
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
} else {
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}
// 执行equalsNoEarlyReturn(数据库中密文,hashpw(前端明文,数据库中密文))方法。
// 上边已经说过,hashpw()方法是最终获取加密字符串的。
// 就很清楚了,SB匹配逻辑,是将前端明文加密(采用同样的算法规则,同样的盐),然后与数据库中密文进行比较验证。
// hashpw(明文,盐),可以看到第二个参数是盐耶,可是匹配的时候,数据库中的密文是作为第二个参数(盐)传进去的。
// 所以可以得到我们库中的加密字符串中是存在盐的,这样匹配验证的时候,从加密字符串(salt)中获取到最终盐(real_salt),然后再对前端传入明文加密验证比对。
public static boolean checkpw(String plaintext, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
}
static boolean equalsNoEarlyReturn(String a, String b) {
char[] caa = a.toCharArray();
char[] cab = b.toCharArray();
if (caa.length != cab.length) {
return false;
} else {
byte ret = 0;
for(int i = 0; i < caa.length; ++i) {
ret = (byte)(ret | caa[i] ^ cab[i]);
}
return ret == 0;
}
}