使用RSA公私鑰實現JWT令牌的簽名和驗籤
1、什麼是JWT令牌
JWT基本概念可從JWT網站獲取,該網站提供了在線DEBUG的功能,對於初次學習JWT的同學來說非常的友好,同時還將各種語言支持JWT的庫提供了列表查閱,方便大家獲取不同語言的第三方庫進行學習和開發。如果你已經對JWT非常的熟悉並且有過JWT開發經驗,你還可以閱讀RFC7519 JSON Web Token規範進一步學習。
2、什麼是RSA
RSA是公開密鑰密碼學體系中的非對稱加密算法,詳細信息可以閱讀維基百科RSA或者閱讀更專業的密碼學書籍,本文不是講解RSA的文章,所以我們在這裏就不過多講解RSA的知識。
3、使用RSA對JWT令牌簽名和驗證
私鑰用於簽名令牌,公鑰用於驗證令牌是否爲私鑰所簽名。私鑰由令牌生成者持有,驗證令牌的組件可獲得對應的公鑰。
3.1 公私鑰生成和存儲
在這裏我們講解兩種生成公私鑰的方法:使用Java中的KeyPairGenerator
生成,使用openssl
命令
3.1.1 使用KeyPairGenerator
生成公私鑰
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair keyPair = generator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
其中KeyPairGenerator
的構造函數支持配置多種參數,包括keysize、SecureRandom和AlgorithmParameterSpec,可根據安全要求和實際需要進行配置。
使用代碼生成RSA公私鑰對非常的簡單,但是我們需要能夠將RSA公私鑰對存儲並且將公鑰分發給需要的組件這樣才能實現RSA的作用。PrivateKey
和PublicKey
都提供了getEncoded()
方法獲取按照格式編碼後的內容,將key的內容存到文件中。
String path = "private_key.der";
Files.write(new File(path).toPath(), keyPair.getPrivate().getEncoded());
String publicPath = "public_key2.der";
Files.write(new File(publicPath).toPath(), keyPair.getPublic().getEncoded());
3.1.2 使用openssl命令生成RSA公私鑰
生產pem格式私鑰:
openssl genrsa -out private_key.pem 3072
將pem格式私鑰轉換爲der格式
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
生成公鑰
openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
3.2 讀取RSA公私鑰
3.1中生成的公私鑰已經存儲到了文件,我們首先需要讀取公私鑰內容生成java中的PrivateKey和PublicKey,這樣才能進行JWT令牌的簽名和驗籤。
3.2.1 讀取私鑰:
私鑰的編碼格式爲:PKCS#8
private static PrivateKey getPrivateKey() {
String privateKeyName = "private_key2.der";
try {
byte[] content = Files.readAllBytes(new File(privateKeyName).toPath());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.2.2 讀取公鑰
公鑰的編碼格式爲:X.509
private static PublicKey getPublicKey() {
String publicKeyName = "public_key2.der";
try {
byte[] keyBytes = Files.readAllBytes(new File(publicKeyName).toPath());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.3 生成JWT令牌
本文使用jjwt開源組件來生成JWT令牌
PrivateKey privateKey = getPrivateKey();
Map<String, String> claims = new HashMap<>();
claims.put("user", "chen tong");
String token = Jwts.builder()
.setClaims(claims)
.signWith(privateKey)
.compact();
3.4 驗籤JWT令牌
PublicKey publicKey = getPublicKey();
Claims claims1 = Jwts.parserBuilder()
.setSigningKey(publicKey)
.build().parseClaimsJws(token).getBody();
如果簽名錯誤,程序將會拋出SignatureException異常。