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 2015-2016 ForgeRock AS.
015 */
016package org.forgerock.audit.secure;
017
018import java.security.KeyStore;
019import java.security.KeyStoreException;
020import java.security.NoSuchAlgorithmException;
021import java.security.PrivateKey;
022import java.security.PublicKey;
023import java.security.UnrecoverableEntryException;
024import java.security.cert.Certificate;
025
026import javax.crypto.SecretKey;
027
028/**
029 * Decorate a {@link KeyStoreHandler} in order to add some commons utility methods to read or write keystore's entries.
030 */
031public class KeyStoreHandlerDecorator implements KeyStoreHandler {
032
033    private final KeyStoreHandler delegate;
034
035    /**
036     * Constructs a new {@literal KeyStoreHandlerDecorator}.
037     * @param delegate the {@literal KeyStoreHandler} to decorate.
038     */
039    public KeyStoreHandlerDecorator(KeyStoreHandler delegate) {
040        this.delegate = delegate;
041    }
042
043    /**
044     * Writes to the secret storage using the same password than the {@literal KeyStoreHandler}.
045     *
046     * @param secretKey
047     *            The data to be written to the secret storage
048     * @param alias
049     *            The kind of cryptoMaterial, whether it is a signature or a key
050     * @throws SecureStorageException
051     *             if it fails to write secret data from secret store
052     */
053    public void writeToKeyStore(SecretKey secretKey, String alias) throws SecureStorageException {
054        writeToKeyStore(secretKey, alias, getPassword());
055    }
056
057    /**
058     * Writes to the secret storage.
059     *
060     * @param secretKey
061     *            The data to be written to the secret storage
062     * @param alias
063     *            The kind of cryptoMaterial, whether it is a signature or a key
064     * @param password
065     *            The password to read the key
066     * @throws SecureStorageException
067     *             if it fails to write secret data from secret store
068     */
069    public void writeToKeyStore(SecretKey secretKey, String alias, String password) throws SecureStorageException {
070        // Note that it need JCEKS to support secret keys.
071        try {
072            KeyStore store = getStore();
073            if (store.containsAlias(alias)) {
074                store.deleteEntry(alias);
075            }
076            KeyStore.SecretKeyEntry secKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
077            KeyStore.ProtectionParameter params = new KeyStore.PasswordProtection(password.toCharArray());
078            store.setEntry(alias, secKeyEntry, params);
079        } catch (Exception ex) {
080            throw new SecureStorageException(ex);
081        }
082    }
083
084    /**
085     * Get the public key with the given alias.
086     * @param alias The alias.
087     * @return The key.
088     * @throws SecureStorageException If the key could not be read.
089     */
090    public PublicKey readPublicKeyFromKeyStore(String alias) throws SecureStorageException {
091        try {
092            KeyStore store = getStore();
093            Certificate certificate = store.getCertificate(alias);
094            return certificate.getPublicKey();
095        } catch (KeyStoreException ex) {
096            throw new SecureStorageException("Error when reading public key: " + alias, ex);
097        }
098    }
099
100    /**
101     * Get a private key for the alias using the default password from {@link #getPassword()}.
102     * @param alias The alias.
103     * @return The key.
104     * @throws SecureStorageException If the key could not be read.
105     */
106    public PrivateKey readPrivateKeyFromKeyStore(String alias) throws SecureStorageException {
107        return readPrivateKeyFromKeyStore(alias, getPassword());
108    }
109
110    /**
111     * Get the private key with the given alias.
112     * @param alias The alias.
113     * @param password The password to use to access the keystore.
114     * @return The key.
115     * @throws SecureStorageException If the key could not be read.
116     */
117    public PrivateKey readPrivateKeyFromKeyStore(String alias, String password) throws SecureStorageException {
118        try {
119            KeyStore store = getStore();
120            KeyStore.ProtectionParameter params = password != null
121                    ? new KeyStore.PasswordProtection(password.toCharArray())
122                    : null;
123            KeyStore.PrivateKeyEntry keyentry = (KeyStore.PrivateKeyEntry) store.getEntry(alias, params);
124            return keyentry != null ? keyentry.getPrivateKey() : null;
125        } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException ex) {
126            throw new SecureStorageException(ex);
127        }
128    }
129
130    /**
131     * Get the secret key with the given alias using the default password from {@link #getPassword()}.
132     * @param alias The alias.
133     * @return The key.
134     * @throws SecureStorageException If the key could not be read.
135     */
136    public SecretKey readSecretKeyFromKeyStore(String alias) throws SecureStorageException {
137        return readSecretKeyFromKeyStore(alias, getPassword());
138    }
139
140    /**
141     * Get the secret key with the given alias.
142     * @param alias The alias.
143     * @param password The password to use to access the keystore.
144     * @return The key.
145     * @throws SecureStorageException If the key could not be read.
146     */
147    public SecretKey readSecretKeyFromKeyStore(String alias, String password) throws SecureStorageException {
148        try {
149            KeyStore store = getStore();
150            KeyStore.ProtectionParameter params = new KeyStore.PasswordProtection(password.toCharArray());
151            KeyStore.SecretKeyEntry keyentry = (KeyStore.SecretKeyEntry) store.getEntry(alias, params);
152            return keyentry != null ? keyentry.getSecretKey() : null;
153        } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
154            throw new SecureStorageException(e);
155        }
156    }
157
158    @Override
159    public KeyStore getStore() {
160        return delegate.getStore();
161    }
162
163    @Override
164    public void setStore(KeyStore keystore) throws Exception {
165        delegate.setStore(keystore);
166    }
167
168    @Override
169    public String getPassword() {
170        return delegate.getPassword();
171    }
172
173    @Override
174    public String getLocation() {
175        return delegate.getLocation();
176    }
177
178    @Override
179    public String getType() {
180        return delegate.getType();
181    }
182
183    @Override
184    public void store() throws Exception {
185        delegate.store();
186    }
187
188}