1. 對稱加密與非對稱加密
加密技術是最常見的安全保密手段,數據加密技術的關鍵在於加密/解密算法和密鑰管理。數據加密的基本過程是對原來爲明文的文件或數據按某種加密算法進行處理,使其成爲不可讀的一段代碼,通常成爲“密文”。“密文”只能在輸入相應的密鑰之後才能顯示出原來的內容,通過這樣的途徑保證被加密的內容不被竊取。
對稱加密的特點是文件或數據加密和解密使用相同的密鑰,這種方法在密碼學中成爲對稱加密算法。
非對稱加密算法區別於對稱加密算法,非對稱加密算法需要兩個密鑰,公鑰(Publickey)和私鑰(PrivateKey)。公鑰與私鑰是一對,如果使用公鑰對數據加密,需要對應的私鑰才能解密;如果使用私鑰對數據加密,需要對應的公鑰才能解密。
2. 非對稱加密技術——RSA算法
RSA算法是常用的非對稱加密技術,名稱的由來是提出人Ron Rivest、Adi Shamir、Leonard Adleman三個人的姓氏的首字母。一般來說,非對稱加密技術有兩種用法,一種是採用公鑰加密,私鑰解密;另一種就是本文所描述的私鑰簽名,公鑰驗籤。
2.1 生成證書文件
首先採用RSA算法生成證書文件,這個證書文件中會包含公鑰與私鑰的信息。
Keytool是Java提供的一個證書管理工具,這裏我們就用keytool來生成密鑰證書。使用keytool生成證書文件有以下幾個步驟
1.保證JDK的環境變量是可用的;
2.打開cmd窗口,並進入證書生成的目標文件夾(證書會在當前文件夾中生成);
3.執行代碼段中的命令,命令的各個參數意義如下:
-alias:密鑰的別名,這裏我設置爲mountainkey
-keyalg:使用的hash算法,這裏我使用的是RSA算法
-keypass:密鑰的訪問密碼,這裏我設置爲mountain
-keystore:密鑰庫的文件名,這裏我設置爲mountain.keystore,生成文件後會在mountain.keystore中保存證書內容
-storepass:密鑰庫的訪問密碼,這裏我設置爲mountainkeystore
keytool -genkeypair -alias mountainkey -keyalg RSA -keypass mountain -keystore mountain.keystore -storepass mountainkeystore
命令的輸入過程如下圖所示。
執行成功會在dos窗口所處的目錄中生成證書文件,如下圖所示。
2.2 獲取公鑰
生成的證書文件mountain.keystore中包含了公鑰與私鑰,現在從證書中獲取公鑰。這裏需要用到openssl工具包,下載地址爲http://slproweb.com/products/Win32OpenSSL.html
下載並安裝成功後,將openssl的bin目錄加入path環境變量,如下圖所示。
現在,以cmd進入剛剛證書文件所在的目錄,輸入以下命令(證書文件爲mountain.keystore),在提示輸入祕鑰庫密碼後輸入storepass設置的內容,也就是祕鑰庫密碼。
keytool -list -rfc --keystore mountain.keystore | openssl x509 -inform pem -pubkey
顯示的內容如下,該內容包含了公鑰的信息,也就是從BEGIN PUBLIC KEY到END PUBLIC KEY的內容,該字符串用於去校驗通過私鑰加密後的內容。
2.3 使用私鑰簽名
現在我們測試通過獲取證書當中的私鑰對數據進行加密,能夠讀取證書文件並獲取私鑰的方法有很多,這裏我使用Spring Security所提供的方法來對進行此項操作。
/**
* 讀取證書文件中的私鑰,並使用私鑰對數據加密,得到加密後的字符串
*/
@Test
public void testGenerateToken(){
String keystore = "mountain.keystore";
String keystorePassword = "mountainkeystore";
ClassPathResource classPathResource = new ClassPathResource(keystore);
String alias = "mountainkey";
String keyPassword = "mountain";
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,keystorePassword.toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, keyPassword.toCharArray());
RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
Map<String,String> contentMap = new HashMap<>();
contentMap.put("hello","hello world");
String bodyString = JSON.toJSONString(contentMap);
Jwt jwt = JwtHelper.encode(bodyString, new RsaSigner(aPrivate));
String encodedContent = jwt.getEncoded();
System.out.println(encodedContent);
}
控制檯打印得到的私鑰加密後的內容如下。
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6ImhlbGxvIHdvcmxkIn0.LNB7GpiTYl1G4MNYpFDPgay6funMLXeoGUo3TU30IfhIJiSU7zobJ-SpJap6ARbCJoRc5_JURb4ti-6vanf3cpIyOayk2H83gfBOSY8_wACbpfhlt_PGT0RYfStGC7OVwKFUAdEaO6hYYD466vI8d6d9uNb1RfNfGToYlosH05McYAyrM2XLcy0T7glZmuNw_JgMflRAjN09K13MlQZttn6W-lhSlv38CE1_CJ8SzNLHC6U7Dzd1FlcW9Xs-IvDalw1xzgzNRfbNpdvcZTnNhbdNa6_bSlSIfSM9IHngG3EVS_hwQkGOfBeV5gxQZrW4BB9niK8FwvoDB1cVjy4Ktw
2.4 使用公鑰校驗
將使用私鑰簽名後的數據進行校驗,這裏依然使用Spring Security提供的方法。
/**
* 使用公鑰校驗加密後的字符串
*/
@Test
public void testCheckTokenByPublicKey() {
// 公鑰
String publickey = "-----BEGIN PUBLIC KEY-----" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbuddIbMU5FjqpJR4Ikn" +
"xktq1k/0C10XfOR2VU79qh4PXGSNn6Vt5BZgK8Ow4cA7SzAMoBUkxev/5I2Mx4p4" +
"gk+6ImQ+IsTi6tqXOQ7DHjpogfsX/VeXJ93Aeq8v9hOqtKYj5q1jy4skGRvbD+c8" +
"Z6knxLQb9I6HE39v3BZZL+WTYz6kx8BTZ0rPd7C5uOVqYo/FG+QzY+Ndv2u7gNcy" +
"V9sRnM+hI2w5e87LuG+V6GhekdKqtS0dsjKskpjX/L2ppykdi1hkCtS/ipZ5aaAj" +
"/SzVfWfQTxw4Yh+3QVc+KoSW61KlCZ+SSu7YrszAqlg93927/eWWLjYUFsCqP0jw" +
"5wIDAQAB" +
"-----END PUBLIC KEY-----";
// 私鑰加密後的內容
String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6ImhlbGxvIHdvcmxkIn0.LNB7GpiTYl1G4MNYpFDPgay6funMLXeoGUo3TU30IfhIJiSU7zobJ-SpJap6ARbCJoRc5_JURb4ti-6vanf3cpIyOayk2H83gfBOSY8_wACbpfhlt_PGT0RYfStGC7OVwKFUAdEaO6hYYD466vI8d6d9uNb1RfNfGToYlosH05McYAyrM2XLcy0T7glZmuNw_JgMflRAjN09K13MlQZttn6W-lhSlv38CE1_CJ8SzNLHC6U7Dzd1FlcW9Xs-IvDalw1xzgzNRfbNpdvcZTnNhbdNa6_bSlSIfSM9IHngG3EVS_hwQkGOfBeV5gxQZrW4BB9niK8FwvoDB1cVjy4Ktw";
//校驗jwt令牌
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
//拿到jwt令牌中自定義的內容
String claims = jwt.getClaims();
System.out.println(claims);
}
控制檯打印的內容即是使用私鑰簽名前的內容。
{"hello":"hello world"}