OIDC--对 JWT标准的id_token进行验证和解密。

来源:互联网 发布:数据分析属于什么行业 编辑:程序博客网 时间:2024/06/03 17:51

记录一下OIDC中id_token的验证

做的一个统一身份认证平台项目,用到了OIDC协议。
其中对OP返回的id_token进行验证的过程,写了一个demo。
用spring-boot写的,环境搭建就省略了,只是一个简单的方法。

package com.example.demo;import com.nimbusds.jose.*;import com.nimbusds.jose.crypto.RSASSASigner;import com.nimbusds.jose.crypto.RSASSAVerifier;import com.nimbusds.jose.jca.JCASupport;import com.nimbusds.jose.jwk.JWK;import com.nimbusds.jose.jwk.KeyUse;import com.nimbusds.jose.jwk.RSAKey;import com.nimbusds.jose.util.Base64URL;import com.nimbusds.jwt.*;import net.minidev.json.JSONObject;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.stereotype.Controller;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;import java.util.Map;/** * @author huangteng * @version 1.0.0 * @description for world peace * @time 15:26 2017/9/27 * @modified by: * @modified time: */@Controller@EnableAutoConfigurationpublic class HelloController {    private Map<String, JWSSigner> signers = new HashMap<>();    private Map<String, JWSVerifier> verifiers = new HashMap<>();    @RequestMapping("/hello")    @ResponseBody    public String hello(){        byte[] bytes = null;        String str = "eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiI5MDM0Mi5BU0RGSldGQSIsImF1ZCI6ImNsaWVudCIsImtpZCI6InJzYTEiLCJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3Q6ODA4MFwvb3BlbmlkLWNvbm5lY3Qtc2VydmVyLXdlYmFwcFwvIiwiZXhwIjoxNTA2NzQwODU5LCJpYXQiOjE1MDY3NDAyNTksImp0aSI6ImY1NmYxYjQ5LTMwNWMtNDhmMC1hODRkLWEzNWYyYzA5M2E3OCJ9.CGncdBpF_NRo1ZOR4JNMfDprdrDSQsrBGquQE8d7Xo5ujA0vHOQranKJFixb_r2qOZNtgez4Yn3NAO8oVRpA1rpwD2QDhJX9sw-JBrAS5_0_C1srHxAqqIF3h4mgZ0gjsXODiYobCJCspSiOQVsw-nxDU21fi5lTLWjaHZeVB4Cxd2InOOYrY-PeF1TKbdY9cxpFxOjLLoZML2O4BoXCAqFYK7nBRzL5iRcp6NOB6oRYzzbi-nG5b2_lUtP_9rsu0rTCwJxUMCbB7sTJU2kVTBZR0fOoi66H0vW_sI1UggxYbJ065JNAutLaGdbFXQHtm2hrTB6DmxTYOuU3fhqOIg";        decryptJWT(str);        return "hello world";    }    /**     * 创建一个解密加密的工具方法     * @param jwk     * @return     */    public void buildSignersAndVerifiers( JWK jwk ){        if( jwk instanceof RSAKey ){            System.out.println("这是一个采用rsaKey");            /**             * 公钥加密,私钥解密             * 这里加密的过程就省略了             */            if( jwk.isPrivate() ){                try{                    if (jwk.isPrivate()) {                        RSASSASigner signer = new RSASSASigner((RSAKey) jwk);                        signers.put("test", signer);                    }                    RSASSAVerifier verifier = new RSASSAVerifier((RSAKey) jwk);                    verifiers.put("test", verifier);                }catch ( Exception e ){                    System.out.println( "error: " + e.toString() );                }            }else{                System.out.println( "你确定你的是私钥" );            }        }    }    /**     * 解密     * @param jwtString     * @return     */    public Map decryptJWT(String jwtString){        Map map = new HashMap();        if(StringUtils.isEmpty(jwtString)) return null;        String kid = "rsa1";        String n = "qt6yOiI_wCoCVlGO0MySsez0VkSqhPvDl3rfabOslx35mYEO-n4ABfIT5Gn2zN-CeIcOZ5ugAXvIIRWv5H55-tzjFazi5IKkOIMCiz5__MtsdxKCqGlZu2zt-BLpqTOAPiflNPpM3RUAlxKAhnYEqNha6-allPnFQupnW_eTYoyuzuedT7dSp90ry0ZcQDimntXWeaSbrYKCj9Rr9W1jn2uTowUuXaScKXTCjAmJVnsD75JNzQfa8DweklTyWQF-Y5Ky039I0VIu-0CIGhXY48GAFe2EFb8VpNhf07DP63p138RWQ1d3KPEM9mYJVpQC68j3wzDQYSljpLf9by7TGw";        String e = "AQAB";        String d = "PvBAngE3kkTnD3yDKo3wCvHJHm20kb9a0FVGLd0s2Y0E_3H2XnZC8-2zPhN6AQTjPhohSDCew20gzm76lyOvMqRiUP2Zpaopa1d2fGvNIQSdM07yKa6EivEYxqPQxa5esoZnexgnb9fom70I8n5OQRNQikwu-az26CsHX2zWMRodzSdN5CXHvb1PV09DmH8azTYwoMElPIqmcTfxiRw2Ov5ucmXXngKRFJgvfUgKd7v4ScBX7sQoQEjWEtt7ta0WvL3Ar5E1RAW4aHxuubZ6AtloxWCf17AAKw03dfP5RDm5TDmgm2B635ecJ7fTvneFmg8W_fdMTPRfBlCGNBp3wQ";        try{            JWT jwt = JWTParser.parse(jwtString);            /**             * 判断JWT类型             */            if (jwt instanceof SignedJWT){                System.out.println("这是一个签名的jwt");                /**                 * 判断对RSA算法的支持                 */                if(JCASupport.isSupported(JWEAlgorithm.RSA_OAEP) && JCASupport.isSupported(EncryptionMethod.A256GCM)){                    System.out.println("提供算法支持");                    /**                     *  header                     *  主要包括 类型:jwt 算法:***                     */                    JSONObject obj = jwt.getHeader() != null ? jwt.getHeader().toJSONObject() : null;                    System.out.println(obj);                    /**                     *  payload                     *      iss: jwt签发者                     *      sub: jwt所面向的用户                     *      aud: 接收jwt的一方                     *      exp: jwt的过期时间,这个过期时间必须要大于签发时间                     *      nbf: 定义在什么时间之前,该jwt都是不可用的                     *      iat: jwt的签发时间                     *      jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。                     *      。。。                     */                    JSONObject payloadHeader = ((SignedJWT) jwt).getPayload().toJSONObject() == null ? null : ((SignedJWT) jwt).getPayload().toJSONObject();                    System.out.println(payloadHeader);                    /**                     *  签名 = HSA256(Base64(header), Base64(payload), 密钥)                     *                     *  如果用RSA算法:                     *  RSA Algorithm for SignedJWT                     *  获取 n, e, d 然后(n, e)作为公钥,(n, d) 作为私钥                     *  RSA:                     *  1. 两个足够大的互质数p, q;                     *  2. 模运算 n = p * q;                     *  3. (n, e)作为公钥,e满足1<e< (p-1)(q-1),且与(p-1)(q-1)互质                     *  4. (n, d) 作为私钥,d满足d*e % (p-1)(q-1)= 1,%是取余运算                     */                    JWK RSAjwk = new RSAKey(                            new Base64URL(n),                            new Base64URL(e),                            new Base64URL(d),                            KeyUse.SIGNATURE,                            null,                            JWEAlgorithm.RSA_OAEP,                            kid,                            null,                            null,                            null,                            null                    );                    /**                     * 对jwt进行验证。                     */                    buildSignersAndVerifiers(RSAjwk);                    JWSSigner signer = signers.get("test");                    JWSVerifier verifier = verifiers.get("test");                    if(((SignedJWT) jwt).verify(verifier)){                        System.out.println("通过验证");                        /**                         *  外层已经验证了id_token的有效性了                         *  再验证签名的有效性,采用解析出 Header, ClimeSet 然后我再签名一次。                         *  最后比对前后的签名是不是一样。                         */                        // 获取算法                        String alg = (String)obj.get("alg");                        JWSAlgorithm a = JWSAlgorithm.parse(alg);                        // 获取之前的签名                        String pre_sign = jwt.getParsedParts()[2].toString();                        // 再次签名                        if(signer.supportedJWSAlgorithms().contains(a)){                            SignedJWT new_jwt = new SignedJWT(((SignedJWT)jwt).getHeader(), ((SignedJWT)jwt).getJWTClaimsSet());                            new_jwt.sign(signer);                            System.out.println(pre_sign);                            System.out.println(new_jwt.getSignature().toString());                            /**                             * 比对                             */                            if(pre_sign.equals(new_jwt.getSignature().toString())){                                System.out.println("签名有效");                            }else{                                System.out.println("失败,签名很可能被篡改了!");                            }                        }                    }else{                        System.out.println("验证失败");                    }                }else{                    System.out.println("不支持算法");                }            }else if (jwt instanceof PlainJWT){                System.out.println("这是一个普通的 PlainJWT");            }else if(jwt instanceof EncryptedJWT){                System.out.println("这是一个加密的 EncryptedJWT");            }        }catch (Exception es){            System.out.println(es.toString());        }        return map;    }}
原创粉丝点击