keycloak~jwt的rs256簽名的驗證方(fang)式
接口地址
- keycloak開放接口地址:/auth/realms/fabao/.well-known/openid-configuration

rsa算法相關術語
RSA算(suan)法(fa)是一種非對稱加密算(suan)法(fa),其(qi)安全性(xing)基于大整(zheng)數分解的困(kun)難性(xing)。在RSA算(suan)法(fa)中,有以下幾(ji)個關鍵參數:
-
n(模數):n 是一(yi)個(ge)大整(zheng)數,通常為兩個(ge)大素(su)數 p 和 q 的(de)乘積,即 n = p * q。n 用于生(sheng)成公鑰和私鑰,并且決(jue)定了加密(mi)和解(jie)密(mi)的(de)計算過(guo)程。
-
e(公鑰指數):e 是一個與 φ(n) 互(hu)質(zhi)的(de)(de)小整(zheng)數(shu),其中 φ(n) 是歐拉函數(shu),表示(shi)小于 n 且與 n 互(hu)質(zhi)的(de)(de)正整(zheng)數(shu)的(de)(de)個數(shu)。e 在加密(mi)時使用,作為公鑰的(de)(de)一部分(fen)。
-
公鑰:公鑰(yao)由 (n, e) 組成(cheng),其中 n 是模數,e 是公鑰(yao)指(zhi)數。公鑰(yao)用于加(jia)密消(xiao)息,任(ren)何人都可以(yi)獲得公鑰(yao)進行加(jia)密操作。
-
私鑰:私(si)鑰(yao)由 (n, d) 組成,其(qi)中 n 是(shi)模數(shu),d 是(shi)私(si)鑰(yao)指(zhi)數(shu)。私(si)鑰(yao)用于解密(mi)已經(jing)被(bei)公鑰(yao)加密(mi)的消息(xi),只有私(si)鑰(yao)的持有者(zhe)才能獲得解密(mi)能力。
總(zong)結來說(shuo),RSA算(suan)法通過公鑰(yao)加密、私(si)鑰(yao)解(jie)密的方式(shi)實現信息(xi)的安(an)全傳輸,公鑰(yao)用(yong)于加密數據,私(si)鑰(yao)用(yong)于解(jie)密數據;反過來,私(si)鑰(yao)可以(yi)用(yong)來生成簽名,而公鑰(yao)可以(yi)用(yong)來驗證簽名的有效(xiao)性。
RSA和RS256
-
RSA:RSA是(shi)一(yi)種非對稱(cheng)加密(mi)算(suan)法(fa),可以用(yong)(yong)于(yu)數(shu)據(ju)的加密(mi)和數(shu)字(zi)簽名。在RSA中,公(gong)鑰和私(si)鑰是(shi)成對存在的,公(gong)鑰用(yong)(yong)于(yu)加密(mi)數(shu)據(ju)或驗證(zheng)數(shu)字(zi)簽名,私(si)鑰用(yong)(yong)于(yu)解密(mi)數(shu)據(ju)或生成數(shu)字(zi)簽名。
-
RS256:RS256是(shi)一種(zhong)基于RSA算法的數(shu)字簽名(ming)算法,其(qi)中(zhong)“RS”代表RSA算法,“256”表示使(shi)用(yong)SHA-256哈希算法生成摘(zhai)要。RS256常(chang)用(yong)于JWT(JSON Web Token)的數(shu)字簽名(ming)過程中(zhong),用(yong)于驗證數(shu)據的完整(zheng)性和(he)真實(shi)性。
因(yin)此,可以(yi)說RS256是RSA算(suan)法的一種(zhong)特定應用(yong),用(yong)于(yu)數(shu)(shu)字簽名,并且(qie)結(jie)合(he)了SHA-256哈希算(suan)法。RSA算(suan)法還可以(yi)用(yong)于(yu)加密數(shu)(shu)據(ju)等其他用(yong)途,而RS256主要用(yong)于(yu)數(shu)(shu)字簽名。
獲取keycloak頒發的公鑰
- 從jwks公鑰開放地址獲取公鑰 /auth/realms/fabao/protocol/openid-connect/certs

- 從keycloak后臺獲取公鑰

keycloak中jwt的驗證
- 在客戶端驗證keycloak的token是否在傳輸過程中被篡改,即簽名驗證是否通過
- 下面代碼中定義了公鑰字符串,從開放地址返回的rsa公鑰的n模數和e指數
- 從kecloak認證中心頒發的公鑰字符串
- 從現在有keycloak認證中心獲取的jwt字符串
// RSA公鑰的模數
String modulus = "yOCNCy8x280...";
// RSA公鑰的指數
String exponent = "AQAB";
// keycloak拿到的公鑰
String publicKeyString = "MIIBIjANBg...B";
String KcJwtToken = "eyJh...";
- 根據公鑰開放地址返回的公鑰信息n和e來驗證簽名
@Test
public void verifySign() throws Exception {
String[] jwtParts = KcJwtToken.split("\\.");
String header = jwtParts[0];
String payload = jwtParts[1];
// 解碼Base64格式的模數和指數
byte[] decodedModulus = Base64.getUrlDecoder().decode(modulus);// getMimeDecoder()會忽略非Base64字符(如換行符、空格等)
byte[] decodedExponent = Base64.getUrlDecoder().decode(exponent);
// 構建RSA公鑰對象
RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(new BigInteger(1, decodedModulus),
new BigInteger(1, decodedExponent));
// 驗征RSA簽名
PublicKey publicKey = KeyFactory.getInstance("rsa").generatePublic(publicSpec);
boolean result = RSAUtils.verify(header + "." + payload, publicKey, jwtParts[2]);
System.out.print("驗簽結果:" + result);
}
- 根據keycloak后臺頒發的公鑰字符串來驗證簽名
// 根據認證平臺頒發的公鑰字符串來驗證簽名
@Test
public void verifyJwtToken() throws Exception {
String[] jwtParts = KcJwtToken.split("\\.");
String header = jwtParts[0];
String payload = jwtParts[1];
String sign = jwtParts[2];
PublicKey publicKey = RSAUtils.getPublicKey(publicKeyString);
boolean result = RSAUtils.verify(header + "." + payload, publicKey, sign);
System.out.print("驗簽結果:" + result);
}
需要注意的是,以上jwt的token簽(qian)名使用rs256(SHA256withRSA)算(suan)法生成的簽(qian)名,所以本例(li)子都是采用這種(zhong)簽(qian)名算(suan)法實現的,例(li)外,也有h256,h512等哈希(xi)算(suan)法。
keycloak支持的簽名算法
public static final String RS256 = "SHA256withRSA";
public static final String RS384 = "SHA384withRSA";
public static final String RS512 = "SHA512withRSA";
public static final String HS256 = "HMACSHA256";
public static final String HS384 = "HMACSHA384";
public static final String HS512 = "HMACSHA512";
public static final String ES256 = "SHA256withECDSA";
public static final String ES384 = "SHA384withECDSA";
public static final String ES512 = "SHA512withECDSA";
public static final String PS256 = "SHA256withRSAandMGF1";
public static final String PS384 = "SHA384withRSAandMGF1";
public static final String PS512 = "SHA512withRSAandMGF1";
public static final String AES = "AES";
public static final String SHA256 = "SHA-256";
public static final String SHA384 = "SHA-384";
public static final String SHA512 = "SHA-512";