keycloak~jwt的rs256簽名的驗證方式

张占岭發表於2024-04-18

介面地址

  • keycloak開放介面地址:/auth/realms/fabao/.well-known/openid-configuration

rsa演算法相關術語

RSA演算法是一種非對稱加密演算法,其安全性基於大整數分解的困難性。在RSA演算法中,有以下幾個關鍵引數:

  1. n(模數):n 是一個大整數,通常為兩個大素數 p 和 q 的乘積,即 n = p * q。n 用於生成公鑰和私鑰,並且決定了加密和解密的計算過程。

  2. e(公鑰指數):e 是一個與 φ(n) 互質的小整數,其中 φ(n) 是尤拉函式,表示小於 n 且與 n 互質的正整數的個數。e 在加密時使用,作為公鑰的一部分。

  3. 公鑰:公鑰由 (n, e) 組成,其中 n 是模數,e 是公鑰指數。公鑰用於加密訊息,任何人都可以獲得公鑰進行加密操作。

  4. 私鑰:私鑰由 (n, d) 組成,其中 n 是模數,d 是私鑰指數。私鑰用於解密已經被公鑰加密的訊息,只有私鑰的持有者才能獲得解密能力。

總結來說,RSA演算法透過公鑰加密、私鑰解密的方式實現資訊的安全傳輸,公鑰用於加密資料,私鑰用於解密資料;反過來,私鑰可以用來生成簽名,而公鑰可以用來驗證簽名的有效性。

RSA和RS256

  1. RSA:RSA是一種非對稱加密演算法,可以用於資料的加密和數字簽名。在RSA中,公鑰和私鑰是成對存在的,公鑰用於加密資料或驗證數字簽名,私鑰用於解密資料或生成數字簽名。

  2. RS256:RS256是一種基於RSA演算法的數字簽名演算法,其中“RS”代表RSA演算法,“256”表示使用SHA-256雜湊演算法生成摘要。RS256常用於JWT(JSON Web Token)的數字簽名過程中,用於驗證資料的完整性和真實性。

因此,可以說RS256是RSA演算法的一種特定應用,用於數字簽名,並且結合了SHA-256雜湊演算法。RSA演算法還可以用於加密資料等其他用途,而RS256主要用於數字簽名。

獲取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簽名使用rs256(SHA256withRSA)演算法生成的簽名,所以本例子都是採用這種簽名演算法實現的,例外,也有h256,h512等雜湊演算法。

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";

相關文章