org.picketlink.pki.internal.DefaultCertificateAuthority.java Source code

Java tutorial

Introduction

Here is the source code for org.picketlink.pki.internal.DefaultCertificateAuthority.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.picketlink.pki.internal;

import static org.picketlink.common.util.Base64.encodeBytes;
import static org.picketlink.pki.internal.DefaultKeyAuthority.generateKeyPair;
import static org.picketlink.pki.internal.util.X509Util.createSigner;
import static org.picketlink.pki.internal.util.X509Util.generateV1Certificate;

import java.io.IOException;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.cert.X509CRLEntryHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509v2CRLBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.picketlink.common.util.Base64;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.event.EventBridge;
import org.picketlink.idm.model.IdentityType;
import org.picketlink.idm.model.Partition;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User;
import org.picketlink.idm.query.Condition;
import org.picketlink.idm.query.IdentityQuery;
import org.picketlink.idm.query.IdentityQueryBuilder;
import org.picketlink.pki.cert.CertificateAuthority;
import org.picketlink.pki.cert.CertificateAuthorityConfig;
import org.picketlink.pki.cert.CertificateRequest;
import org.picketlink.pki.event.CertificateCreateEvent;
import org.picketlink.pki.internal.util.X509Util;
import org.picketlink.pki.key.KeyAuthority;
import org.picketlink.pki.model.CertificateRevocationListType;
import org.picketlink.pki.model.CertificateType;

/**
 * @author Pedro Igor
 * @author Giriraj Sharma
 * @since December 2, 2014
 */
@ApplicationScoped
public class DefaultCertificateAuthority extends AbstractAuthority implements CertificateAuthority {

    private static final long serialVersionUID = 1L;

    @Inject
    private KeyAuthority keyAuthority;

    @Override
    public String getName() {
        return getCertificate().getObject().getSubjectDN().getName();
    }

    @Override
    public KeyPair getKeyPair() {
        return getPartitionKeyPair();
    }

    @Override
    public CertificateType getCertificate() {
        return getCertificate(getPartition().getId());
    }

    @Override
    public KeyAuthority getKeyAuthority() {
        return this.keyAuthority;
    }

    @Override
    public CertificateType issue(IdentityType identityType) {
        CertificateType certificate;

        try {
            IdentityManager identityManager = getIdentityManager();
            IdentityType storedIdentityType = identityManager.lookupIdentityById(IdentityType.class,
                    identityType.getId());
            KeyPair userKeyPair = this.keyAuthority.getKeyPair(storedIdentityType);
            String commonName = null;

            if (User.class.isInstance(identityType)) {
                commonName = ((User) identityType).getLoginName();
            }

            X500Name subjectDN = new X500Name("CN=" + commonName + "," + getConfiguration().getBaseDN());
            X509Certificate x509Certificate = X509Util.generateV3Certificate(getCertificate().getObject(),
                    getPartitionKeyPair(), subjectDN, userKeyPair, getConfiguration());
            certificate = new CertificateType(storedIdentityType, x509Certificate, getConfiguration());

            identityManager.add(certificate);
        } catch (Exception e) {
            throw new RuntimeException("Could not issue certificate.", e);
        }

        return certificate;
    }

    @Override
    public CertificateType issue(CertificateRequest request) {
        try {
            IdentityManager identityManager = getIdentityManager();
            JcaPKCS10CertificationRequest certificationRequest = new JcaPKCS10CertificationRequest(
                    request.getMessage());
            User user = BasicModel.getUser(identityManager, request.getSubjectName());
            KeyPair userKeyPair = this.keyAuthority.getKeyPair(user);
            ContentVerifierProvider contentVerifierProvider = new JcaContentVerifierProviderBuilder()
                    .setProvider("BC").build(userKeyPair.getPublic());

            if (certificationRequest.isSignatureValid(contentVerifierProvider)) {
                return issue(user);
            }

            throw new RuntimeException("Invalid CSR.");
        } catch (Exception e) {
            throw new RuntimeException("Could not issue certificate.", e);
        }
    }

    @Override
    public CertificateType retrieve(String distinguishedName) {
        IdentityManager identityManager = getIdentityManager();

        IdentityQueryBuilder builder = identityManager.getQueryBuilder();
        Condition condition = builder.equal(CertificateType.DISTINGUISHED_NAME, distinguishedName);
        IdentityQuery<CertificateType> query = builder.createIdentityQuery(CertificateType.class).where(condition);

        List<CertificateType> result = query.getResultList();

        if (result.isEmpty()) {
            return null;
        } else if (result.size() > 1) {
            throw new IllegalStateException(
                    "Multiple certificates found for the given DN [" + distinguishedName + "].");
        }

        CertificateType certificate = result.get(0);

        if (validate(certificate)) {
            return certificate;
        }

        return null;
    }

    @Override
    public boolean validate(CertificateType certificate) {
        try {
            X509Certificate x509Certificate = certificate.getObject();

            x509Certificate.checkValidity();
            x509Certificate.verify(getPartitionKeyPair().getPublic());

            return !isRevoked(certificate);
        } catch (Exception e) {
        }

        return false;
    }

    @Override
    public void revoke(CertificateType certificate) {
        try {
            X509CRLHolder crlHolder = getCRLHolder();
            Date date = new Date();
            X509v2CRLBuilder builder = new X509v2CRLBuilder(crlHolder.getIssuer(), date); // Create
            Date nextUpdate = new Date(date.getTime() + 30 * 24 * 60 * 60 * 1000);

            // add the existing one into it
            builder.addCRL(crlHolder);
            // Add the serial to be revoked
            builder.addCRLEntry(certificate.getObject().getSerialNumber(), date, CRLReason.privilegeWithdrawn);
            builder.setNextUpdate(nextUpdate);

            ContentSigner contentSigner = createSigner(getPartitionKeyPair().getPrivate(), getConfiguration());

            X509CRLHolder updatedCRL = builder.build(contentSigner);

            CertificateRevocationListType certificateRevocationList = getCertificateRevocationList();

            certificateRevocationList.setEncoded(Base64.encodeBytes(updatedCRL.getEncoded()));

            getIdentityManager().update(certificateRevocationList);
        } catch (Exception e) {
            throw new RuntimeException("Could not update revocation list.", e);
        }
    }

    @Override
    public boolean isRevoked(CertificateType certificate) {
        X509Certificate x509Certificate = certificate.getObject();
        X509CRLEntryHolder revocationEntry = getCRLHolder()
                .getRevokedCertificate(x509Certificate.getSerialNumber());

        if (revocationEntry != null) {
            return true;
        }

        return false;
    }

    @Override
    public CertificateAuthorityConfig getConfiguration() {
        return getConfiguration(getPartition());
    }

    private X509CRLHolder getCRLHolder() {
        CertificateRevocationListType certificateRevocationList = getCertificateRevocationList();

        byte[] csrHolderBytes = Base64.decode(certificateRevocationList.getEncoded());

        try {
            return new X509CRLHolder(csrHolderBytes);
        } catch (IOException e) {
            throw new RuntimeException("Could not obtain CRL.", e);
        }
    }

    private CertificateRevocationListType getCertificateRevocationList() {
        IdentityManager identityManager = getIdentityManager();

        IdentityQueryBuilder builder = identityManager.getQueryBuilder();
        Condition condition = builder.equal(CertificateRevocationListType.PARTITION_ID, getPartition().getId());
        IdentityQuery<CertificateRevocationListType> query = builder
                .createIdentityQuery(CertificateRevocationListType.class).where(condition);

        List<CertificateRevocationListType> result = query.getResultList();

        if (result.isEmpty()) {
            return null;
        }

        return result.get(0);
    }

    private KeyPair getPartitionKeyPair() {
        return this.keyAuthority.getKeyPair(getPartition());
    }

    private static boolean hasCertificate(Partition partition, PartitionManager partitionManager) {
        IdentityManager identityManager = partitionManager.createIdentityManager(partition);

        IdentityQueryBuilder builder = identityManager.getQueryBuilder();
        Condition condition = builder.equal(CertificateType.ATTRIBUTED_TYPE_ID, partition.getId());
        IdentityQuery<CertificateType> query = builder.createIdentityQuery(CertificateType.class).where(condition);

        List<CertificateType> result = query.getResultList();

        return !result.isEmpty();
    }

    private CertificateType getCertificate(String attributedTypeId) {
        IdentityManager identityManager = getIdentityManager();

        IdentityQueryBuilder builder = identityManager.getQueryBuilder();
        Condition condition = builder.equal(CertificateType.ATTRIBUTED_TYPE_ID, attributedTypeId);
        IdentityQuery<CertificateType> query = builder.createIdentityQuery(CertificateType.class).where(condition);

        List<CertificateType> result = query.getResultList();

        if (result.isEmpty()) {
            return null;
        }

        return result.get(0);
    }

    private static X509CRLHolder createRevocationList(KeyPair partitionKeys, X509Certificate certificate,
            CertificateAuthorityConfig config) {
        Date date = new Date();
        X509v2CRLBuilder builder = new X509v2CRLBuilder(new X500Name(certificate.getSubjectDN().getName()), date);
        Date nextUpdate = new Date(date.getTime() + 30 * 24 * 60 * 60 * 1000); // Every 30 days

        builder.setNextUpdate(nextUpdate);

        ContentSigner contentSigner = createSigner(partitionKeys.getPrivate(), config);

        return builder.build(contentSigner);
    }

    static void issueSelf(Partition partition, PartitionManager partitionManager, CertificateAuthorityConfig config,
            EventBridge eventBridge) {
        if (!hasCertificate(partition, partitionManager)) {

            try {
                KeyPair partitionKeys = DefaultKeyAuthority.getKeyPair(partition, partitionManager);
                if (partitionKeys == null) {
                    partitionKeys = generateKeyPair(partition, partitionManager, eventBridge, config);
                }

                X500Name partitionDN = new X500Name("CN=" + partition.getName() + "," + config.getBaseDN());

                CertificateCreateEvent createEvent = new CertificateCreateEvent(partition);
                eventBridge.raiseEvent(createEvent);
                X509Certificate x509Certificate = createEvent.getX509Certificate();

                if (createEvent.getX509Certificate() == null) {
                    x509Certificate = generateV1Certificate(partitionDN, partitionKeys, config);
                }

                CertificateType newCertificate = new CertificateType(partition, x509Certificate, config);
                IdentityManager identityManager = partitionManager.createIdentityManager(partition);
                identityManager.add(newCertificate);

                X509CRLHolder crlHolder = createRevocationList(partitionKeys, x509Certificate, config);
                CertificateRevocationListType crl = new CertificateRevocationListType(partition,
                        encodeBytes(crlHolder.getEncoded()));
                identityManager.add(crl);

            } catch (Exception e) {
                throw new RuntimeException("Could not initialize partition as CA.", e);
            }
        }
    }

}