在對稱加密的時代,加密和解密用的是同一個密鑰,這個密鑰既用於加密,又用於解密。這樣做有一個明顯的缺點,如果兩個人之間傳輸文件,兩個人都要知道密鑰,如果是三個人呢,五個人呢?於是就產生了非對稱加密,用一個密鑰進行加密(公鑰),用另一個密鑰進行解密(私鑰)。
1.公鑰私鑰原理
張三有兩把鑰匙,一把是公鑰,另一把是私鑰。

張三把公鑰送給他的朋友們—-李四、王五、趙六—-每人一把。

李四要給張三寫一封保密的信。她寫完后用張三的公鑰加密,就可以達到保密的效果。

張三收信后,用私鑰解密,就看到了信件內容。這里要強調的是,只要張三的私鑰不泄露,這封信就是安全的,即使落在別人手里,也無法解密。

張三給李四回信,決定采用“數字簽名”。他寫完后先用Hash函數,生成信件的摘要(digest)。然后利用私鑰將摘要進行加密,張三將這個簽名,附在信件下面,一起發給李四。

李四收信后,取下數字簽名,用張三的公鑰解密,得到信件的摘要。由此證明,這封信確實是張三發出的。李四再對信件本身使用Hash函數,將得到的結果,與上一步得到的摘要進行對比。如果兩者一致,就證明這封信未被修改過。

2.生成私鑰公鑰
Spring Security 提供對JWT的支持,我們使用Spring Security 提供的JwtHelper來創建JWT令牌,校驗JWT令牌 等操作。 這里JWT令牌我們采用非對稱算法進行加密,所以我們要先生成公鑰和私鑰。
(1)生成密鑰證書 下邊命令生成密鑰證書,采用RSA 算法每個證書包含公鑰和私鑰
創建一個文件夾,在該文件夾下執行如下命令行:
keytool -genkeypair -alias changgou -keyalg RSA -keypass changgou -keystore changgou.jks -storepass changgou
Keytool 是一個java提供的證書管理工具
-alias:密鑰的別名 -keyalg:使用的hash算法 -keypass:密鑰的訪問密碼 -keystore:密鑰庫文件名,xc.keystore保存了生成的證書 -storepass:密鑰庫的訪問密碼
(2)查詢證書信息
keytool -list -keystore changgou.jks
(3)刪除別名
keytool -delete -alias changgou -keystore changgou.jsk
4.2.3 導出公鑰
openssl是一個加解密工具包,這里使用openssl來導出公鑰信息。
安裝 openssl:http://slproweb.com/products/Win32OpenSSL.html
安裝資料目錄下的Win64OpenSSL-1_1_0g.exe
cmd進入changgou.jks文件所在目錄執行如下命令(如下命令在windows下執行,會把-變成中文方式,請將它改成英文的-):
keytool -list -rfc --keystore changgou.jks | openssl x509 -inform pem -pubkey

將上邊的公鑰拷貝到文本public.key文件中,合並為一行,可以將它放到需要實現授權認證的工程中。
4.2.4 JWT令牌
(1)創建令牌數據
public class Test {
public static void main(String[] args) {
/**
* 密鑰證書文件路徑
*/
String key_location="changgou.jks";
/**
* 密鑰庫的訪問密碼
*/
String key_password="changgou";
/**
* 密鑰的訪問密碼
*/
String keypwd = "changgou";
/**
* 秘鑰別名
*/
String alias = "changgou";
/**
* 訪問證書路徑
*/
ClassPathResource resource = new ClassPathResource(key_location);
/**
* 創建秘鑰工廠
* 資源路徑
* 密鑰庫訪問密碼
*/
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,key_password.toCharArray());
/**
* 讀取秘鑰對(公鑰、私鑰)
* 密鑰別名
* 密鑰訪問密碼
*/
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,keypwd.toCharArray());
/**
* 獲取私鑰
*/
RSAPrivateKey rsaPrivate = (RSAPrivateKey) keyPair.getPrivate();
/**
* 定義Payload
*/
Map<String, Object> tokenMap = new HashMap<>();
tokenMap.put("id", "1");
tokenMap.put("name", "itheima");
tokenMap.put("roles", "ROLE_VIP,ROLE_USER");
/**
* 生成jwt令牌
* 用私鑰加密
*/
Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner(rsaPrivate));
/**
* 取出令牌
*/
String encoded = jwt.getEncoded();
System.out.println(encoded);
}
}
(2)解析令牌
上面創建令牌后,我們可以對JWT令牌進行解析,這里解析需要用到公鑰,我們可以將之前生成的公鑰public.key拷貝出來用字符串變量token存儲,然后通過公鑰解密。
public class Test2 {
public static void main(String[] args) {
/**
* 令牌
*/
String token ="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6IlJPTEVfVklQLFJPTEVfVVNFUiIsIm5hbWUiOiJpdGhlaW1hIiwiaWQiOiIxIn0.oW1h0Xza7vUEgi0rzOlgMLdLicnJ7kFXg6pBjW640sxzeeiPC0cUfXEYAAmmTYEMQtLeivbZeiar-p5ydupUVhTVF8evEzqt64p31DJQ5tsIQ2-moXE8Q6W9JP2MY1sylBdN8rmVmIW6pqBh4KuLkFFalWd851HAzdQg95SvIbyUu6F6M-iZvL4-qcTdakYjpnWKOTWNBighB-hMlaAs6REMXyMsU0wo8DRHyNypUb4vILb_NPjd69ubFhOTUvLkYRtImEk8QNnQ8-l68GhAGmqifxDkNAz3I0f0Mcgfu3OdT056qcR6FG_o1thzOaXRZQliA1B2ZnZ3tepHv6ahxw";
/**
* 使用公鑰進行解密
*/
String publickey ="-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15w2T+DOwQzFxWa26bFq\n" +
"1xhYdy49INE7vPC1D6+09P/c1byY6U7N3AStO3eYhT/v5TUwCfXobXZ98e2Gf1/O\n" +
"KO/TmLFXOcoea9MP0kVcVV/80O6g19tzWDIkBbphO9g5E0lq/VwQYC0Cc3/4jNO0\n" +
"87d5BwMdlSasqbJjA8MVDXjzupPDl+hw6Wr48motbJGgSzrOpNPZMJK25ylwlqac\n" +
"TCetcjQXL4tpKcPXOaCEUFwWWxBUtFfwP+nU0PSDTUhwe6UsKtugnkAd1TYn9+bh\n" +
"78J77CFwpFPxtfutSbP2E8/yxYKufUg6NYo+F3PRQQElTb/aH8zi4n+RIHvec2Zw\n" +
"HwIDAQAB\n" +
"-----END PUBLIC KEY-----";
/**
* 校驗jwt
*/
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
/**
* 獲取Jwt原始內容
*/
String claims = jwt.getClaims();
System.out.println(claims);
}
}
