加密模塊
1. 前言
我們知道安全性的實(shí)現(xiàn)往往少不了加解密的參與,Spring Security 加密模塊支持對(duì)稱(chēng)加密、秘鑰生成和密碼編碼。這些代碼被發(fā)布在 Spring Security 核心模塊當(dāng)中,與其他模塊或代碼之間零耦合。
本節(jié)主要討論 Spring Security 中的加密模塊。
2. 加密器
加密器的相關(guān)類(lèi)提供了構(gòu)造對(duì)稱(chēng)加密器的工廠(chǎng)方法。通過(guò)該類(lèi),我們可以創(chuàng)建 ByteEncryptor
用于加密原始字節(jié)流內(nèi)容,我們也可以構(gòu)建出 TextEncryptor
用于加密文本字符串,這些加密器都是線(xiàn)程安全的。
2.1 BytesEncryptor
BytesEncryptor
是通過(guò) Encryptors.stronger
工廠(chǎng)方法構(gòu)造出的:
Encryptors.stronger("password", "salt");
stronger
加密方法構(gòu)造了一個(gè)使用GCM模式的 256 位 AES 算法加密器。它使用 PKCS #5 的 PBKDF2(基于密碼的密鑰派生函數(shù) #2)導(dǎo)出密鑰(此方法需要 Java 6)。方法中 password
參數(shù)用于生產(chǎn)加密密鑰,它將被保存在一個(gè)不被共享的安全區(qū)域。salt
參數(shù)用于避免字典攻擊。除此之外,此處還應(yīng)用了 16 字節(jié)的隨機(jī)初始化向量來(lái)保證每個(gè)加密消息都是唯一的。
salt
參數(shù)的是一個(gè) 16 禁止編碼字符串,具有隨機(jī)性,至少有 8 位長(zhǎng),salt
可以通過(guò)以下方式生成:
String salt = KeyGenerators.string().generateKey(); // 生成一個(gè)隨機(jī) 8 位長(zhǎng)的 16 進(jìn)制字符串
我們也可以使用標(biāo)準(zhǔn)加密方法,即 CBC 模式的 256 位 AES 算法加密模式。這種模式不需要通過(guò)身份認(rèn)證,也無(wú)法保證數(shù)據(jù)的真實(shí)性,相對(duì) Encryptors.stronger
來(lái)說(shuō)安全性會(huì)低一些。
2.2 TextEncryptor
使用 Encryptors.text
工廠(chǎng)方法構(gòu)造標(biāo)準(zhǔn)的文本加密器 TextEncryptor
:
Encryptors.text("password", "salt");
TextEncryptor
使用的是標(biāo)準(zhǔn) BytesEncryptor
來(lái)加密文本數(shù)據(jù),加密的結(jié)果以 16 進(jìn)制編碼字符串形式返回,這樣容易被文件系統(tǒng)或數(shù)據(jù)庫(kù)保存。
我們也可以使用 Encryptors.queryableText
工廠(chǎng)方法構(gòu)建可查詢(xún)的文本加密器:
Encryptors.queryableText("password", "salt");
這兩種加密器間的區(qū)別是擁有不同的初始化向量(iv)處理。在可查詢(xún)文本加密器中,初始化向量是可共享的、固定的,并且不是隨機(jī)生成的。這意味著相同的文本內(nèi)容多次被加密的結(jié)果是相同的。這樣的場(chǎng)景的安全性相對(duì)較低,但有時(shí)為了加密出的結(jié)果是有規(guī)律的,比如希望他仍然可以被再次查詢(xún)到,還是需要用到這樣的加密方式??刹樵?xún)文本加密器的使用場(chǎng)景有如 OAuth 的 apiKey。
3. 秘鑰生成器
密鑰生成器(KeyGenerators)提供了一些工廠(chǎng)方法,用于構(gòu)造不同類(lèi)型的密鑰生成器。例如 BytesKeyGenerator
可用于生產(chǎn) byte[]
類(lèi)型密鑰。StringKeyGenerator
用于生成字符串類(lèi)型密鑰。密鑰生成器是線(xiàn)程安全的。
3.1 BytesKeyGenerator
使用 KeyGenerators.secureRandom
工廠(chǎng)方法構(gòu)造 BytesKeyGenerator
實(shí)例:
BytesKeyGenerator generator = KeyGenerators.secureRandom();
byte[] key = generator.generateKey();
默認(rèn)配置下,生成的 key 長(zhǎng)度為 8 位,我們也可以為 secureRandom
設(shè)置參數(shù)來(lái)修改生成密鑰長(zhǎng)度:
KeyGenerators.secureRandom(16);
使用 KeyGenerators.shared
工廠(chǎng)方法構(gòu)造 BytesKeyGenerator
可以使多次生成的密鑰內(nèi)容相同。
KeyGenerators.shared(16);
3.2 StringKeyGenerator
使用 KeyGenerators.string
工廠(chǎng)方法可以構(gòu)造 8 為長(zhǎng)的隨機(jī) 16 禁止字符串密碼:
KeyGenerators.string();
4. 密碼編碼
在 Spring Security 加密模塊中,password
包提供了編碼密碼的方法。PasswordEncoder
是其中的核心類(lèi):
public interface PasswordEncoder {
String encode(String rawPassword);
boolean matches(String rawPassword, String encodedPassword);
}
matches
方法用來(lái)判斷密碼原文在經(jīng)過(guò)一次編碼后,與密碼密文是否匹配,這個(gè)方法用于基于密碼認(rèn)證的場(chǎng)景。
最常見(jiàn)的實(shí)現(xiàn)類(lèi)是 BCryptPasswordEncoder
,它使用了 bcrypt
算法來(lái)散列密碼。Bcrypt 使用了一個(gè)隨機(jī) 16 位鹽值,用于制造冗余,以防止密碼被破解。冗余次數(shù)可以通過(guò) strength
參數(shù)設(shè)置,其值為 4~31 之間,值約高,散列次數(shù)越多,默認(rèn)值為 10。
// 構(gòu)造一個(gè)強(qiáng)度為 16 的密碼加密器
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
Pbkdf2PasswordEncoder
實(shí)現(xiàn)了 PBKDF2 算法用來(lái)散列密碼。該算法為了防止被破解,有意的減慢了執(zhí)行時(shí)間,大概需要 0.5 秒完成密碼的驗(yàn)證。
// 創(chuàng)建一個(gè) PBKDF2 算法的密碼加密器
Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder();
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
5. 小結(jié)
本節(jié)討論了 Spring Security 安全框架中的加解密實(shí)現(xiàn),主要內(nèi)容有:
- Spring Security 在核心模塊中包含了一個(gè)關(guān)于加密算法的包,其包含了加密、密鑰生成、密碼編碼三個(gè)主要功能;
- Spring Security 的加密主要分基于
byte[]
對(duì)象的加密和文本形式的加密; - Spring Security 的加密可分為冪等的和非冪等的,其分別有各自的應(yīng)用場(chǎng)景;
- Spring Security 對(duì)密碼的加密采取了有意拖延的方式,防止密碼被暴力破解。
下節(jié)討論在 Spring Security 項(xiàng)目中,如何實(shí)現(xiàn)單元測(cè)試。