公鑰與私鑰
在對稱加密的時代,加密和解密用的是同一個金鑰,這個金鑰既用於加密,又用於解密。這樣做有一個明顯的缺點,如果兩個人之間傳輸檔案,兩個人都要知道金鑰,如果是三個人呢,五個人呢?於是就產生了非對稱加密,用一個金鑰進行加密(公鑰),用另一個金鑰進行解密(私鑰)。
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); } }