zoukankan      html  css  js  c++  java
  • 各类JWT库(java)的对比

    参考和转载于 :http://andaily.com/blog/?p=956

    在 https://jwt.io/ 网站中收录有各类语言的JWT库实现(有关JWT详细介绍请访问 https://jwt.io/introduction/),其中JAVA语言到目前(2020-09)有6个实现库

    按顺序依次是
    
    Auth0实现 的 java-jwt
     -- maven: com.auth0 / java-jwt / 3.3.0 Brian Campbell实现的 jose4j
      -- maven: org.bitbucket.b_c / jose4j / 0.6.3 connect2id实现的 nimbus
    -jose-jwt   -- maven: com.nimbusds / nimbus-jose-jwt / 5.7
    Les Haziewood实现的 jjwt
      -- maven: io.jsonwebtoken / jjwt-root / 0.11.1 Inversoft实现的prime
    -jwt
      -- maven: io.fusionauth / fusionauth-jwt / 3.5.0 Vertx实现的vertx
    -auth-jwt.
      -- maven: io.vertx / vertx-auth-jwt / 3.5.1 

    以下是各个库的使用测试

    java-jwt

    package myoidc.server.infrastructure;
    
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import org.junit.Test;
    
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.RSAPublicKey;
    
    import static org.junit.Assert.assertNotNull;
    
    /**
     * 2018/5/30
     * <p>
     * OAuth0  jwt
     *
     * @author Shengzhao Li
     */
    public class Auth0JwtTest {
    
    
        /**
         * Test JWT
         *
         * @throws Exception Exception
         */
        @Test
        public void jwt() throws Exception {
    
            // RSA keyPair Generator
            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            /*
             * 长度 至少 1024, 建议 2048
             */
            final int keySize = 2048;
            keyPairGenerator.initialize(keySize);
    
            final KeyPair keyPair = keyPairGenerator.genKeyPair();
    
    
            final PublicKey publicKey = keyPair.getPublic();
            final PrivateKey privateKey = keyPair.getPrivate();
    
    
            // gen id_token
            final Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
    
            final String idToken = JWT.create().withJWTId("jwt-id").withAudience("audience").withSubject("subject").sign(algorithm);
    
            assertNotNull(idToken);
            System.out.println(idToken);
    
    
            //verify
    //        final DecodedJWT decodedJWT = JWT.decode(idToken);
    //        System.out.println("id_token -> header: " + decodedJWT.getHeader());
    //        System.out.println("id_token -> payload: " + decodedJWT.getPayload());
    //        System.out.println("id_token -> token: " + decodedJWT.getToken());
    //        System.out.println("id_token -> signature: " + decodedJWT.getSignature());
    
    
            final JWTVerifier verifier = JWT.require(algorithm).build();
            final DecodedJWT verify = verifier.verify(idToken);
    
            assertNotNull(verify);
            System.out.println(verify);
    
    
    //        final Algorithm none = Algorithm.none();
    
        }
    
    
    }

    代码地址:  https://github.com/monkeyk/MyOIDC/blob/1.1.0/myoidc-server/src/test/java/myoidc/server/infrastructure/Auth0JwtTest.java

    点评:

    Auth0提供的JWT库简单实用, 依赖第三方(如JAVA运行环境)提供的证书信息(keypair);有一问题是在 生成id_token与 校验(verify)id_token时都需要 公钥(public key)与密钥(private key), 个人感觉是一不足(实际上在校验时只需要public key即可)

    jose4j

    package myoidc.server.infrastructure;
    
    
    import com.google.common.base.Charsets;
    import com.google.common.io.CharStreams;
    import org.apache.commons.lang3.RandomStringUtils;
    import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
    import org.jose4j.jwe.JsonWebEncryption;
    import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;
    import org.jose4j.jwk.*;
    import org.jose4j.jws.AlgorithmIdentifiers;
    import org.jose4j.jws.JsonWebSignature;
    import org.jose4j.jwt.JwtClaims;
    import org.jose4j.jwt.consumer.JwtConsumer;
    import org.jose4j.jwt.consumer.JwtConsumerBuilder;
    import org.jose4j.keys.AesKey;
    import org.jose4j.keys.EllipticCurves;
    import org.jose4j.keys.RsaKeyUtil;
    import org.junit.Test;
    
    
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.security.Key;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    
    import static myoidc.server.Constants.*;
    import static org.junit.Assert.*;
    
    
    /**
     * 2016/12/25
     * <p/>
     * Testing
     * https://bitbucket.org/b_c/jose4j
     *
     * @author Shengzhao Li
     */
    public class Jose4JTest {
    
    
        @Test
        public void testRsaJsonWebKey() throws Exception {
    
            RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(DEFAULT_KEY_SIZE);
            //sig or enc
            jwk.setUse(USE_SIG);
            jwk.setKeyId(DEFAULT_KEY_ID);
            jwk.setAlgorithm(OIDC_ALG);
    //        jwk.setKeyOps();
    
            final String publicKeyString = jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
            final String privateKeyString = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
    
            assertNotNull(publicKeyString);
            assertNotNull(privateKeyString);
    //        System.out.println("PublicKey:
    " + publicKeyString);
    //        System.out.println("PrivateKey:
    " + privateKeyString);
    
    
            try (InputStream is = getClass().getClassLoader().getResourceAsStream(KEYSTORE_NAME)) {
                String keyJson = CharStreams.toString(new InputStreamReader(is, Charsets.UTF_8));
                JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(keyJson);
                assertNotNull(jsonWebKeySet);
                JsonWebKey jsonWebKey = jsonWebKeySet.findJsonWebKey(DEFAULT_KEY_ID, RsaKeyUtil.RSA, USE_SIG, OIDC_ALG);
                assertNotNull(jsonWebKey);
    //            System.out.println(jsonWebKey);
            }
    
    
        }
    
    
        /**
         * RSA 加密与解密, 256位
         *
         * @since 1.1.0
         */
        @Test
        public void aesEncryptDecryptRSA() throws Exception {
    
            RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048);
            jwk.setKeyId(keyId());
    
            final String publicKeyString = jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
            final String privateKeyString = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
    
            String data = "I am marico 3";
            //加密
            JsonWebEncryption jwe = new JsonWebEncryption();
            jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
            jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_CBC_HMAC_SHA_512);
            PublicKey publicKey = RsaJsonWebKey.Factory.newPublicJwk(publicKeyString).getPublicKey();
            jwe.setKey(publicKey);
            jwe.setPayload(data);
    
            String idToken = jwe.getCompactSerialization();
            assertNotNull(idToken);
            System.out.println(data + " ->>: " + idToken);
    
    
            //解密
            JsonWebEncryption jwe2 = new JsonWebEncryption();
            PrivateKey privateKey = RsaJsonWebKey.Factory.newPublicJwk(privateKeyString).getPrivateKey();
            jwe2.setKey(privateKey);
    //        jwe2.setKey(jwk.getRsaPrivateKey());
            jwe2.setCompactSerialization(idToken);
    
            final String payload = jwe2.getPayload();
            assertNotNull(payload);
            assertEquals(payload, data);
    
        }
    
    
        /*
        * AES 加密与解密, 128位
        * */
        @Test
        public void aesEncryptDecrypt128() throws Exception {
    
            String keyText = "iue98623diDEs096";
            String data = "I am marico";
            Key key = new AesKey(keyText.getBytes());
    
            //加密
            JsonWebEncryption jwe = new JsonWebEncryption();
            jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128KW);
            jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
            jwe.setKey(key);
            jwe.setPayload(data);
    
            String idToken = jwe.getCompactSerialization();
            assertNotNull(idToken);
            System.out.println(data + " idToken: " + idToken);
    
            //解密
            JsonWebEncryption jwe2 = new JsonWebEncryption();
            jwe2.setKey(key);
            jwe2.setCompactSerialization(idToken);
    
            final String payload = jwe2.getPayload();
            assertNotNull(payload);
            assertEquals(payload, data);
    
        }
    
    
        /*
        * AES 加密与解密, 256位
        * */
        @Test
        public void aesEncryptDecrypt256() throws Exception {
    
            String keyText = "iue98623diDEs096_8u@idls(*JKse09";
            String data = "I am marico";
            Key key = new AesKey(keyText.getBytes());
    
            //加密
            JsonWebEncryption jwe = new JsonWebEncryption();
            jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A256KW);
            jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_CBC_HMAC_SHA_512);
            jwe.setKey(key);
            jwe.setPayload(data);
    
            String idToken = jwe.getCompactSerialization();
            assertNotNull(idToken);
            System.out.println(data + " idToken: " + idToken);
    
            //解密
            JsonWebEncryption jwe2 = new JsonWebEncryption();
            jwe2.setKey(key);
            jwe2.setCompactSerialization(idToken);
    
            final String payload = jwe2.getPayload();
            assertNotNull(payload);
            assertEquals(payload, data);
    
        }
    
    
        /**
         * JWT 生成 idToken, 并进行消费(consume)
         * 算法:RSA SHA256
         *
         * @throws Exception
         */
        @Test
        public void jwtIdTokenConsumer() throws Exception {
    
            String keyId = keyId();
    
            //生成idToken
            JwtClaims claims = getJwtClaims();
    
            JsonWebSignature jws = new JsonWebSignature();
            jws.setPayload(claims.toJson());
            jws.setKeyIdHeaderValue(keyId);
            // Set the signature algorithm on the JWT/JWS that will integrity protect the claims
            jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
    
    
            RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048);
            jwk.setKeyId(keyId);
            //set private key
            jws.setKey(jwk.getPrivateKey());
    
    //        jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
    
            final String idToken = jws.getCompactSerialization();
            assertNotNull(idToken);
            System.out.println("idToken: " + idToken);
    
    
            //解析idToken, 验签
            JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                    .setRequireExpirationTime() // the JWT must have an expiration time
                    .setMaxFutureValidityInMinutes(300) // but the  expiration time can't be too crazy
                    .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
                    .setRequireSubject() // the JWT must have a subject claim
                    .setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by
                    .setExpectedAudience("Audience") // to whom the JWT is intended for
                    //公钥
                    .setVerificationKey(jwk.getKey()) // verify the signature with the public key
                    .build(); // create the JwtConsumer instance
    
            final JwtClaims jwtClaims = jwtConsumer.processToClaims(idToken);
            assertNotNull(jwtClaims);
            System.out.println(jwtClaims);
    
        }
    
        private String keyId() {
            return RandomStringUtils.random(32, true, true);
        }
    
        private JwtClaims getJwtClaims() {
            JwtClaims claims = new JwtClaims();
            claims.setIssuer("Issuer");  // who creates the token and signs it
            claims.setAudience("Audience"); // to whom the token is intended to be sent
            claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now)
            claims.setGeneratedJwtId(); // a unique identifier for the token
            claims.setIssuedAtToNow();  // when the token was issued/created (now)
            claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
            claims.setSubject("subject"); // the subject/principal is whom the token is about
            claims.setClaim("email", "mail@example.com"); // additional claims/attributes about the subject can be added
            return claims;
        }
    
    
        /**
         * JWT 生成 idToken+加密, 进行消费(consume)
         * 使用EC
         *
         * @throws Exception
         */
        @Test
        public void jwtECIdTokenConsumer() throws Exception {
    
    //        String keyId = GuidGenerator.generate();
            EllipticCurveJsonWebKey sendJwk = EcJwkGenerator.generateJwk(EllipticCurves.P256);
            sendJwk.setKeyId(keyId());
    
            final String publicKeyString = sendJwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
            final String privateKeyString = sendJwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
            System.out.println("publicKeyString: " + publicKeyString);
            System.out.println("privateKeyString: " + privateKeyString);
    
            //生成 idToken
            final JwtClaims jwtClaims = getJwtClaims();
            JsonWebSignature jws = new JsonWebSignature();
            jws.setPayload(jwtClaims.toJson());
            //私钥
            jws.setKey(sendJwk.getPrivateKey());
            jws.setKeyIdHeaderValue(sendJwk.getKeyId());
            jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);
    
            String innerIdToken = jws.getCompactSerialization();
            assertNotNull(innerIdToken);
            System.out.println("innerIdToken: " + innerIdToken);
    
    
            //对 idToken 进行加密
            JsonWebEncryption jwe = new JsonWebEncryption();
            jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES_A128KW);
            String encAlg = ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256;
            jwe.setEncryptionMethodHeaderParameter(encAlg);
    
    
            EllipticCurveJsonWebKey receiverJwk = EcJwkGenerator.generateJwk(EllipticCurves.P256);
            receiverJwk.setKeyId(keyId());
    
            jwe.setKey(receiverJwk.getPublicKey());
            jwe.setKeyIdHeaderValue(receiverJwk.getKeyId());
    
            jwe.setContentTypeHeaderValue("JWT");
            jwe.setPayload(innerIdToken);
    
            String idToken = jwe.getCompactSerialization();
            assertNotNull(idToken);
            System.out.println("idToken: " + idToken);
    
    
            //解析idToken, 验签
            JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                    .setRequireExpirationTime() // the JWT must have an expiration time
                    .setRequireSubject() // the JWT must have a subject claim
                    .setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by
                    .setExpectedAudience("Audience") // to whom the JWT is intended for
                    //解密的私钥
                    .setDecryptionKey(receiverJwk.getPrivateKey()) // decrypt with the receiver's private key
                    //验签的公钥
                    .setVerificationKey(sendJwk.getPublicKey()) // verify the signature with the sender's public key
                    .build(); // create the JwtConsumer instance
    
            final JwtClaims claims = jwtConsumer.processToClaims(idToken);
            assertNotNull(claims);
            System.out.println(claims);
    
    
        }
    
    
    }

    代码地址: https://github.com/monkeyk/MyOIDC/blob/1.1.0/myoidc-server/src/test/java/myoidc/server/infrastructure/Jose4JTest.java

    点评:

    jose4j提供了完整的JWT实现, 可以不依赖第三方提供的证书信息(keypair, 库本身自带有RSA的实现),类定义与JWT协议规定匹配度高,易理解与上手对称加密与非对称加密都有提供实现

    nimbus-jose-jwt

    package myoidc.server.infrastructure;
    
    import com.google.common.base.Charsets;
    import com.google.common.io.CharStreams;
    import com.nimbusds.jose.*;
    import com.nimbusds.jose.crypto.*;
    import com.nimbusds.jose.jwk.Curve;
    import com.nimbusds.jose.jwk.JWK;
    import com.nimbusds.jose.jwk.JWKSet;
    import com.nimbusds.jwt.EncryptedJWT;
    import com.nimbusds.jwt.JWTClaimsSet;
    import com.nimbusds.jwt.SignedJWT;
    import net.minidev.json.JSONObject;
    import org.apache.commons.lang3.RandomStringUtils;
    import org.junit.Ignore;
    import org.junit.Test;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import java.io.InputStreamReader;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.interfaces.ECPrivateKey;
    import java.security.interfaces.ECPublicKey;
    import java.security.interfaces.RSAPublicKey;
    import java.util.Date;
    import java.util.List;
    
    import static org.junit.Assert.*;
    
    
    /**
     * 2016/12/25
     * <p/>
     * Testing
     * http://connect2id.com/products/nimbus-jose-jwt
     *
     * @author Shengzhao Li
     */
    public class NimbusJoseJwtTest {
    
    
        /**
         * @throws Exception e
         * @since 1.1.0
         */
        @Test
        @Ignore
        public void testJWKSet() throws Exception {
    
            Resource resource = new ClassPathResource("classpath*:keystore.jwks");
            // read in the file
            String s = CharStreams.toString(new InputStreamReader(resource.getInputStream(), Charsets.UTF_8));
            JWKSet jwkSet = JWKSet.parse(s);
            assertNotNull(jwkSet);
    //        System.out.println(jwkSet);
    
            List<JWK> keys = jwkSet.getKeys();
            for (JWK key : keys) {
    //            System.out.println(key);
    //            System.out.println(key.getAlgorithm());
    //            System.out.println(key.getKeyStore());
    //            System.out.println(key.getKeyUse());
    //            System.out.println(key.getKeyType());
    //            System.out.println(key.getParsedX509CertChain());
                System.out.println(key.getKeyID());
                System.out.println(key.isPrivate());
    
    //            JWK jwk = key.toPublicJWK();
    //            System.out.println(jwk);
    //            JSONObject jsonObject = key.toJSONObject();
    //            System.out.println(jsonObject);
    
    //            PublicJsonWebKey rsk = RsaJsonWebKey.Factory.newPublicJwk(key.toString());
    //            PrivateKey privateKey = rsk.getPrivateKey();
    //            PublicKey publicKey = rsk.getPublicKey();
    //            System.out.println(publicKey + "
    " + privateKey);
    
    //            RSAKey  rsaKey= new RSAKey();
    //            rsaKey.
            }
        }
    
    
        /**
         * JWS
         * 使用HMAC SHA-256 进行加密 与 解密
         * 基于相同的 secret (对称算法)
         * <p/>
         * 算法     Secret长度
         * HS256   32
         * HS384   64
         * HS512   64
         *
         * @throws Exception
         */
        @Test
        public void jwsMAC() throws Exception {
    
            String sharedSecret = RandomStringUtils.random(64, true, true);
            JWSSigner jwsSigner = new MACSigner(sharedSecret);
    
            //加密
    //        JWSHeader header = new JWSHeader(JWSAlgorithm.HS256);
    //        JWSHeader header = new JWSHeader(JWSAlgorithm.HS384);
            JWSHeader header = new JWSHeader(JWSAlgorithm.HS512);
            final String payloadText = "I am MyOIDC";
            Payload payload = new Payload(payloadText);
            JWSObject jwsObject = new JWSObject(header, payload);
    
            jwsObject.sign(jwsSigner);
            //获取 idToken
            final String idToken = jwsObject.serialize();
            System.out.println(payloadText + " -> id_token: " + idToken);
    
            //解密
            JWSVerifier verifier = new MACVerifier(sharedSecret);
            final JWSObject parseJWS = JWSObject.parse(idToken);
            final boolean verify = parseJWS.verify(verifier);
    
            assertTrue(verify);
            final String decryptPayload = parseJWS.getPayload().toString();
            assertEquals(decryptPayload, payloadText);
        }
    
        /**
         * JWT
         * 使用HMAC SHA-256 进行加密 与 解密
         * 基于相同的 secret (对称算法)
         * <p/>
         * 算法     Secret长度
         * HS256   32
         * HS384   64
         * HS512   64
         *
         * @throws Exception
         */
        @Test
        public void jwtMAC() throws Exception {
    
            String sharedSecret = RandomStringUtils.random(64, true, true);
            JWSSigner jwsSigner = new MACSigner(sharedSecret);
    
            //生成idToken
            final String payloadText = "I am MyOIDC";
            JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                    .subject("subject")
                    .issuer("https://andaily.com")
                    .claim("payloadText", payloadText)
                    .expirationTime(new Date(new Date().getTime() + 60 * 1000))
                    .build();
    
    //        final JWSHeader header = new JWSHeader(JWSAlgorithm.HS256);
    //        final JWSHeader header = new JWSHeader(JWSAlgorithm.HS384);
            final JWSHeader header = new JWSHeader(JWSAlgorithm.HS512);
            SignedJWT signedJWT = new SignedJWT(header, claimsSet);
            signedJWT.sign(jwsSigner);
    
            final String idToken = signedJWT.serialize();
    
            //校验idToken
            final SignedJWT parseJWT = SignedJWT.parse(idToken);
            JWSVerifier jwsVerifier = new MACVerifier(sharedSecret);
            final boolean verify = parseJWT.verify(jwsVerifier);
    
            assertTrue(verify);
    //        final Payload payload = parseJWT.getPayload();
            final JWTClaimsSet jwtClaimsSet = parseJWT.getJWTClaimsSet();
            assertEquals(jwtClaimsSet.getSubject(), "subject");
    
        }
    
    
        /**
         * JWS
         * 使用 RSA 算法 生成 id_token
         * 以及对其进行校验(verify)
         * 需要公私钥对
         * <p/>
         * 支持算法
         * RS256
         * RS384
         * RS512
         *
         * @throws Exception
         */
        @Test
        public void jwsRSA() throws Exception {
    
            // RSA keyPair Generator
            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            /**
             * 长度 至少 1024, 建议 2048
             */
            final int keySize = 2048;
            keyPairGenerator.initialize(keySize);
    
            final KeyPair keyPair = keyPairGenerator.genKeyPair();
            //公钥
            final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            //私钥
            final PrivateKey privateKey = keyPair.getPrivate();
    
            //keyId
            String keyId = RandomUtils.randomNumber();
    
            //生成id_token
            JWSSigner jwsSigner = new RSASSASigner(privateKey);
    
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(keyId).build();
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS384).keyID(keyId).build();
            JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS512).keyID(keyId).build();
    
            final String payloadText = "I am MyOIDC [RSA]";
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("Issuer", "Issuer");
            jsonObject.put("Audience", "Audience");
            jsonObject.put("payloadText", payloadText);
    
    //        Payload payload = new Payload(payloadText);
            Payload payload = new Payload(jsonObject);
            JWSObject jwsObject = new JWSObject(header, payload);
    
            jwsObject.sign(jwsSigner);
            final String idToken = jwsObject.serialize();
            System.out.println(payloadText + " -> id_token: " + idToken);
    
    
            //校验 id_token
            final JWSObject parseJWS = JWSObject.parse(idToken);
    
            JWSVerifier verifier = new RSASSAVerifier(publicKey);
            final boolean verify = parseJWS.verify(verifier);
            assertTrue(verify);
    
            final Payload payload1 = parseJWS.getPayload();
            assertNotNull(payload1);
            final JSONObject jsonObject1 = payload1.toJSONObject();
            assertNotNull(jsonObject1);
    
            assertEquals(payloadText, jsonObject1.get("payloadText"));
    
        }
    
    
        /**
         * JWT
         * 使用 RSA 算法 生成 id_token
         * 以及对其进行校验(verify)
         * 需要公私钥对
         * <p/>
         * 支持算法
         * RS256
         * RS384
         * RS512
         *
         * @throws Exception
         */
        @Test
        public void jwtRSA() throws Exception {
    
            // RSA keyPair Generator
            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            /**
             * 长度 至少 1024, 建议 2048
             */
            final int keySize = 2048;
            keyPairGenerator.initialize(keySize);
    
            final KeyPair keyPair = keyPairGenerator.genKeyPair();
            //公钥
            final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            //私钥
            final PrivateKey privateKey = keyPair.getPrivate();
    
            //keyId
            String keyId = RandomUtils.randomNumber();
    
            //生成id_token
            JWSSigner jwsSigner = new RSASSASigner(privateKey);
    
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(keyId).build();
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS384).keyID(keyId).build();
            JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS512).keyID(keyId).build();
    
            final String payloadText = "I am MyOIDC [RSA]";
            JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                    .subject("subject")
                    .issuer("Issuer")
                    .audience("Audience")
                    .claim("payloadText", payloadText)
                    .expirationTime(new Date(new Date().getTime() + 60 * 1000))
                    .build();
    
            SignedJWT signedJWT = new SignedJWT(header, claimsSet);
    
            signedJWT.sign(jwsSigner);
            final String idToken = signedJWT.serialize();
            System.out.println(payloadText + " -> id_token: " + idToken);
    
    
            //校验 id_token
            final SignedJWT parseJWT = SignedJWT.parse(idToken);
    
            JWSVerifier verifier = new RSASSAVerifier(publicKey);
            final boolean verify = parseJWT.verify(verifier);
            assertTrue(verify);
    
            final JWTClaimsSet jwtClaimsSet = parseJWT.getJWTClaimsSet();
            assertNotNull(jwtClaimsSet);
            assertEquals(payloadText, jwtClaimsSet.getStringClaim("payloadText"));
    
    
        }
    
    
        /**
         * JWS
         * 使用 EC 算法 生成 id_token
         * 以及对其进行校验(verify)
         * 需要公私钥对
         * <p/>
         * 支持算法
         * ES256
         * ES384
         * ES512
         *
         * @throws Exception
         */
        @Test
        public void jwsEC() throws Exception {
    
            //EC KeyPair
            KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("EC");
    //        keyGenerator.initialize(ECKey.Curve.P_256.toECParameterSpec());
    //        keyGenerator.initialize(ECKey.Curve.P_384.toECParameterSpec());
            keyGenerator.initialize(Curve.P_521.toECParameterSpec());
            KeyPair keyPair = keyGenerator.generateKeyPair();
    
            ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
            ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
    
            //keyId
            String keyId = RandomUtils.randomNumber();
    
            //生成id_token
            JWSSigner signer = new ECDSASigner(privateKey);
    
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(keyId).build();
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(keyId).build();
            JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES512).keyID(keyId).build();
    
            final String payloadText = "I am MyOIDC [ECDSA]";
            Payload payload = new Payload(payloadText);
            JWSObject jwsObject = new JWSObject(header, payload);
    
            jwsObject.sign(signer);
            final String idToken = jwsObject.serialize();
            System.out.println(payloadText + " -> id_token: " + idToken);
    
    
            //校验 id_token
            final JWSObject parseJWS = JWSObject.parse(idToken);
    
            JWSVerifier verifier = new ECDSAVerifier(publicKey);
            final boolean verify = parseJWS.verify(verifier);
    
            assertTrue(verify);
            final String s = parseJWS.getPayload().toString();
            assertEquals(s, payloadText);
    
        }
    
    
        /**
         * JWT
         * 使用 EC 算法 生成 id_token
         * 以及对其进行校验(verify)
         * 需要公私钥对
         * <p/>
         * 支持算法
         * ES256
         * ES384
         * ES512
         *
         * @throws Exception
         */
        @Test
        public void jwtEC() throws Exception {
    
            //EC KeyPair
            KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("EC");
    //        keyGenerator.initialize(ECKey.Curve.P_256.toECParameterSpec());
    //        keyGenerator.initialize(ECKey.Curve.P_384.toECParameterSpec());
            keyGenerator.initialize(Curve.P_521.toECParameterSpec());
            KeyPair keyPair = keyGenerator.generateKeyPair();
    
            ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
            ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
    
            //keyId
            String keyId = RandomUtils.randomNumber();
    
            //生成id_token
            JWSSigner signer = new ECDSASigner(privateKey);
    
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(keyId).build();
    //        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES384).keyID(keyId).build();
            JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES512).keyID(keyId).build();
    
            final String payloadText = "I am MyOIDC [ECDSA]";
            JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                    .subject("subject")
                    .issuer("Issuer")
                    .audience("Audience")
                    .claim("payloadText", payloadText)
                    .expirationTime(new Date(new Date().getTime() + 60 * 1000))
                    .build();
            SignedJWT signedJWT = new SignedJWT(header, claimsSet);
    
            signedJWT.sign(signer);
            final String idToken = signedJWT.serialize();
            System.out.println(payloadText + " -> id_token: " + idToken);
    
    
            //校验 id_token
            final SignedJWT parseJWS = SignedJWT.parse(idToken);
    
            JWSVerifier verifier = new ECDSAVerifier(publicKey);
            final boolean verify = parseJWS.verify(verifier);
    
            assertTrue(verify);
            final JWTClaimsSet jwtClaimsSet = parseJWS.getJWTClaimsSet();
            assertEquals(jwtClaimsSet.getClaim("payloadText"), payloadText);
    
        }
    
    
        /**
         * 使用RSA 算法进行加密数据
         * 与解密数据
         * <p/>
         * 128
         * RSA_OAEP   - A128GCM
         * 256
         * RSA_OAEP_256 - A256GCM
         *
         * @throws Exception
         */
        @Test
        public void jwtRSAEncryption() throws Exception {
    
            // RSA keyPair Generator
            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            /**
             * 长度 至少 1024, 建议 2048
             */
            final int keySize = 2048;
            keyPairGenerator.initialize(keySize);
    
            final KeyPair keyPair = keyPairGenerator.genKeyPair();
            //公钥
            final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            //私钥
            final PrivateKey privateKey = keyPair.getPrivate();
    
    
            //加密, 生成idToken
            //加密的数据放在 JWTClaimsSet 中
            JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                    .issuer("https://myoidc.cc")
                    .subject("Lims")
                    .audience("https://one-app.com")
                    .notBeforeTime(new Date())
                    .issueTime(new Date())
                    .expirationTime(new Date(new Date().getTime() + 1000 * 60 * 10))
                    .jwtID(RandomStringUtils.random(16, true, true))
                    .build();
    
    //        JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128GCM);
            JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM);
            EncryptedJWT jwt = new EncryptedJWT(header, claimsSet);
    
            RSAEncrypter encrypter = new RSAEncrypter(publicKey);
            jwt.encrypt(encrypter);
    
            final String idToken = jwt.serialize();
            assertNotNull(idToken);
    
            //解密
            final EncryptedJWT parseJWT = EncryptedJWT.parse(idToken);
            RSADecrypter decrypter = new RSADecrypter(privateKey);
            parseJWT.decrypt(decrypter);
    
            final JWTClaimsSet jwtClaimsSet = parseJWT.getJWTClaimsSet();
            assertNotNull(jwtClaimsSet);
            assertNotNull(jwtClaimsSet.getAudience());
    
        }
    
    
        /**
         * AES 加密/解密
         * JWE
         *
         * @throws Exception
         */
        @Test
        public void jweAES() throws Exception {
    
            final KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            //位数
    //        keyGenerator.init(128);
            keyGenerator.init(256);
            final SecretKey secretKey = keyGenerator.generateKey();
    
            //加密
    //        JWEHeader jweHeader = new JWEHeader(JWEAlgorithm.DIR, EncryptionMethod.A128GCM);
            JWEHeader jweHeader = new JWEHeader(JWEAlgorithm.DIR, EncryptionMethod.A256GCM);
            Payload payload = new Payload("I am MyOIDC");
    
            JWEObject jweObject = new JWEObject(jweHeader, payload);
            jweObject.encrypt(new DirectEncrypter(secretKey));
    
            final String idToken = jweObject.serialize();
            assertNotNull(idToken);
    
            //解密
            final JWEObject parseJWE = JWEObject.parse(idToken);
            parseJWE.decrypt(new DirectDecrypter(secretKey));
    
            final Payload payload1 = parseJWE.getPayload();
            assertNotNull(payload1);
    
        }
    
    
    }

    代码地址: https://github.com/monkeyk/MyOIDC/blob/1.1.0/myoidc-server/src/test/java/myoidc/server/infrastructure/NimbusJoseJwtTest.java

    点评:

    nimbus-jose-jwt库类定义清晰,简单易用,易理解 , 依赖第三方提供的证书信息(keypair), 对称算法 与非对称算法皆有实现.

    jjwt

    package myoidc.server.infrastructure;
    
    
    import io.jsonwebtoken.*;
    import org.apache.commons.lang3.time.DateUtils;
    import org.junit.Test;
    
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.util.Date;
    
    import static org.junit.Assert.assertNotNull;
    
    /**
     * 2018/5/30
     * <p>
     * <p>
     * Test JJWT  lib
     *
     * @author Shengzhao Li
     */
    public class JJwtTest {
    
    
        @Test
        public void idToken() throws Exception {
    
            // RSA keyPair Generator
            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            /*
             * 长度 至少 1024, 建议 2048
             */
            final int keySize = 2048;
            keyPairGenerator.initialize(keySize);
    
            final KeyPair keyPair = keyPairGenerator.genKeyPair();
    
            final PrivateKey privateKey = keyPair.getPrivate();
    
            // gen id_token
            final Date exp = DateUtils.addMinutes(new Date(), 5);
            final JwtBuilder jwtBuilder = Jwts.builder().setId("jti").setSubject("sub").setExpiration(exp).signWith(SignatureAlgorithm.RS512, privateKey);
            final String idToken = jwtBuilder.compact();
    
    
            assertNotNull(idToken);
            System.out.println(idToken);
    
    
            // verify
    
            final PublicKey publicKey = keyPair.getPublic();
    //        final Jwt jwt = Jwts.parser().parse(idToken);
            final JwtParser parser = Jwts.parser();
            final Jwt jwt = parser.setSigningKey(publicKey).parse(idToken);
    
            assertNotNull(jwt);
            System.out.println(jwt.getHeader());
            System.out.println(jwt.getBody());
    
    
        }
    
    
    }

    代码地址: https://github.com/monkeyk/MyOIDC/blob/1.1.0/myoidc-server/src/test/java/myoidc/server/infrastructure/JJwtTest.java

    点评:

    jjwt小巧够用, 但对JWT的一些细节包装不够, 比如 Claims (只提供获取header,body)

    prime-jwt

    package myoidc.server.infrastructure;
    
    
    import org.junit.Test;
    import org.primeframework.jwt.JWTUtils;
    import org.primeframework.jwt.domain.JWT;
    import org.primeframework.jwt.domain.RSAKeyPair;
    import org.primeframework.jwt.hmac.HMACSigner;
    import org.primeframework.jwt.hmac.HMACVerifier;
    import org.primeframework.jwt.rsa.RSASigner;
    import org.primeframework.jwt.rsa.RSAVerifier;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    
    /**
     * 2018/6/2
     * <p>
     * Testing
     * https://github.com/inversoft/prime-jwt
     *
     * @author Shengzhao Li
     */
    public class PrimeJwtTest {
    
    
        /**
         * Test RSA   非对称算法
         *
         * @throws Exception Exception
         */
        @Test
        public void jwtRSA() throws Exception {
    
    
            // keypair
            final RSAKeyPair rsaKeyPair = JWTUtils.generate2048RSAKeyPair();
    
            System.out.println("PublicKey: " + rsaKeyPair.publicKey);
            System.out.println("PrivateKey: " + rsaKeyPair.privateKey);
    
    
    //        final RSAPublicKey publicKey = RSAUtils.getPublicKeyFromPEM(rsaKeyPair.publicKey);
    //        final RSAPrivateKey privateKey = RSAUtils.getPrivateKeyFromPEM(rsaKeyPair.privateKey);
    
            // generate
            final RSASigner rsaSigner = RSASigner.newSHA256Signer(rsaKeyPair.privateKey);
            final JWT jwt = new JWT().setSubject("subject").setAudience("audi").setUniqueId("uid-id");
            final String idToken = JWT.getEncoder().encode(jwt, rsaSigner);
    
            assertNotNull(idToken);
            System.out.println(idToken);
    
            //verify
            final RSAVerifier rsaVerifier = RSAVerifier.newVerifier(rsaKeyPair.publicKey);
            final JWT decode = JWT.getDecoder().decode(idToken, rsaVerifier);
    
            assertNotNull(decode);
            assertEquals(decode.audience, "audi");
    
        }
    
        /**
         * Test HMAC, 对称算法
         *
         * @throws Exception Exception
         */
        @Test
        public void jwtHMAC() throws Exception {
    
    
            // secret
            final String secret = JWTUtils.generateSHA256HMACSecret();
    
            System.out.println("secret: " + secret);
    
    
            // generate
    
            final JWT jwt = new JWT().setSubject("subject").setAudience("audi").setUniqueId("uid-id");
            final HMACSigner hmacSigner = HMACSigner.newSHA256Signer(secret);
            final String idToken = JWT.getEncoder().encode(jwt, hmacSigner);
    
            assertNotNull(idToken);
            System.out.println(idToken);
    
            //verify
            final HMACVerifier hmacVerifier = HMACVerifier.newVerifier(secret);
            final JWT decode = JWT.getDecoder().decode(idToken, hmacVerifier);
    
            assertNotNull(decode);
            assertEquals(decode.audience, "audi");
    
        }
    
    
    }

    完整测试链接: https://github.com/monkeyk/MyOIDC/blob/1.1.0/myoidc-server/src/test/java/myoidc/server/infrastructure/PrimeJwtTest.java

    点评:

    prime jwt库怎么说呢, 有些地方不符合JAVA语言规范, 支持对称算法(HMAC) 与非对称算法(RSA), 也算容易理解

    vertx-auth-jwt

    package myoidc.server.infrastructure;
    
    
    import io.vertx.core.json.JsonObject;
    import io.vertx.ext.jwt.JWK;
    import io.vertx.ext.jwt.JWT;
    import io.vertx.ext.jwt.JWTOptions;
    import org.junit.Test;
    
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.util.Base64;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    
    /**
     * 2018/6/2
     * <p>
     * Test
     * https://github.com/vert-x3/vertx-auth
     *
     * @author Shengzhao Li
     */
    public class VertxAuthJwtTest {
    
    
        /**
         * Generate/ Verify
         *
         * @throws Exception Exception
         */
        @Test
        public void jwt() throws Exception {
    
    
            // RSA keyPair Generator
            final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            /*
             * 长度 至少 1024, 建议 2048
             */
            final int keySize = 2048;
            keyPairGenerator.initialize(keySize);
    
            final KeyPair keyPair = keyPairGenerator.genKeyPair();
            //公钥
            final PublicKey publicKey = keyPair.getPublic();
            //私钥
            final PrivateKey privateKey = keyPair.getPrivate();
    
    
            final String pemPub = Base64.getEncoder().encodeToString(publicKey.getEncoded());
            final String pemSec = Base64.getEncoder().encodeToString(privateKey.getEncoded());
    
    
            //generate
            final String algorithm = "RS256";
            JWK jwk = new JWK(algorithm, pemPub, pemSec);
            final JWT jwt = new JWT().addJWK(jwk);
    
    
            JsonObject payload = new JsonObject();
            payload.put("appid", "appid");
    
            JWTOptions options = new JWTOptions();
            options.setAlgorithm(algorithm);
            options.setSubject("subject");
    
            String idToken = jwt.sign(payload, options);
    
            assertNotNull(idToken);
            System.out.println(idToken);
    
    
            //verify
            JWK jwk2 = new JWK(algorithm, pemPub, pemSec);
            final JWT jwtVerify = new JWT().addJWK(jwk2);
    
            final JsonObject decode = jwtVerify.decode(idToken);
            assertNotNull(decode);
            assertEquals(decode.getString("appid"), "appid");
    
        }
    
    
    }

    代码地址: https://github.com/monkeyk/MyOIDC/blob/1.1.0/myoidc-server/src/test/java/myoidc/server/infrastructure/VertxAuthJwtTest.java

     点评:

    Vertx Auth Jwt 库算是最不容易理解的一个库了.花了不少时间才弄通这一示例. 不容易上手. 并且生成与校验id_token 时都需要公钥与私钥,不足.

    ———————————————————

    以下是在使用中的一些总结或注意点

    1. 几乎所有库都要求JAVA版本1.7或更高版本, 1.6或以下的版本需要二次开发(或不支持)

    2.从易用性, 扩展性, 完整性等来看, 使用首先推荐 jose4j, 其次是 Nimbus-jose-jwt.

    3. JWT是实现OIDC的基石,掌握其使用对实现OIDC有很大帮助(同时对JAVA证书使用, PKI体系的掌握也有要求)

  • 相关阅读:
    WCF ria services完美登陆功能(10)
    利用DYCOM快速建立wcf服务器端
    DYCom简要介绍
    断剑
    生命的价值
    飞翔的蜘蛛
    JSP中如何获取select标签选中的值
    wrapClass
    iconfont 在vue项目中的应用(iconcomponent组件)
    正则表达式
  • 原文地址:https://www.cnblogs.com/hlkawa/p/13675792.html
Copyright © 2011-2022 走看看