001/*
002* The contents of this file are subject to the terms of the Common Development and
003* Distribution License (the License). You may not use this file except in compliance with the
004* License.
005*
006* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007* specific language governing permission and limitations under the License.
008*
009* When distributing Covered Software, include this CDDL Header Notice in each file and include
010* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011* Header, with the fields enclosed by brackets [] replaced by your own identifying
012* information: "Portions copyright [year] [name of copyright owner]".
013*
014* Copyright 2014-2016 ForgeRock AS.
015*/
016package org.forgerock.jaspi.modules.openid.resolvers;
017
018import java.security.Key;
019import java.security.interfaces.ECPublicKey;
020import java.security.interfaces.RSAPublicKey;
021import java.util.Date;
022import javax.crypto.SecretKey;
023
024import org.forgerock.jaspi.modules.openid.exceptions.InvalidIssException;
025import org.forgerock.jaspi.modules.openid.exceptions.JwtExpiredException;
026import org.forgerock.jaspi.modules.openid.exceptions.OpenIdConnectVerificationException;
027import org.forgerock.json.jose.jws.SignedJwt;
028import org.forgerock.json.jose.jws.SigningManager;
029import org.forgerock.json.jose.jws.handlers.SigningHandler;
030
031/**
032 * Implementation of the OpenIdResolver interface. Comments in the specific verify methods
033 * are taken directly from OpenID Connect Basic Client Implementer's Guide 1.0,
034 * section 2.2.1 - ID Token Validation
035 *
036 * Currently we do NO validation against the client ID/intended audience.
037 *
038 * @see <a href="http://openid.net/specs/openid-connect-basic-1_0.html">
039 *     http://openid.net/specs/openid-connect-basic-1_0.html</a>
040 */
041public abstract class BaseOpenIdResolver implements OpenIdResolver {
042
043    private final String issuer;
044
045    /**
046     * Abstract constructor for setting the issuer's identity.
047     *
048     * @param issuer The issuer (provider) of the Open Id Connect id token
049     */
050    public BaseOpenIdResolver(final String issuer) {
051        this.issuer = issuer;
052    }
053
054    /**
055     * Verifies the issuer is exactly who it is expected to be.
056     *
057     * @param issuerName The name of the claimed issuer
058     * @throws InvalidIssException if the expected issuer and actual issuer do not match
059     */
060    void verifyIssuer(final String issuerName) throws InvalidIssException {
061        //The issuer MUST exactly match the value of the iss (issuer) Claim.
062        if (!issuer.equals(issuerName)) {
063            throw new InvalidIssException("Invalid issuer");
064        }
065    }
066
067    /**
068     * Verifies that the current date is no later than the expiry date on the JWT.
069     *
070     * @param expirationTime time at which this id token expires
071     * @throws JwtExpiredException if the current time is after the expired time
072     */
073    void verifyExpiration(final Date expirationTime) throws JwtExpiredException {
074        //Expiration time on or after which the ID Token MUST NOT be accepted for processing.
075        if (new Date().after(expirationTime)) {
076            throw new JwtExpiredException("Token expired");
077        }
078    }
079
080    /**
081     * {@inheritDoc}
082     */
083    @Override
084    public void validateIdentity(final SignedJwt idClaim) throws OpenIdConnectVerificationException {
085
086        if (idClaim == null) {
087            throw new OpenIdConnectVerificationException("A valid SignedJWT must be supplied to the resolver");
088        }
089
090        verifyIssuer(idClaim.getClaimsSet().getIssuer());
091        verifyExpiration(idClaim.getClaimsSet().getExpirationTime());
092    }
093
094    /**
095     * Determine an appropriate signing handler to use for verifying signatures using the given verification key.
096     *
097     * @param signingManager the signing manager.
098     * @param key the verification key.
099     * @return the appropriate signing handler.
100     * @throws IllegalArgumentException if no handler can be determined for the given key.
101     */
102    protected SigningHandler createSigningHandlerForKey(final SigningManager signingManager, final Key key) {
103        if (key instanceof ECPublicKey) {
104            return signingManager.newEcdsaVerificationHandler(((ECPublicKey) key));
105        } else if (key instanceof RSAPublicKey) {
106            return signingManager.newRsaSigningHandler(key);
107        } else if (key instanceof SecretKey) {
108            return signingManager.newHmacSigningHandler(key.getEncoded());
109        } else {
110            throw new IllegalArgumentException("Unable to determine signing algorithm");
111        }
112    }
113
114    /**
115     * {@inheritDoc}
116     */
117    @Override
118    public String getIssuer() {
119        return issuer;
120    }
121}