Java-apache.shiro.crypto.hash SimpleHash
想仔细了解一下后端认证的做法,于是来看下相关源码。
基本的做法是。网络请求得到明文的password和salt,然后将salt转成hash与数据库中的hash比对。
将password和salt转成hash的过程中,用到了Sha256Hash这个类。所以想看下这个类做了什么。
1 2 3
| public class Sha256Hash extends SimpleHash {}
public class SimpleHash extends AbstrachHash {}
|
SimpleHash 继承 AbstrachHash,那么直觉上AbstractHash应该是官方提供的一个实现hash的抽象类。
Sha256Hash
Sha256Hash的源码不长,基本就是创建了一个默认algorithm=SHA-256的一个SimpleHash,那么还是得看一下SimpleHash做了什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Sha256Hash extends SimpleHash { public static final String ALGORITHM_NAME = "SHA-256";
public Sha256Hash () {super("SHA-256");}
public Sha256Hash (Object source) { super("SHA-256", source) }
public Sha256Hash (Object source, Object salt) { super("SHA-256", source, salt); }
public Sha256Hash(Object source, Object salt, int hashIterations) { super("SHA-256", source, salt, hashIterations); }
public static Sha256Hash fromHexString(String hex) { Sha256Hash hash = new Sha256Hash(); hash.setBytes(Hex.decode(hex)); return hash; }
public static Sha256Hash fromBase64String(String base64) { Sha256Hash has = new Sha256Hash(); hash.setBytes(Base64.decode(base64)); return hash; } }
|
SimpleHash
SimpleHash的构造方法最终都会调用这个构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) throws CodeException, UnknownAlgorithmException { this.hexEncoded = null; this.base64Encoded = null; if (!StringUtils.hasText(algorithmName)) { throw new NullPointerException("algorithmName argument cannot be null or empty."); } else { this.algorithmName = algorithmName; this.iterations =Math.max(1, hashIterations);
ByteSource saltBytes = null; if (salt != null) { saltBytes = this.convertSaltToBytes(salt); this.salt = saltBytes; }
ByteSource sourceBytes = this.convertSourceToBytes(source); this.hsah(sourceBytes, saltBytes, hashIterations); } }
|
在构造方法中,主要做的是
- 判断参数是否为空
- 将Object类型的参数转化为ByteSource类型
- 调用 hash方法
ByteSource
先来看看ByteSource,他是做什么的
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface ByteSource { byte[] getBytes();
String toHex();
String toBase64();
boolean isEmpty();
public static final class Util { } }
|
这样看好像看不出什么东西。那就去看看转化为ByteSource的方法中做了什么事情.
convertSaltToBytes & convertSourceToBytes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| protected ByteSource convertSaltToBytes (Object salt) { return this.toByteSource(salt); }
protected ByteSource convertSourceToBytes (Object source) { return this.toByteSource(source); }
protected ByteSource toByteSource(Object o) { if (o == null) { return null; } else if (o instanceof ByteSource) { return (ByteSource)o; } else { byte[] bytes = this.toBytes(o); return Util.bytes(bytes); } }
|
上面的两个核心部分是
- this.Bytes(o)
- Util.Bytes(bytes)
this.Bytes(o)
toBytes 这个方法是在CodeSupport这个类里面的
该方法做的事情有:
- 判断传入是否为空
- 如果传入为 byte[], ByteSource, char[], String, File, InputStream, 则执行对应的转换为bytes的操作
- 大致上最终是通过方法进行获取bytes
- 方法
- String.getBytes(encoding)
- 默认
Util.Bytes(bytes)
这里的Util是[ByteSource](‘### ByteSource’)中的静态工具类
1 2 3
| public static ByteSource bytes(byte[] bytes) { return new SimpleByteSource(bytes); }
|
可以看到这也是返回了一个SimpleByteSource实例,先不展开。
this.hash
得到ByteSource的实例之后,调用hash。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodeException, UnknownAlgorithmException { byte[] saltBytes = salt != null ? salt.getBytes() : null; byte[] hashedBytes = this.hash(source.getBytes(), saltBytes, hashIterations); this.setBytes(hashedBytes); }
protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { MessageDigest digest = this.getDigest(this.getAlgorithmName()); if (salt != null) { digest.reset(); digest.update(salt); }
byte[] hashed = digest.digest(bytes); int iterations = hashIterations -1;
for(int i =0; i < iterations; ++i) { digest.reset(); hashed = digest.digest(hashed); }
return hashed; }
public void setBytes(byte[] alreadyHashedBytes) { this.bytes = alreadyHashedBytes; this.hexEncoded = null; this.base64Encoded = null; }
protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException { try { return MessageDigest.getInstance(algorithmName); } catch (...) { ... } }
|
在这部分中可以看见,最终实现从source + salt => byte 是通过MessageDigest这个类来实现的,于是我们来看看MessageDigest这个类
MessageDigest
MessageDigest 是 java.security这个包中的类。相关源码先不看,只看看这几个方法做了什么
- MessageDigest.getInstance(algorithmName)
- digest.reset();
- digest.update();
- digest.digest(bytes);
getInstance
获取单例,但是具体怎么获取的,挺复杂的。
最终通过main方法调试debug的方式获取到了SHA-256对应的MessageDigest实现类的className
1 2 3 4 5 6 7
| public static void main(String args) { Sha256Hash sha256Hsah = new Sha256Hash(); MessageDigest MessageDigest = sha256Hsah.getDigest(sha256Hsah.getAlgorithmName()); System.out.println(digest.getClass().getName()) }
|
1 2 3 4 5 6 7 8 9
| abstract class SHA2 extends DigestBase {
public static class SHA256 extends SHA2 { private static final int[] INITIAL_HASHES = new int[]{....};
public SHA256() { super("SHA-256", 32, INITIAL_HASHES)} } }
|
其他部分的实现
其他部分最终会在DigestBase 和 SHA2这个两个类中得到实现
DigestBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| abstract class DigestBase extends MessageDigestSpi implements Cloneable { private byte[] oneByte; private final String algorithm; private final int digestLength; private final int blockSize; byte[] buffer; private int bufOfs; long bytesProcessed; static final byte[] padding = new byte[136];
DigestBase (String var1, int var2, int var3) { this.algorithm = var1; this.digestLength = var2; this.blockSize = var3; this.buffer = new byte[var3]; }
protected final int engineGetDigestLength() { return this.digestLength;}
protected final void engineUpdate(byte var1) { if(this.oneByte == null) { this.oneByte = new Byte[1]; }
this.oneByte[0] = var1; this.engineUpdate(this.oneByte, 0,1); }
protected final void engineUpdate(byte[] var1, int var2, int var3) { if (var3 != 0) { if (var2 >= 0, && var3 >= 0 && var2 <= var1.length - var3) {
} } } }
|
SHA2
AbstrachHash