import java.security.PrivateKey; import java.security.PublicKey; import java.util.UUID; //import org.jose4j.base64url.Base64; import org.jose4j.json.JsonUtil; import org.jose4j.jwa.AlgorithmConstraints; import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.RsaJsonWebKey; import org.jose4j.jwk.RsaJwkGenerator; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.MalformedClaimException; import org.jose4j.jwt.NumericDate; import org.jose4j.jwt.consumer.ErrorCodes; import org.jose4j.jwt.consumer.InvalidJwtException; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.lang.JoseException; public class OpenIDConnectMain { public static void main(String[] args) throws JoseException, MalformedClaimException { //Generate keyId, use 32-bit uuid. String keyId = UUID.randomUUID().toString().replaceAll("-", ""); System.out.println("keyId=" + keyId); //generate public and private key pair RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk (2048); jwk.setKeyId (keyId); jwk.setAlgorithm(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256); // public key String publicKeyText = jwk.toJson (JsonWebKey.OutputControlLevel.PUBLIC_ONLY); System.out.println("publicKeyText====="+publicKeyText); //private key String privateKeyText = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE); System.out.println("privateKeyText====="+privateKeyText); //Signed by private key, generate token String idToken = createIdToken(keyId, privateKeyText); verifyIdToken(publicKeyText, idToken); } //Use the public key to verify the token signature and parse the content of the token. private static void verifyIdToken(String publicKeyText, String idToken) throws JoseException, MalformedClaimException { //Parse the header information (this operation does not require a public key) JsonWebSignature jwo = (JsonWebSignature) JsonWebSignature.fromCompactSerialization(idToken); String alg = jwo.getHeader("alg"); //The API gateway obtains the publicKey according to the kid (when registering the authentication API, the keyId and publicKey are configured) String kid = jwo.getHeader("kid"); PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(publicKeyText)).getPublicKey(); JwtConsumer jwtConsumer = new JwtConsumerBuilder().setRequireExpirationTime() // the .setAllowedClockSkewInSeconds(30) // allow some leeway in .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 .setVerificationKey(publicKey) // verify the signature with the public key .setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context new AlgorithmConstraints(ConstraintType.WHITELIST, alg)) .build(); // create the JwtConsumer instance try { // Validate the JWT and process it to the Claims JwtClaims jwtClaims = jwtConsumer.processToClaims(idToken); System.out.println("JWT validation succeeded! " + jwtClaims); } catch (InvalidJwtException e) { // InvalidJwtException will be thrown, if the JWT failed processing // or validation in anyway. // Hopefully with meaningful explanations(s) about what went wrong. System.out.println("Invalid JWT! " + e); // Programmatic access to (some) specific reasons for JWT invalidity // is also possible // should you want different error handling behavior for certain // conditions. // Whether or not the JWT has expired being one common reason for // invalidity if (e.hasExpired()) { System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime()); } // Or maybe the audience was invalid if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) { System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience()); } } } // https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples private static String createIdToken(String keyId, String privateKeyText) throws JoseException { // claims JwtClaims claims = new JwtClaims(); claims.setGeneratedJwtId(); claims.setIssuedAtToNow (); // expire time NumericDate date = NumericDate.now(); date.addSeconds(120000);//1.3 days claims.setExpirationTime(date); claims.setNotBeforeMinutesInThePast(1); claims.setIssuer("Issuer"); // who creates the token and signs it claims.setSubject("Subject"); claims.setAudience("Audience"); // add custom parameters claims.setClaim("UserKey1", "UserVal1"); // jws JsonWebSignature jws = new JsonWebSignature(); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); jws.setKeyIdHeaderValue(keyId); jws.setPayload(claims.toJson()); PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyText)).getPrivateKey(); jws.setKey(privateKey); System.out.println("createIdToken::jws=" + jws); // idToken String idToken = jws.getCompactSerialization(); System.out.println("createIdToken::idToken=" + idToken); return idToken; } }
The jose.4.j library is an open source (Apache 2.0) implementation of JWT
id_token, also called ID Token, is a token defined in the OIDC protocol.
id_token generation requires KeyPair, keyId and Claims.
The main extension of OIDC to OAuth2 is to provide ID Token.
ID Token is a security token, which is a data structure in JWT format provided by an authorization server that contains user information ( consisting of a set of Cliams and other auxiliary Cliams ).
The main components of ID Token are as follows (OIDC using OAuth2 flow).
iss = Issuer Identifier: Required . The unique identifier of the person who provided the authentication information. Usually a https url (excluding querystring and fragment parts).
sub = Subject Identifier: Required . The EU logo provided by iss is unique within the scope of iss. It will be used by the RP to identify a unique user. Up to 255 ASCII characters.
aud = Audience(s): Required . Identify the audience of the ID Token. Must contain OAuth2 client_id.
exp = Expiration time: Required . Expiration time, the ID Token that exceeds this time will be invalid and will no longer be verified.
iat = Issued At Time: Required . The time the JWT was constructed.
auth_time = AuthenticationTime: The time when the EU completed the authentication. If the RP sends the AuthN request with the max_age parameter, this claim is required.
nonce: The random string provided by the RP when sending the request to slow down replay attacks, and it can also be used to associate the ID Token with the session information of the RP itself.
acr = Authentication Context Class Reference: Optional. Represents an authentication context reference value that can be used to identify the authentication context class.
amr = Authentication Methods References: Optional. Represents a set of authentication methods.
azp = Authorized party: Optional. Use in conjunction with aud. This value is only used when the authenticated party and the audience (aud) do not agree, and are rarely used in general.