org.wso2.carbon.identity.authenticator.x509Certificate.X509CertificateUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.identity.authenticator.x509Certificate.X509CertificateUtil.java

Source

/*
 *  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */

package org.wso2.carbon.identity.authenticator.x509Certificate;

import org.apache.axiom.om.util.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.identity.application.authentication.framework.config.builder.FileBasedConfigurationBuilder;
import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig;
import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext;
import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.x509Certificate.validation.CertificateValidationException;
import org.wso2.carbon.identity.x509Certificate.validation.service.RevocationValidationManager;
import org.wso2.carbon.identity.x509Certificate.validation.service.RevocationValidationManagerImpl;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import java.io.ByteArrayInputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Working with certificate and claims store.
 */
public class X509CertificateUtil {
    private static Log log = LogFactory.getLog(X509CertificateUtil.class);

    /**
     * Get certificate from claims.
     *
     * @param username name of the user
     * @return x509 certificate
     * @throws AuthenticationFailedException authentication failed exception
     */
    public static X509Certificate getCertificate(String username) throws AuthenticationFailedException {
        X509Certificate x509Certificate;
        UserRealm userRealm = getUserRealm(username);
        try {
            String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username);
            String claimURI = getClaimUri();
            if (userRealm != null) {
                Map<String, String> userClaimValues = userRealm.getUserStoreManager()
                        .getUserClaimValues(tenantAwareUsername, new String[] { claimURI }, null);
                String userCertificate = userClaimValues.get(claimURI);
                if (log.isDebugEnabled()) {
                    log.debug("The user certificate is " + userCertificate);
                }
                if (StringUtils.isNotEmpty(userCertificate)) {
                    CertificateFactory cf = CertificateFactory.getInstance("X509");
                    x509Certificate = (X509Certificate) cf
                            .generateCertificate(new ByteArrayInputStream(Base64.decode(userCertificate)));
                } else {
                    return null;
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("UserRealm is null for username: " + username);
                }
                throw new AuthenticationFailedException("Cannot find the user realm for the given tenant domain : "
                        + CarbonContext.getThreadLocalCarbonContext().getTenantDomain());
            }
        } catch (CertificateException e) {
            throw new AuthenticationFailedException("Error while decoding the certificate ", e);
        } catch (UserStoreException e) {
            throw new AuthenticationFailedException("Error while retrieving the user store manager ", e);
        }
        return x509Certificate;
    }

    /**
     * Add certificate into claims.
     *
     * @param username        name of the user
     * @param x509Certificate x509 certificate
     * @return boolean status of the action
     * @throws AuthenticationFailedException authentication failed exception
     */
    public static boolean addCertificate(String username, X509Certificate x509Certificate)
            throws AuthenticationFailedException {
        Map<String, String> claims = new HashMap<>();
        UserRealm userRealm = getUserRealm(username);
        try {
            if (userRealm != null) {
                claims.put(getClaimUri(), Base64.encode(x509Certificate.getEncoded()));
                String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username);
                userRealm.getUserStoreManager().setUserClaimValues(tenantAwareUsername, claims,
                        X509CertificateConstants.DEFAULT);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("UserRealm is null for username: " + username);
                }
                throw new AuthenticationFailedException("Cannot find the user realm for the given tenant domain : "
                        + CarbonContext.getThreadLocalCarbonContext().getTenantDomain());
            }
        } catch (CertificateException e) {
            throw new AuthenticationFailedException("Error while retrieving certificate of user: " + username, e);
        } catch (UserStoreException e) {
            throw new AuthenticationFailedException("Error while retrieving the user store manager ", e);
        }
        if (log.isDebugEnabled()) {
            log.debug("X509 certificate is added for user: " + username);
        }
        return true;
    }

    /**
     * Validate the user certificate
     *
     * @param userName         name of the user
     * @param certificateBytes x509 certificate
     * @return boolean status of the action
     * @throws AuthenticationFailedException
     */
    public static boolean validateCertificate(String userName, AuthenticationContext authenticationContext,
            byte[] certificateBytes, boolean isSelfRegistrationEnable) throws AuthenticationFailedException {
        X509Certificate x509Certificate;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            x509Certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateBytes));

            if (isSelfRegistrationEnable && isCertificateExist(userName)
                    && !isUserCertificateValid(userName, x509Certificate)) {
                return false;
            } else if (!isSelfRegistrationEnable && !isUserExists(userName, authenticationContext)) {
                return false;
            }

            if (isCertificateRevoked(x509Certificate)) {
                if (log.isDebugEnabled()) {
                    log.debug("X509 certificate with serial num: " + x509Certificate.getSerialNumber()
                            + " is revoked");
                }
                if (isSelfRegistrationEnable) {
                    deleteUserCertificate(userName, x509Certificate);
                }
                return false;
            } else if (isSelfRegistrationEnable && !isCertificateExist(userName)) {
                addUserCertificate(userName, x509Certificate);
            }
        } catch (CertificateException e) {
            throw new AuthenticationFailedException("Error while retrieving certificate ", e);
        } catch (CertificateValidationException e) {
            throw new AuthenticationFailedException("Error while validating client certificate with serial num: ",
                    e);
        } catch (UserStoreException e) {
            throw new AuthenticationFailedException("Cannot find the user realm for the username: " + userName, e);
        }
        return true;
    }

    /**
     * Check availability of certificate.
     *
     * @param userName name of the user
     * @return boolean status of availability
     */
    public static boolean isCertificateExist(String userName) throws AuthenticationFailedException {
        return getCertificate(userName) != null;
    }

    /**
     * Get parameter values from local file.
     */
    public static Map<String, String> getX509Parameters() {
        AuthenticatorConfig authConfig = FileBasedConfigurationBuilder.getInstance()
                .getAuthenticatorBean(X509CertificateConstants.AUTHENTICATOR_NAME);
        if (authConfig != null) {
            return authConfig.getParameterMap();
        }
        if (log.isDebugEnabled()) {
            log.debug("AuthenticatorConfig is not provided for " + X509CertificateConstants.AUTHENTICATOR_NAME);
        }
        return Collections.emptyMap();
    }

    /**
     * Get X509Certificate claimURI value.
     *
     * @return X509Certificate claimURI
     */
    public static String getClaimUri() {
        String claimURI = X509CertificateConstants.CLAIM_DIALECT_URI;
        Map<String, String> parametersMap = getX509Parameters();
        if (parametersMap != null) {
            Object claimURIObj = parametersMap.get(X509CertificateConstants.CLAIM_URI);
            if (claimURIObj != null) {
                claimURI = String.valueOf(claimURIObj);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("The X509Certificate claimUri is " + claimURI);
        }
        return claimURI;
    }

    /**
     * Get the user realm of the logged in user.
     *
     * @param username the username
     * @return the userRealm for given username
     * @throws AuthenticationFailedException
     */
    public static UserRealm getUserRealm(String username) throws AuthenticationFailedException {
        UserRealm userRealm = null;
        if (log.isDebugEnabled()) {
            log.debug("Getting userRealm for user: " + username);
        }
        try {
            if (StringUtils.isNotEmpty(username)) {
                String tenantDomain = MultitenantUtils.getTenantDomain(username);
                int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
                RealmService realmService = X509CertificateRealmServiceComponent.getRealmService();
                userRealm = realmService.getTenantUserRealm(tenantId);
            }
        } catch (UserStoreException e) {
            throw new AuthenticationFailedException("Cannot find the user realm for the username: " + username, e);
        }
        return userRealm;
    }

    /**
     * Check the revocation status of the certificate
     *
     * @param x509Certificate x509 certificate
     * @return true if the certificate is revoked
     * @throws CertificateValidationException
     */
    private static boolean isCertificateRevoked(X509Certificate x509Certificate)
            throws CertificateValidationException {

        RevocationValidationManager revocationValidationManager = new RevocationValidationManagerImpl();
        return revocationValidationManager.verifyRevocationStatus(x509Certificate);
    }

    private static void deleteUserCertificate(String userName, X509Certificate x509Certificate)
            throws AuthenticationFailedException {

        if (isCertificateExist(userName) && isUserCertificateValid(userName, x509Certificate)) {
            if (log.isDebugEnabled()) {
                log.debug("Provided X509 client certificate with serial num: " + x509Certificate.getSerialNumber()
                        + " has been revoked. Removing the x509Certificate claim of the user: " + userName);
            }
            deleteCertificate(userName);
        }
    }

    private static void deleteCertificate(String username) throws AuthenticationFailedException {

        String[] claims = new String[1];
        UserRealm userRealm = getUserRealm(username);
        try {
            if (userRealm != null) {
                claims[0] = getClaimUri();
                String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(username);
                userRealm.getUserStoreManager().deleteUserClaimValues(tenantAwareUsername, claims,
                        X509CertificateConstants.DEFAULT);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("UserRealm is null for username: " + username);
                }
                throw new AuthenticationFailedException("Cannot find the user realm for the given tenant domain : "
                        + CarbonContext.getThreadLocalCarbonContext().getTenantDomain());
            }
        } catch (UserStoreException e) {
            throw new AuthenticationFailedException("Error while deleting certificate of user: " + username, e);
        }
        if (log.isDebugEnabled()) {
            log.debug("X509 certificate is deleted for user: " + username);
        }
    }

    private static void addUserCertificate(String userName, X509Certificate x509Certificate)
            throws AuthenticationFailedException {

        if (log.isDebugEnabled()) {
            log.debug("X509 Certificate with serial num: " + x509Certificate.getSerialNumber()
                    + " does not exit for user: " + userName);
        }
        X509CertificateUtil.addCertificate(userName, x509Certificate);
        if (log.isDebugEnabled()) {
            log.debug("Adding the X509 certificate with serial num: " + x509Certificate.getSerialNumber()
                    + " as a user claim.");
        }
    }

    private static boolean isUserCertificateValid(String userName, X509Certificate x509Certificate)
            throws AuthenticationFailedException {

        X509Certificate certInUserClaim = getCertificate(userName);
        if (log.isDebugEnabled()) {
            log.debug("X509 certificate with serial num: " + x509Certificate.getSerialNumber()
                    + " is getting matched with the user certificate with serial num : "
                    + certInUserClaim.getSerialNumber() + " in the user claim of user: " + userName);
        }
        return x509Certificate.equals(certInUserClaim);
    }

    private static boolean isUserExists(String userName, AuthenticationContext authenticationContext)
            throws UserStoreException, AuthenticationFailedException {

        boolean isUserExist = X509CertificateUtil.getUserRealm(userName).getUserStoreManager()
                .isExistingUser(MultitenantUtils.getTenantAwareUsername(userName));
        if (isUserExist) {
            if (log.isDebugEnabled()) {
                log.debug("User exists with the user name: " + userName);
            }
            return true;
        } else {
            authenticationContext.setProperty(X509CertificateConstants.X509_CERTIFICATE_ERROR_CODE,
                    X509CertificateConstants.USER_NOT_FOUND);
            throw new AuthenticationFailedException(" Unable to find X509 Certificate's user in user store. ");
        }
    }

}