be.fedict.hsm.model.KeyStoreSingletonBean.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.hsm.model.KeyStoreSingletonBean.java

Source

/*
 * HSM Proxy Project.
 * Copyright (C) 2013 FedICT.
 * Copyright (C) 2013 Frank Cornelis.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * 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, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.hsm.model;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.EJB;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import be.fedict.hsm.entity.KeyStoreEntity;

@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class KeyStoreSingletonBean {

    private static final Log LOG = LogFactory.getLog(KeyStoreSingletonBean.class);

    private static final byte[] SHA1_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
            0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };

    private static final byte[] SHA256_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
            (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };

    private static final byte[] SHA512_DIGEST_INFO_PREFIX = new byte[] { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
            (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };

    private static final Map<String, byte[]> digestInfoPrefixes;

    static {
        digestInfoPrefixes = new HashMap<String, byte[]>();
        digestInfoPrefixes.put("SHA-1", SHA1_DIGEST_INFO_PREFIX);
        digestInfoPrefixes.put("SHA-256", SHA256_DIGEST_INFO_PREFIX);
        digestInfoPrefixes.put("SHA-512", SHA512_DIGEST_INFO_PREFIX);
    }

    /**
     * key store id -> key store alias -> private key
     */
    private Map<Long, Map<String, PrivateKeyEntry>> privateKeyEntries;

    @PersistenceContext
    private EntityManager entityManager;

    @EJB
    private KeyStoreLoader keyStoreLoader;

    @PostConstruct
    @Lock(LockType.WRITE)
    public void loadKeys() {
        LOG.debug("load keys...");
        this.privateKeyEntries = new HashMap<Long, Map<String, PrivateKeyEntry>>();
        List<KeyStoreEntity> keyStoreEntities = KeyStoreEntity.getList(this.entityManager);
        for (KeyStoreEntity keyStoreEntity : keyStoreEntities) {
            load(keyStoreEntity);
        }
    }

    private boolean load(KeyStoreEntity keyStoreEntity) {
        Map<String, PrivateKeyEntry> entries = this.keyStoreLoader.loadKeyStore(keyStoreEntity);
        if (null != entries) {
            this.privateKeyEntries.put(keyStoreEntity.getId(), entries);
            return true;
        } else {
            this.privateKeyEntries.remove(keyStoreEntity.getId());
            return false;
        }
    }

    /**
     * Sign the given digest value.
     * 
     * @param keyStoreId
     * @param keyStoreAlias
     * @param digestAlgo
     * @param digestValue
     * @return the signature, or <code>null</code> in case something went wrong.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     * @throws SignatureException
     */
    @Lock(LockType.READ)
    public byte[] sign(long keyStoreId, String keyStoreAlias, String digestAlgo, byte[] digestValue)
            throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException {
        Map<String, PrivateKeyEntry> keyStoreKeys = this.privateKeyEntries.get(keyStoreId);
        if (null == keyStoreKeys) {
            LOG.error("unknown key store: " + keyStoreId);
            return null;
        }
        PrivateKeyEntry privateKeyEntry = keyStoreKeys.get(keyStoreAlias);
        if (null == privateKeyEntry) {
            LOG.error("private key for alias not available: " + keyStoreAlias);
            return null;
        }
        PrivateKey privateKey = privateKeyEntry.getPrivateKey();
        Signature signature = Signature.getInstance("NONEwithRSA");
        signature.initSign(privateKey);

        ByteArrayOutputStream digestInfo = new ByteArrayOutputStream();
        byte[] digestInfoPrefix = digestInfoPrefixes.get(digestAlgo);
        if (null == digestInfoPrefix) {
            throw new NoSuchAlgorithmException(digestAlgo);
        }
        digestInfo.write(digestInfoPrefix);
        digestInfo.write(digestValue);

        signature.update(digestInfo.toByteArray());

        return signature.sign();
    }

    @Lock(LockType.WRITE)
    public boolean newKeyStore(long keyStoreId) {
        KeyStoreEntity keyStoreEntity = this.entityManager.find(KeyStoreEntity.class, keyStoreId);
        LOG.debug("new key store: " + keyStoreId);
        return load(keyStoreEntity);
    }

    @Lock(LockType.WRITE)
    public List<String> getKeyStoreAliases(long keyStoreId) {
        Map<String, PrivateKeyEntry> keyStorePrivateKeys = this.privateKeyEntries.get(keyStoreId);
        if (null == keyStorePrivateKeys) {
            return new LinkedList<String>();
        }
        List<String> aliases = new LinkedList<String>();
        for (String alias : keyStorePrivateKeys.keySet()) {
            LOG.debug("key store alias: " + alias);
            aliases.add(alias);
        }
        return aliases;
    }

    @Lock(LockType.WRITE)
    public void removeKeyStore(long keyStoreId) {
        this.privateKeyEntries.remove(keyStoreId);
    }

    @Lock(LockType.WRITE)
    public boolean reload(long keyStoreId) {
        this.privateKeyEntries.remove(keyStoreId);
        KeyStoreEntity keyStoreEntity = this.entityManager.find(KeyStoreEntity.class, keyStoreId);
        return load(keyStoreEntity);
    }

    @Lock(LockType.WRITE)
    public Certificate[] getCertificateChain(long keyStoreId, String keyStoreAlias) {
        Map<String, PrivateKeyEntry> keyStorePrivateKeys = this.privateKeyEntries.get(keyStoreId);
        if (null == keyStorePrivateKeys) {
            LOG.error("key store not found: " + keyStoreId);
            return null;
        }
        PrivateKeyEntry privateKeyEntry = keyStorePrivateKeys.get(keyStoreAlias);
        if (null == privateKeyEntry) {
            LOG.error("no key entry found for alias: " + keyStoreAlias);
            return null;
        }
        return privateKeyEntry.getCertificateChain();
    }
}