org.cesecore.authentication.tokens.X509CertificateAuthenticationToken.java Source code

Java tutorial

Introduction

Here is the source code for org.cesecore.authentication.tokens.X509CertificateAuthenticationToken.java

Source

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.cesecore.authentication.tokens;

import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.util.Set;
import java.util.regex.Pattern;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.cesecore.authorization.user.AccessUserAspect;
import org.cesecore.authorization.user.matchvalues.AccessMatchValue;
import org.cesecore.authorization.user.matchvalues.AccessMatchValueReverseLookupRegistry;
import org.cesecore.authorization.user.matchvalues.X500PrincipalAccessMatchValue;
import org.cesecore.certificates.util.DNFieldExtractor;
import org.cesecore.util.CertTools;

/**
 * This is an implementation of the AuthenticationToken concept, based on using an {@link X509Certificate} as it's single credential, and that
 * certificate's {@link X500Principal} as its principle, but as the X500Principle is contained in the X509Certificate, this remains little more than a
 * formality. This AuthenticationToken is the default used in EJBCA.
 * 
 * The implementation of the <code>matches(...)</code> method is based on <code>AdminEntity.java 10832 2010-12-13 13:54:25Z anatom</code> from EJBCA.
 * 
 * 
 * @version $Id: X509CertificateAuthenticationToken.java 17625 2013-09-20 07:12:06Z netmackan $
 * 
 */
public class X509CertificateAuthenticationToken extends LocalJvmOnlyAuthenticationToken {

    public static final String TOKEN_TYPE = "CertificateAuthenticationToken";

    private static final Logger log = Logger.getLogger(X509CertificateAuthenticationToken.class);

    private static final long serialVersionUID = 1097165653913865515L;

    private static final Pattern serialPattern = Pattern.compile("\\bSERIALNUMBER=", Pattern.CASE_INSENSITIVE);

    private final X509Certificate certificate;

    private final int adminCaId;
    private final DNFieldExtractor dnExtractor;
    private final DNFieldExtractor anExtractor;

    /**
     * Standard constructor for X509CertificateAuthenticationToken
     * 
     * @param principals
     *            A set of X500Principals. Should contain one and only one value.
     * @param credentials
     *            A set of X509Certificates. As with the principals, this set should contain one and only one value, anything else will result in a
     *            {@link InvalidAuthenticationTokenException} being thrown.
     */
    public X509CertificateAuthenticationToken(Set<X500Principal> principals, Set<X509Certificate> credentials) {
        super(principals, credentials);

        /*
         * In order to save having to verify the credentials set every time the <code>matches(...)</code> method is called, it's checked here, and the
         * resulting credential is stored locally.
         */
        X509Certificate[] certificateArray = getCredentials().toArray(new X509Certificate[0]);
        if (certificateArray.length != 1) {
            throw new InvalidAuthenticationTokenException("X509CertificateAuthenticationToken was containing "
                    + certificateArray.length + " credentials instead of 1.");
        } else {
            certificate = certificateArray[0];
        }

        String certstring = CertTools.getSubjectDN(certificate).toString();
        adminCaId = CertTools.getIssuerDN(certificate).hashCode();
        certstring = serialPattern.matcher(certstring).replaceAll("SN=");
        String altNameString = CertTools.getSubjectAlternativeName(certificate);

        dnExtractor = new DNFieldExtractor(certstring, DNFieldExtractor.TYPE_SUBJECTDN);
        anExtractor = new DNFieldExtractor(altNameString, DNFieldExtractor.TYPE_SUBJECTALTNAME);
    }

    /**
     * This implementation presumes that a lone {@link X509Certificate} has been submitted as a credential (which should have been verified by the
     * constructor), and will use that value to match this authentication token to the AccessUserData entity submitted.
     * 
     * FIXME: This class is a candidate for optimization. 
     * FIXME: Attempt to remove as many static calls as possible.
     * 
     */
    @Override
    public boolean matches(AccessUserAspect accessUser) {
        // Protect against spoofing by checking if this token was created locally
        if (!super.isCreatedInThisJvm()) {
            return false;
        }
        boolean returnvalue = false;

        int parameter;
        int size = 0;
        String[] clientstrings = null;
        if (StringUtils.equals(TOKEN_TYPE, accessUser.getTokenType())) {
            // First check that issuers match.
            if (accessUser.getCaId() == adminCaId) {
                // Determine part of certificate to match with.
                DNFieldExtractor usedExtractor = dnExtractor;
                X500PrincipalAccessMatchValue matchValue = (X500PrincipalAccessMatchValue) getMatchValueFromDatabaseValue(
                        accessUser.getMatchWith());
                if (matchValue == X500PrincipalAccessMatchValue.WITH_SERIALNUMBER) {
                    try {
                        BigInteger matchValueAsBigInteger = new BigInteger(accessUser.getMatchValue(), 16);
                        switch (accessUser.getMatchTypeAsType()) {
                        case TYPE_EQUALCASE:
                        case TYPE_EQUALCASEINS:
                            returnvalue = matchValueAsBigInteger.equals(certificate.getSerialNumber());
                            break;
                        case TYPE_NOT_EQUALCASE:
                        case TYPE_NOT_EQUALCASEINS:
                            returnvalue = !matchValueAsBigInteger.equals(certificate.getSerialNumber());
                            break;
                        default:
                        }
                    } catch (NumberFormatException nfe) {
                        log.info("Invalid matchValue for accessUser when expecting a hex serialNumber: "
                                + accessUser.getMatchValue());
                    }
                } else if (matchValue == X500PrincipalAccessMatchValue.WITH_FULLDN) {
                    String value = accessUser.getMatchValue();
                    switch (accessUser.getMatchTypeAsType()) {
                    case TYPE_EQUALCASE:
                        returnvalue = value.equals(CertTools.getSubjectDN(certificate));
                    case TYPE_EQUALCASEINS:
                        returnvalue = value.equalsIgnoreCase(CertTools.getSubjectDN(certificate));
                        break;
                    case TYPE_NOT_EQUALCASE:
                        returnvalue = !value.equals(CertTools.getSubjectDN(certificate));
                    case TYPE_NOT_EQUALCASEINS:
                        returnvalue = !value.equalsIgnoreCase(CertTools.getSubjectDN(certificate));
                        break;
                    default:
                    }
                } else {
                    parameter = DNFieldExtractor.CN;
                    switch (matchValue) {
                    case WITH_COUNTRY:
                        parameter = DNFieldExtractor.C;
                        break;
                    case WITH_DOMAINCOMPONENT:
                        parameter = DNFieldExtractor.DC;
                        break;
                    case WITH_STATEORPROVINCE:
                        parameter = DNFieldExtractor.ST;
                        break;
                    case WITH_LOCALITY:
                        parameter = DNFieldExtractor.L;
                        break;
                    case WITH_ORGANIZATION:
                        parameter = DNFieldExtractor.O;
                        break;
                    case WITH_ORGANIZATIONALUNIT:
                        parameter = DNFieldExtractor.OU;
                        break;
                    case WITH_TITLE:
                        parameter = DNFieldExtractor.T;
                        break;
                    case WITH_DNSERIALNUMBER:
                        parameter = DNFieldExtractor.SN;
                        break;
                    case WITH_COMMONNAME:
                        parameter = DNFieldExtractor.CN;
                        break;
                    case WITH_UID:
                        parameter = DNFieldExtractor.UID;
                        break;
                    case WITH_DNEMAILADDRESS:
                        parameter = DNFieldExtractor.E;
                        break;
                    case WITH_RFC822NAME:
                        parameter = DNFieldExtractor.RFC822NAME;
                        usedExtractor = anExtractor;
                        break;
                    case WITH_UPN:
                        parameter = DNFieldExtractor.UPN;
                        usedExtractor = anExtractor;
                        break;
                    default:
                    }
                    size = usedExtractor.getNumberOfFields(parameter);
                    clientstrings = new String[size];
                    for (int i = 0; i < size; i++) {
                        clientstrings[i] = usedExtractor.getField(parameter, i);
                    }

                    // Determine how to match.
                    if (clientstrings != null) {
                        switch (accessUser.getMatchTypeAsType()) {
                        case TYPE_EQUALCASE:
                            for (int i = 0; i < size; i++) {
                                returnvalue = clientstrings[i].equals(accessUser.getMatchValue());
                                if (returnvalue) {
                                    break;
                                }
                            }
                            break;
                        case TYPE_EQUALCASEINS:
                            for (int i = 0; i < size; i++) {
                                returnvalue = clientstrings[i].equalsIgnoreCase(accessUser.getMatchValue());
                                if (returnvalue) {
                                    break;
                                }
                            }
                            break;
                        case TYPE_NOT_EQUALCASE:
                            for (int i = 0; i < size; i++) {
                                returnvalue = !clientstrings[i].equals(accessUser.getMatchValue());
                                if (returnvalue) {
                                    break;
                                }
                            }
                            break;
                        case TYPE_NOT_EQUALCASEINS:
                            for (int i = 0; i < size; i++) {
                                returnvalue = !clientstrings[i].equalsIgnoreCase(accessUser.getMatchValue());
                                if (returnvalue) {
                                    break;
                                }
                            }
                            break;
                        default:
                        }
                    }
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace(
                            "Caid does not match. Required=" + adminCaId + ", actual was " + accessUser.getCaId());
                }
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Token type does not match. Required=" + TOKEN_TYPE + ", actual was "
                        + accessUser.getTokenType());
            }
        }

        return returnvalue;
    }

    /** Returns user information of the user this authentication token belongs to. */
    @Override
    public String toString() {
        return CertTools.getSubjectDN(certificate);
    }

    @Override
    public int hashCode() {
        final int prime = 4711;
        int result = 1;
        result = prime * result + ((certificate == null) ? 0 : certificate.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        X509CertificateAuthenticationToken other = (X509CertificateAuthenticationToken) obj;
        if (certificate == null) {
            if (other.certificate != null) {
                return false;
            }
        } else if (!certificate.equals(other.certificate)) {
            return false;
        }
        return true;
    }

    /**
     * @return the certificate
     */
    public X509Certificate getCertificate() {
        return certificate;
    }

    @Override
    public boolean matchTokenType(String tokenType) {
        return tokenType.equals(TOKEN_TYPE);
    }

    @Override
    public AccessMatchValue getMatchValueFromDatabaseValue(Integer databaseValue) {
        return AccessMatchValueReverseLookupRegistry.INSTANCE.performReverseLookup(TOKEN_TYPE,
                databaseValue.intValue());
    }

    @Override
    public AccessMatchValue getDefaultMatchValue() {
        return X500PrincipalAccessMatchValue.NONE;
    }
}