- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我已经在java中成功实现了OpenID Connect。仅剩余部分经过验证 id_token。我已经使用 base64 解码了 id_token 并获取了用户电子邮件和姓名。现在我想验证 id_token 以便我可以使用该用户详细信息。谁能帮我验证 id_token?
我已尝试使用以下代码来验证 id_token
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.RSAPublicKeySpec;
import org.apache.commons.codec.binary.Base64;
public class ValidateIdToken {
public static final String id_token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjRlMjVjMTQ2Y2Y4NmU2MGM4ODYxNDdlYWQyNzBlYzAxYTllODU1YTEifQ.eyJhenAiOiIyODQzNDYxNzE1OTEtMGxtOGphamkwOW9iMzNuM2h1bXRrOXYyMzczcjgxcnEuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIyODQzNDYxNzE1OTEtMGxtOGphamkwOW9iMzNuM2h1bXRrOXYyMzczcjgxcnEuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDk0NjgzNjY4OTQ1NjQwMDU3MzgiLCJoZCI6ImluZm9vYmplY3RzLmNvbSIsImVtYWlsIjoia2lzaG9yZS5zdXRoYXJAaW5mb29iamVjdHMuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJod255SGc0dnlrOTZXVUxZYktyRTl3IiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwiaWF0IjoxNDk4NDc5MDIyLCJleHAiOjE0OTg0ODI2MjIsIm5hbWUiOiJLaXNob3JlIFN1dGhhciIsInBpY3R1cmUiOiJodHRwczovL2xoNS5nb29nbGV1c2VyY29udGVudC5jb20vLXJNb3kzeEpfRHA4L0FBQUFBQUFBQUFJL0FBQUFBQUFBQUFBL0FJNnlHWHp5WXdoeXMtU0FwVnhodDBJbTFOQktlYXpZa0Evczk2LWMvcGhvdG8uanBnIiwiZ2l2ZW5fbmFtZSI6Iktpc2hvcmUiLCJmYW1pbHlfbmFtZSI6IlN1dGhhciIsImxvY2FsZSI6ImVuIn0.iWJquu48swut8denWospcnOu-0QpQjWrw7d_K7lDdtwJ6xo6zE1rnmoOHw1xxeWrE9e2Vy-AqJ4mLHYn3rGoRe026oWLvwQMAgcNmhBENwvd9kAbYZukg_J7Ypy2Srnsu97u0q-QTAs3MBmxO0Yp2bHwuR5gmwEvTaE6gTtSZOf_Tg5FyrqaZgdrBuXxxLvQqHsNXIp4MxthxBtAH1kGo_KBrVAoKH1dRkKzsjSlt73nB_uKODGe2FoUwrNybItp9t5xE653pnxp2L6rbLJ2U9TU3gGSQ5yDdWPMt-AIAGJ7d1WOIHHGpN9FcT3KYEueS0j0h6Kfktr5XCCYnuEwQg";
public static final String[] id_token_parts = id_token.split("\\.");
public static final String MODULUS = "5SGw1jcqyFYEZaf39RoxAhlq-hfRSOsneVtsT2k09yEQhwB2myvf3ckVAwFyBF6y0Hr1psvu1FlPzKQ9YfcQkfge4e7eeQ7uaez9mMQ8RpyAFZprq1iFCix4XQw-jKW47LAevr9w1ttZY932gFrGJ4gkf_uqutUny82vupVUETpQ6HDmIL958SxYb_-d436zi5LMlHnTxcR5TWIQGGxip-CrD7vOA3hrssYLhNGQdwVYtwI768EvwE8h4VJDgIrovoHPH1ofDQk8-oG20eEmZeWugI1K3z33fZJS-E_2p_OiDVr0EmgFMTvPTnQ75h_9vyF1qhzikJpN9P8KcEm8oGu7KJGIn8ggUY0ftqKG2KcWTaKiirFFYQ981PhLHryH18eOIxMpoh9pRXf2y7DfNTyid99ig0GUH-lzAlbKY0EV2sIuvEsIoo6G8YT2uI72xzl7sCcp41FS7oFwbUyHp_uHGiTZgN7g-18nm2TFmQ_wGB1xCwJMFzjIXq1PwEjmg3W5NBuMLSbG-aDwjeNrcD_4vfB6yg548GztQO2MpV_BuxtrZDJQm-xhJXdm4FfrJzWdwX_JN9qfsP0YU1_mxtSU_m6EKgmwFdE3Yh1WM0-kRRSk3gmNvXpiKeVduzm8I5_Jl7kwLgBw24QUVaLZn8jC2xWRk_jcBNFFLQgOf9U";
public static final String EXPONENT = "AQAB";
public static final String ID_TOKEN_HEADER = base64UrlDecode(id_token_parts[0]);
public static final String ID_TOKEN_PAYLOAD = base64UrlDecode(id_token_parts[1]);
public static final byte[] ID_TOKEN_SIGNATURE = base64UrlDecodeToBytes(id_token_parts[2]);
public static String base64UrlDecode(String input) {
byte[] decodedBytes = base64UrlDecodeToBytes(input);
String result = new String(decodedBytes, StandardCharsets.UTF_8);
return result;
}
public static byte[] base64UrlDecodeToBytes(String input) {
Base64 decoder = new Base64(-1, null, true);
byte[] decodedBytes = decoder.decode(input);
return decodedBytes;
}
public static void main(String args[]) {
validateToken();
}
public static void validateToken() {
PublicKey publicKey = getPublicKey(MODULUS, EXPONENT);
byte[] data = (id_token_parts[0] + "." + id_token_parts[1]).getBytes(StandardCharsets.UTF_8);
try {
boolean isSignatureValid = verifyUsingPublicKey(data, ID_TOKEN_SIGNATURE, publicKey);
System.out.println("isSignatureValid: " + isSignatureValid);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
}
public static PublicKey getPublicKey(String MODULUS, String EXPONENT) {
byte[] nb = base64UrlDecodeToBytes(MODULUS);
byte[] eb = base64UrlDecodeToBytes(EXPONENT);
BigInteger n = new BigInteger(1, nb);
BigInteger e = new BigInteger(1, eb);
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(n, e);
try {
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(rsaPublicKeySpec);
return publicKey;
} catch (Exception ex) {
throw new RuntimeException("Cant create public key", ex);
}
}
private static boolean verifyUsingPublicKey(byte[] data, byte[] signature, PublicKey pubKey)
throws GeneralSecurityException {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
sig.update(data);
return sig.verify(signature);
}
}
错误
java.security.SignatureException: Signature length not correct: got 256 but was expecting 512
at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:189)
at java.security.Signature$Delegate.engineVerify(Signature.java:1219)
at java.security.Signature.verify(Signature.java:652)
at com.certain.sso.handler.ValidateIdToken.verifyUsingPublicKey(ValidateIdToken.java:75)
at com.certain.sso.handler.ValidateIdToken.validateToken(ValidateIdToken.java:45)
at com.certain.sso.handler.ValidateIdToken.main(ValidateIdToken.java:37)
最佳答案
Java 异常是由于您的签名未正确计算而导致的:ID token header 中的“HS256”alg 参数不仅仅意味着您需要计算 SHA-256 哈希并使用 RSA 私钥“加密”它,然后使用 base64 对其进行编码并将其填充在 ID token 的末尾。
HS256 意味着您需要使用 PKCS#1“SHA256withRSA”算法,该算法由 RFC-8017 完全指定。根据此 RFC,签名的八位位组长度必须与 RSA 模数相同。您的 RSA 模数为 512 字节长度,因此您的 ID token 的签名部分必须为 512 字节长度。即使哈希函数(HMAC-SHA256)返回256位长度的值。
因此,要从 256 位长度的 HMAC-SHA256 哈希值计算 512 字节长度的签名,RFC-8017 要求您使用 I2OSP 整数到八位字节字符串原语,它需要 2 个参数:256 位哈希值和整数 512(RSA 模数的长度)。 I2OSP 将输出一个 512 字节长度的八位字节字符串,您最终必须将其作为参数提供给 sig.verify()。
关于java - 如何在不使用任何库的情况下验证 id_token,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44762198/
我正在尝试创建一个 Shiny 的应用程序,该应用程序首先使用 OAuth 进行授权(请参阅 https://developers.google.com/identity/sign-in/web/si
我成功地将我的 Alexa 应用程序(客户端)链接到我们公司的 OpenID Connect 平台(授权服务器)。 我们的授权服务器向 Alexa 客户端返回以下信息: { "access_toke
我已经在java中成功实现了OpenID Connect。仅剩余部分经过验证 id_token。我已经使用 base64 解码了 id_token 并获取了用户电子邮件和姓名。现在我想验证 id_to
我正在实现“使用 PayPal 登录”功能。应该相当简单,但不幸的是 PayPal 缺少文档(只描述了一些基本的东西)。 我使用“使用 PayPal 登录”小部件获取 authorization_co
我想注册一个守护程序应用程序并为其授予 Microsoft graph API 的应用程序权限。现在,我将从我的客户端将 client_id 和 key 传递到端点,然后进行如下调用app.acqui
我想注册一个守护程序应用程序并为其授予 Microsoft graph API 的应用程序权限。现在,我将从我的客户端将 client_id 和 key 传递到端点,然后进行如下调用app.acqui
使用 Google OAuth2 API 时,我收到不一致的回复参数。 范围:['电子邮件','个人资料'] 预期响应: { access_token: 'ya29.fQB...aYSwXTK7E
使用 response_type=code 对支持 OpenID 的 OAuth2 授权服务器进行身份验证后与 scope=openid email ,调用 token 端点应返回 id_token
var login = function () { var deferred = $q.defer(); gapi.auth.authorize({
我正在使用 IdentityServer4,我想将计算字段添加到 access_token/id_token。 此类字段的示例可以是用户的 IP(或 token 绑定(bind)哈希), token
我们正在开发一个 Multi-Tenancy Web 应用程序。我们的租户将使用 Windows Azure Active Directory 进行身份验证。我们正在使用 OWIN OpenIdCon
我关注了以下博客 post实现苹果登录。它是很久以前写的,当时没有带有用户信息的 id_token。当我请求 token API 时,我收到包含多个字段的 JSON。问题是如何解码 id_token
引用 JWT(id_token) decoding docs 后 我很困惑主要服务提供商是否会遵循相同的技术...... 微软oauth2.0客户端在git project getUserEmailF
我在 Azure AD 中注册了一个应用程序。 如果我在 Web API 级别和客户端(SPA 应用程序)级别使用相同的应用程序 ID,为什么两个 Azure AD 身份验证库都这样做 (ADAL J
我正在使用从 Microsoft 到客户端的 jwt token 来验证从它到 Web API(服务器)的请求。我可以控制客户端 (js) 和服务器 (Python) 的代码。 在客户端,我使用以下请
我已成功通过我的 Angular 应用程序中的 Azure AD ( https://login.microsoftonline.com/ {tenantId}/oauth2/v2.0/authori
引用 JWT(id_token) decoding docs 后 我很困惑主要服务提供商是否会遵循相同的技术...... 微软oauth2.0客户端在git project getUserEmailF
我在 Azure AD 中注册了一个应用程序。 如果我在 Web API 级别和客户端(SPA 应用程序)级别使用相同的应用程序 ID,为什么两个 Azure AD 身份验证库都这样做 (ADAL J
我正在使用从 Microsoft 到客户端的 jwt token 来验证从它到 Web API(服务器)的请求。我可以控制客户端 (js) 和服务器 (Python) 的代码。 在客户端,我使用以下请
我目前正在实现 Google OpenID 以在我的网站上实现使用 google 登录。继tutorial given by google 。它强调只有 id_token 是发送到后端服务器的东西。由
我是一名优秀的程序员,十分优秀!