1// 使用Expensive key setup算法初始化Blowfish状态 2state <- EksBlowfishSetup(cost, salt, password) // 这一步是整个算法中最耗时的步骤 3 4ctext <- "OrpheanBeholderScryDoubt" // 24 bytes,初始向量 5repeat (64) 6 ctext <- EncryptECB(state, ctext) // 使用 blowfish 算法的ECB模式进行加密 7 8return Concatenate(cost, salt, ctext)
// Expensive key setup Function EksBlowfishSetup Input: cost: Number (4..31) salt: array of Bytes (16 bytes) password: array of Bytes (1..72 bytes) Output: state: opaque BlowFish state structure
1state <- InitialState() 2state <- ExpandKey(state, salt, password) 3repeat (2 ^ cost) // 计算密集型 4 state <- ExpandKey(state, 0, password) 5 state <- ExpandKey(state, 0, salt) 6 7return state
Function ExpandKey(state, salt, password) Input: state: Opaque BlowFish state structure // 内部包含 P-array 和 S-box salt: array of Bytes (16 bytes) password: array of Bytes (1..72 bytes) Output: state: Opaque BlowFish state structure
// ExpandKey 是对输入参数进行固定的移位异或等运算,这里不列出
</code></pre></div></div>
<p>通过伪代码可以看出,通过制定不同的cost值,可以使得EksBlowfishSetup的运算次数大幅提升,从而达到慢哈希的目的。</p> <p><strong>Spring Security 中的 Bcrypt</strong></p> <p>理解了Bcrypt算法的原理,再来看Spring Security中的实现就很简单了。</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">crypto</span><span class="o">.</span><span class="na">bcrypt</span><span class="o">;</span><span class="o">...</span><span class="err">省略</span><span class="kn">import</span><span class="nn">...</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">BCryptPasswordEncoder</span> <span class="kd">implements</span> <span class="n">PasswordEncoder</span> <span class="o">{</span> <span class="o">...</span><span class="err">省略</span><span class="n">log</span><span class="o">...</span>
1<span class="kd">private</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">strength</span><span class="o">;</span> <span class="c1">// 相当于wiki伪代码中的cost,默认为10</span> 2 3<span class="kd">private</span> <span class="kd">final</span> <span class="n">SecureRandom</span> <span class="n">random</span><span class="o">;</span> <span class="c1">// CSPRNG</span> 4 5<span class="c1">// 构造函数</span> 6<span class="kd">public</span> <span class="nf">BCryptPasswordEncoder</span><span class="o">()</span> <span class="o">{</span> 7 <span class="k">this</span><span class="o">(-</span><span class="mi">1</span><span class="o">);</span> 8<span class="o">}</span> 9 10<span class="c1">// 相当于伪代码中的cost, 长度 4 ~ 31</span> 11<span class="kd">public</span> <span class="nf">BCryptPasswordEncoder</span><span class="o">(</span><span class="kt">int</span> <span class="n">strength</span><span class="o">)</span> <span class="o">{</span> 12 <span class="k">this</span><span class="o">(</span><span class="n">strength</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span> 13<span class="o">}</span> 14 15<span class="c1">// 构造函数</span> 16<span class="kd">public</span> <span class="nf">BCryptPasswordEncoder</span><span class="o">(</span><span class="kt">int</span> <span class="n">strength</span><span class="o">,</span> <span class="n">SecureRandom</span> <span class="n">random</span><span class="o">)</span> <span class="o">{</span> 17 <span class="k">if</span> <span class="o">(</span><span class="n">strength</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&&</span> <span class="o">(</span><span class="n">strength</span> <span class="o"><</span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">MIN_LOG_ROUNDS</span> <span class="o">||</span> <span class="n">strength</span> <span class="o">></span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">MAX_LOG_ROUNDS</span><span class="o">))</span> <span class="o">{</span> 18 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Bad strength"</span><span class="o">);</span> 19 <span class="o">}</span> 20 <span class="k">this</span><span class="o">.</span><span class="na">strength</span> <span class="o">=</span> <span class="n">strength</span><span class="o">;</span> 21 <span class="k">this</span><span class="o">.</span><span class="na">random</span> <span class="o">=</span> <span class="n">random</span><span class="o">;</span> 22<span class="o">}</span> 23 24<span class="c1">// 加密函数</span> 25<span class="kd">public</span> <span class="n">String</span> <span class="nf">encode</span><span class="o">(</span><span class="n">CharSequence</span> <span class="n">rawPassword</span><span class="o">)</span> <span class="o">{</span> 26 <span class="n">String</span> <span class="n">salt</span><span class="o">;</span> 27 <span class="k">if</span> <span class="o">(</span><span class="n">strength</span> <span class="o">></span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> 28 <span class="k">if</span> <span class="o">(</span><span class="n">random</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> 29 <span class="n">salt</span> <span class="o">=</span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">gensalt</span><span class="o">(</span><span class="n">strength</span><span class="o">,</span> <span class="n">random</span><span class="o">);</span> 30 <span class="o">}</span> 31 <span class="k">else</span> <span class="o">{</span> 32 <span class="n">salt</span> <span class="o">=</span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">gensalt</span><span class="o">(</span><span class="n">strength</span><span class="o">);</span> 33 <span class="o">}</span> 34 <span class="o">}</span> 35 <span class="k">else</span> <span class="o">{</span> 36 <span class="n">salt</span> <span class="o">=</span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">gensalt</span><span class="o">();</span> 37 <span class="o">}</span> 38 <span class="k">return</span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">hashpw</span><span class="o">(</span><span class="n">rawPassword</span><span class="o">.</span><span class="na">toString</span><span class="o">(),</span> <span class="n">salt</span><span class="o">);</span> 39<span class="o">}</span> 40 41<span class="c1">// 密码匹配函数</span> 42<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">matches</span><span class="o">(</span><span class="n">CharSequence</span> <span class="n">rawPassword</span><span class="o">,</span> <span class="n">String</span> <span class="n">encodedPassword</span><span class="o">)</span> <span class="o">{</span> 43 <span class="k">if</span> <span class="o">(</span><span class="n">encodedPassword</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">encodedPassword</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> 44 <span class="n">logger</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"Empty encoded password"</span><span class="o">);</span> 45 <span class="k">return</span> <span class="kc">false</span><span class="o">;</span> 46 <span class="o">}</span> 47 48 <span class="k">if</span> <span class="o">(!</span><span class="n">BCRYPT_PATTERN</span><span class="o">.</span><span class="na">matcher</span><span class="o">(</span><span class="n">encodedPassword</span><span class="o">).</span><span class="na">matches</span><span class="o">())</span> <span class="o">{</span> 49 <span class="n">logger</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"Encoded password does not look like BCrypt"</span><span class="o">);</span> 50 <span class="k">return</span> <span class="kc">false</span><span class="o">;</span> 51 <span class="o">}</span> 52 53 <span class="k">return</span> <span class="n">BCrypt</span><span class="o">.</span><span class="na">checkpw</span><span class="o">(</span><span class="n">rawPassword</span><span class="o">.</span><span class="na">toString</span><span class="o">(),</span> <span class="n">encodedPassword</span><span class="o">);</span> 54<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">security</span><span class="o">.</span><span class="na">crypto</span><span class="o">.</span><span class="na">bcrypt</span><span class="o">;</span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">BCrypt</span> <span class="o">{</span>
1<span class="c1">// 生成盐值的函数 "$2a$" + 2字节log_round + "$" + 22字节随机数Base64编码</span> 2<span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">gensalt</span><span class="o">(</span><span class="kt">int</span> <span class="n">log_rounds</span><span class="o">,</span> <span class="n">SecureRandom</span> <span class="n">random</span><span class="o">)</span> <span class="o">{</span> 3 <span class="k">if</span> <span class="o">(</span><span class="n">log_rounds</span> <span class="o"><</span> <span class="n">MIN_LOG_ROUNDS</span> <span class="o">||</span> <span class="n">log_rounds</span> <span class="o">></span> <span class="n">MAX_LOG_ROUNDS</span><span class="o">)</span> <span class="o">{</span> 4 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Bad number of rounds"</span><span class="o">);</span> 5 <span class="o">}</span> 6 <span class="n">StringBuilder</span> <span class="n">rs</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">();</span> 7 <span class="kt">byte</span> <span class="n">rnd</span><span class="o">[]</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="n">BCRYPT_SALT_LEN</span><span class="o">];</span> 8 9 <span class="n">random</span><span class="o">.</span><span class="na">nextBytes</span><span class="o">(</span><span class="n">rnd</span><span class="o">);</span> 10 11 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"$2a$"</span><span class="o">);</span> 12 <span class="k">if</span> <span class="o">(</span><span class="n">log_rounds</span> <span class="o"><</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span> 13 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"0"</span><span class="o">);</span> 14 <span class="o">}</span> 15 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">log_rounds</span><span class="o">);</span> 16 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"$"</span><span class="o">);</span> 17 <span class="n">encode_base64</span><span class="o">(</span><span class="n">rnd</span><span class="o">,</span> <span class="n">rnd</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="n">rs</span><span class="o">);</span> 18 <span class="k">return</span> <span class="n">rs</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span> <span class="c1">// 总长度29字节</span> 19<span class="o">}</span> 20 21<span class="cm">/** 22 * Hash a password using the OpenBSD bcrypt scheme 23 * @param password the password to hash 24 * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt) 25 * @return the hashed password 26 * @throws IllegalArgumentException if invalid salt is passed 27 */</span> 28<span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">hashpw</span><span class="o">(</span><span class="n">String</span> <span class="n">password</span><span class="o">,</span> <span class="n">String</span> <span class="n">salt</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IllegalArgumentException</span> <span class="o">{</span> 29 <span class="c1">// 该函数在验证阶段也会用到,因为前29字节为盐值,所以可以将之前计算过的密码哈希值做为盐值</span> 30 <span class="n">BCrypt</span> <span class="n">B</span><span class="o">;</span> 31 <span class="n">String</span> <span class="n">real_salt</span><span class="o">;</span> 32 <span class="kt">byte</span> <span class="n">passwordb</span><span class="o">[],</span> <span class="n">saltb</span><span class="o">[],</span> <span class="n">hashed</span><span class="o">[];</span> 33 <span class="kt">char</span> <span class="n">minor</span> <span class="o">=</span> <span class="o">(</span><span class="kt">char</span><span class="o">)</span> <span class="mi">0</span><span class="o">;</span> 34 <span class="kt">int</span> <span class="n">rounds</span><span class="o">,</span> <span class="n">off</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 35 <span class="n">StringBuilder</span> <span class="n">rs</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">();</span> 36 37 <span class="k">if</span> <span class="o">(</span><span class="n">salt</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> 38 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"salt cannot be null"</span><span class="o">);</span> 39 <span class="o">}</span> 40 41 <span class="kt">int</span> <span class="n">saltLength</span> <span class="o">=</span> <span class="n">salt</span><span class="o">.</span><span class="na">length</span><span class="o">();</span> 42 43 <span class="k">if</span> <span class="o">(</span><span class="n">saltLength</span> <span class="o"><</span> <span class="mi">28</span><span class="o">)</span> <span class="o">{</span> 44 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Invalid salt"</span><span class="o">);</span> 45 <span class="o">}</span> 46 47 <span class="k">if</span> <span class="o">(</span><span class="n">salt</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">!=</span> <span class="sc">'$'</span> <span class="o">||</span> <span class="n">salt</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">1</span><span class="o">)</span> <span class="o">!=</span> <span class="sc">'2'</span><span class="o">)</span> <span class="o">{</span> 48 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Invalid salt version"</span><span class="o">);</span> 49 <span class="o">}</span> 50 <span class="k">if</span> <span class="o">(</span><span class="n">salt</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">2</span><span class="o">)</span> <span class="o">==</span> <span class="sc">'$'</span><span class="o">)</span> <span class="o">{</span> 51 <span class="n">off</span> <span class="o">=</span> <span class="mi">3</span><span class="o">;</span> 52 <span class="o">}</span> 53 <span class="k">else</span> <span class="o">{</span> 54 <span class="n">minor</span> <span class="o">=</span> <span class="n">salt</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span> 55 <span class="k">if</span> <span class="o">(</span><span class="n">minor</span> <span class="o">!=</span> <span class="sc">'a'</span> <span class="o">||</span> <span class="n">salt</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="mi">3</span><span class="o">)</span> <span class="o">!=</span> <span class="sc">'$'</span><span class="o">)</span> <span class="o">{</span> 56 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Invalid salt revision"</span><span class="o">);</span> 57 <span class="o">}</span> 58 <span class="n">off</span> <span class="o">=</span> <span class="mi">4</span><span class="o">;</span> 59 <span class="o">}</span> 60 61 <span class="k">if</span> <span class="o">(</span><span class="n">saltLength</span> <span class="o">-</span> <span class="n">off</span> <span class="o"><</span> <span class="mi">25</span><span class="o">)</span> <span class="o">{</span> 62 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Invalid salt"</span><span class="o">);</span> 63 <span class="o">}</span> 64 65 <span class="c1">// Extract number of rounds</span> 66 <span class="k">if</span> <span class="o">(</span><span class="n">salt</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="n">off</span> <span class="o">+</span> <span class="mi">2</span><span class="o">)</span> <span class="o">></span> <span class="sc">'$'</span><span class="o">)</span> <span class="o">{</span> 67 <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"Missing salt rounds"</span><span class="o">);</span> 68 <span class="o">}</span> 69 <span class="n">rounds</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">salt</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">off</span><span class="o">,</span> <span class="n">off</span> <span class="o">+</span> <span class="mi">2</span><span class="o">));</span> 70 71 <span class="n">real_salt</span> <span class="o">=</span> <span class="n">salt</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">off</span> <span class="o">+</span> <span class="mi">3</span><span class="o">,</span> <span class="n">off</span> <span class="o">+</span> <span class="mi">25</span><span class="o">);</span> 72 <span class="k">try</span> <span class="o">{</span> 73 <span class="n">passwordb</span> <span class="o">=</span> <span class="o">(</span><span class="n">password</span> <span class="o">+</span> <span class="o">(</span><span class="n">minor</span> <span class="o">>=</span> <span class="sc">'a'</span> <span class="o">?</span> <span class="s">"\000"</span> <span class="o">:</span> <span class="s">""</span><span class="o">)).</span><span class="na">getBytes</span><span class="o">(</span><span class="s">"UTF-8"</span><span class="o">);</span> 74 <span class="o">}</span> 75 <span class="k">catch</span> <span class="o">(</span><span class="n">UnsupportedEncodingException</span> <span class="n">uee</span><span class="o">)</span> <span class="o">{</span> 76 <span class="k">throw</span> <span class="k">new</span> <span class="nf">AssertionError</span><span class="o">(</span><span class="s">"UTF-8 is not supported"</span><span class="o">);</span> 77 <span class="o">}</span> 78 79 <span class="c1">// 解析成16字节的字节数组</span> 80 <span class="n">saltb</span> <span class="o">=</span> <span class="n">decode_base64</span><span class="o">(</span><span class="n">real_salt</span><span class="o">,</span> <span class="n">BCRYPT_SALT_LEN</span><span class="o">);</span> 81 82 <span class="c1">// 这里new了一个新的对象是因为会用到BCrypt中int P[]和 int S[],扩展的密钥存放在这两个结构体</span> 83 <span class="n">B</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCrypt</span><span class="o">();</span> 84 <span class="n">hashed</span> <span class="o">=</span> <span class="n">B</span><span class="o">.</span><span class="na">crypt_raw</span><span class="o">(</span><span class="n">passwordb</span><span class="o">,</span> <span class="n">saltb</span><span class="o">,</span> <span class="n">rounds</span><span class="o">);</span> 85 86 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"$2"</span><span class="o">);</span> 87 <span class="k">if</span> <span class="o">(</span><span class="n">minor</span> <span class="o">>=</span> <span class="sc">'a'</span><span class="o">)</span> <span class="o">{</span> 88 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">minor</span><span class="o">);</span> 89 <span class="o">}</span> 90 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"$"</span><span class="o">);</span> 91 <span class="k">if</span> <span class="o">(</span><span class="n">rounds</span> <span class="o"><</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span> 92 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"0"</span><span class="o">);</span> 93 <span class="o">}</span> 94 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">rounds</span><span class="o">);</span> 95 <span class="n">rs</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"$"</span><span class="o">);</span> 96 <span class="n">encode_base64</span><span class="o">(</span><span class="n">saltb</span><span class="o">,</span> <span class="n">saltb</span><span class="o">.</span><span class="na">length</span><span class="o">,</span> <span class="n">rs</span><span class="o">);</span> 97 <span class="n">encode_base64</span><span class="o">(</span><span class="n">hashed</span><span class="o">,</span> <span class="n">bf_crypt_ciphertext</span><span class="o">.</span><span class="na">length</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">-</span> <span class="mi">1</span><span class="o">,</span> <span class="n">rs</span><span class="o">);</span> 98 <span class="k">return</span> <span class="n">rs</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span> 99<span class="o">}</span> 100 101<span class="cm">/** 102 * Perform the central password hashing step in the bcrypt scheme 103 * @param password the password to hash 104 * @param salt the binary salt to hash with the password 105 * @param log_rounds the binary logarithm of the number of rounds of hashing to apply 106 * @return an array containing the binary hashed password 107 */</span> 108<span class="kd">private</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">crypt_raw</span><span class="o">(</span><span class="kt">byte</span> <span class="n">password</span><span class="o">[],</span> <span class="kt">byte</span> <span class="n">salt</span><span class="o">[],</span> <span class="kt">int</span> <span class="n">log_rounds</span><span class="o">)</span> <span class="o">{</span> 109 <span class="kt">int</span> <span class="n">cdata</span><span class="o">[]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">[])</span> <span class="n">bf_crypt_ciphertext</span><span class="o">.</span><span class="na">clone</span><span class="o">();</span> 110 <span class="kt">int</span> <span class="n">clen</span> <span class="o">=</span> <span class="n">cdata</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> 111 <span class="kt">byte</span> <span class="n">ret</span><span class="o">[];</span> 112 113 <span class="c1">// rounds = 1 << log_round</span> 114 <span class="kt">long</span> <span class="n">rounds</span> <span class="o">=</span> <span class="n">roundsForLogRounds</span><span class="o">(</span><span class="n">log_rounds</span><span class="o">);</span> 115 116 <span class="n">init_key</span><span class="o">();</span> 117 <span class="n">ekskey</span><span class="o">(</span><span class="n">salt</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span> 118 <span class="k">for</span> <span class="o">(</span><span class="kt">long</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">rounds</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="c1">// 最耗时的密钥扩展</span> 119 <span class="n">key</span><span class="o">(</span><span class="n">password</span><span class="o">);</span> 120 <span class="n">key</span><span class="o">(</span><span class="n">salt</span><span class="o">);</span> 121 <span class="o">}</span> 122 123 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">64</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 124 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">j</span> <span class="o"><</span> <span class="o">(</span><span class="n">clen</span> <span class="o">>></span> <span class="mi">1</span><span class="o">);</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span> 125 <span class="n">encipher</span><span class="o">(</span><span class="n">cdata</span><span class="o">,</span> <span class="n">j</span> <span class="o"><<</span> <span class="mi">1</span><span class="o">);</span> 126 <span class="o">}</span> 127 <span class="o">}</span> 128 129 <span class="n">ret</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="n">clen</span> <span class="o">*</span> <span class="mi">4</span><span class="o">];</span> 130 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">clen</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 131 <span class="n">ret</span><span class="o">[</span><span class="n">j</span><span class="o">++]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span> <span class="o">((</span><span class="n">cdata</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">>></span> <span class="mi">24</span><span class="o">)</span> <span class="o">&</span> <span class="mh">0xff</span><span class="o">);</span> 132 <span class="n">ret</span><span class="o">[</span><span class="n">j</span><span class="o">++]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span> <span class="o">((</span><span class="n">cdata</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">>></span> <span class="mi">16</span><span class="o">)</span> <span class="o">&</span> <span class="mh">0xff</span><span class="o">);</span> 133 <span class="n">ret</span><span class="o">[</span><span class="n">j</span><span class="o">++]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span> <span class="o">((</span><span class="n">cdata</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">>></span> <span class="mi">8</span><span class="o">)</span> <span class="o">&</span> <span class="mh">0xff</span><span class="o">);</span> 134 <span class="n">ret</span><span class="o">[</span><span class="n">j</span><span class="o">++]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span> <span class="o">(</span><span class="n">cdata</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">&</span> <span class="mh">0xff</span><span class="o">);</span> 135 <span class="o">}</span> 136 <span class="k">return</span> <span class="n">ret</span><span class="o">;</span> 137<span class="o">}</span> 138 139<span class="cm">/** 140 * Check that a plaintext password matches a previously hashed one 141 * @param plaintext the plaintext password to verify 142 * @param hashed the previously-hashed password 143 * @return true if the passwords match, false otherwise 144 */</span> 145<span class="kd">public</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">checkpw</span><span class="o">(</span><span class="n">String</span> <span class="n">plaintext</span><span class="o">,</span> <span class="n">String</span> <span class="n">hashed</span><span class="o">)</span> <span class="o">{</span> 146 <span class="k">return</span> <span class="nf">equalsNoEarlyReturn</span><span class="o">(</span><span class="n">hashed</span><span class="o">,</span> <span class="n">hashpw</span><span class="o">(</span><span class="n">plaintext</span><span class="o">,</span> <span class="n">hashed</span><span class="o">));</span> 147<span class="o">}</span> 148 149<span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">equalsNoEarlyReturn</span><span class="o">(</span><span class="n">String</span> <span class="n">a</span><span class="o">,</span> <span class="n">String</span> <span class="n">b</span><span class="o">)</span> <span class="o">{</span> 150 <span class="kt">char</span><span class="o">[]</span> <span class="n">caa</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">();</span> 151 <span class="kt">char</span><span class="o">[]</span> <span class="n">cab</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">();</span> 152 153 <span class="k">if</span> <span class="o">(</span><span class="n">caa</span><span class="o">.</span><span class="na">length</span> <span class="o">!=</span> <span class="n">cab</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span> 154 <span class="k">return</span> <span class="kc">false</span><span class="o">;</span> 155 <span class="o">}</span> 156 157 <span class="kt">byte</span> <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 158 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">caa</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 159 <span class="n">ret</span> <span class="o">|=</span> <span class="n">caa</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">^</span> <span class="n">cab</span><span class="o">[</span><span class="n">i</span><span class="o">];</span> 160 <span class="o">}</span> 161 <span class="k">return</span> <span class="n">ret</span> <span class="o">==</span> <span class="mi">0</span><span class="o">;</span> 162<span class="o">}</span>
<span class="o">}</span> </code></pre></div></div>
<p><strong>性能</strong></p> <p>慢哈希既要防止攻击者无法使用暴力破击,又不能影响用户体验,由于机器性能的差异,获取强度参数最好的办法就是执行一个简短的性能基准测试,找到使哈希函数大约耗费0.5秒的值。</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">BCryptBench</span> <span class="o">{</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">\[\]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span> <span class="kt">long</span> <span class="n">startTime</span><span class="o">,</span> <span class="n">endTime</span><span class="o">,</span> <span class="n">duration</span><span class="o">;</span>1 <span class="c1">// the default strength 10</span> 2 <span class="n">BCryptPasswordEncoder</span> <span class="n">bCryptPasswordEncoder10</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCryptPasswordEncoder</span><span class="o">();</span> 3 <span class="n">duration</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 4 5 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 6 <span class="n">startTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 7 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">bCryptPasswordEncoder10</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> 8 <span class="n">endTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 9 <span class="n">duration</span> <span class="o">+=</span> <span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span> 10 <span class="o">}</span> 11 12 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">10.0</span><span class="o">);</span> <span class="c1">// 88.4ms</span> 13 14 <span class="c1">// strength 11</span> 15 <span class="c1">// the default strength 10</span> 16 <span class="n">BCryptPasswordEncoder</span> <span class="n">bCryptPasswordEncoder11</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCryptPasswordEncoder</span><span class="o">(</span><span class="mi">11</span><span class="o">);</span> 17 <span class="n">duration</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 18 19 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 20 <span class="n">startTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 21 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">bCryptPasswordEncoder11</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> 22 <span class="n">endTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 23 <span class="n">duration</span> <span class="o">+=</span> <span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span> 24 <span class="o">}</span> 25 26 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">10.0</span><span class="o">);</span> <span class="c1">// 175.3ms</span> 27 28 <span class="c1">// strength 12</span> 29 <span class="n">BCryptPasswordEncoder</span> <span class="n">bCryptPasswordEncoder12</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCryptPasswordEncoder</span><span class="o">(</span><span class="mi">12</span><span class="o">);</span> 30 <span class="n">duration</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 31 32 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 33 <span class="n">startTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 34 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">bCryptPasswordEncoder12</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> 35 <span class="n">endTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 36 <span class="n">duration</span> <span class="o">+=</span> <span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span> 37 <span class="o">}</span> 38 39 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span> <span class="n">duration</span> <span class="o">/</span> <span class="mf">10.0</span><span class="o">);</span> <span class="c1">// 344.3ms</span> 40 41 <span class="c1">// strength 13</span> 42 <span class="n">BCryptPasswordEncoder</span> <span class="n">bCryptPasswordEncoder13</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCryptPasswordEncoder</span><span class="o">(</span><span class="mi">13</span><span class="o">);</span> 43 <span class="n">duration</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 44 45 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 46 <span class="n">startTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 47 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">bCryptPasswordEncoder13</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> 48 <span class="n">endTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 49 <span class="n">duration</span> <span class="o">+=</span> <span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span> 50 <span class="o">}</span> 51 52 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">10.0</span><span class="o">);</span> <span class="c1">// 703.8ms</span> 53 54 <span class="c1">// strength 14</span> 55 <span class="n">BCryptPasswordEncoder</span> <span class="n">bCryptPasswordEncoder14</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCryptPasswordEncoder</span><span class="o">(</span><span class="mi">14</span><span class="o">);</span> 56 <span class="n">duration</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 57 58 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 59 <span class="n">startTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 60 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">bCryptPasswordEncoder14</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> 61 <span class="n">endTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 62 <span class="n">duration</span> <span class="o">+=</span> <span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span> 63 <span class="o">}</span> 64 65 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">10.0</span><span class="o">);</span> <span class="c1">// 1525.0ms</span> 66 67 <span class="c1">// strength 15</span> 68 <span class="n">BCryptPasswordEncoder</span> <span class="n">bCryptPasswordEncoder15</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BCryptPasswordEncoder</span><span class="o">(</span><span class="mi">15</span><span class="o">);</span> 69 <span class="n">duration</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> 70 71 <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> 72 <span class="n">startTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 73 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">bCryptPasswordEncoder15</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="s">"admin"</span><span class="o">));</span> 74 <span class="n">endTime</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span> 75 <span class="n">duration</span> <span class="o">+=</span> <span class="o">(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">);</span> 76 <span class="o">}</span> 77 78 <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">10.0</span><span class="o">);</span> <span class="c1">// 2921.9ms</span> 79<span class="o">}</span>
<span class="o">}</span> </code></pre></div></div>
<p>从测试的结果可以看出,如果想选定一个执行时间为0.5秒的慢哈希,需要将Bcrypt函数的强度设置为12或13。而在我们自己的微服务中,使用了默认的强度10。</p> <p><br> <span class="post-meta"> Reference: </span> <br> <span class="post-meta"> 1 <a href="https://crackstation.net/hashing-security.htm">Salted Password Hashing - Doing it Right</a><br> 2 <a href="http://blog.jobbole.com/61872/">Salted Password Hashing - Doing it Right 译文</a> </span></p> 原文地址:https://zhjwpku.com/2017/11/30/bcrypt-in-spring-security.html </div>