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 ForgeRock AS.
015 */
016package org.forgerock.audit.secure;
017
018import java.security.InvalidKeyException;
019import java.security.NoSuchAlgorithmException;
020import java.security.PrivateKey;
021import java.security.PublicKey;
022import java.security.Signature;
023import java.security.SignatureException;
024
025import javax.crypto.SecretKey;
026
027
028/**
029 * Implementation of a secure storage using a keystore.
030 */
031public class KeyStoreSecureStorage implements SecureStorage {
032
033    /** The initial key used to calculate the HEADER_HMAC. */
034    public static final String ENTRY_INITIAL_KEY = "InitialKey";
035
036    /** The alias to lookup the private/public signature key into the keystore. */
037    public static final String ENTRY_SIGNATURE = "Signature";
038
039    /** The last signature inserted into the file. */
040    public static final String ENTRY_CURRENT_SIGNATURE = "CurrentSignature";
041
042    /** The current key used to calculate the HEADER_HMAC. */
043    public static final String ENTRY_CURRENT_KEY = "CurrentKey";
044
045    public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
046    public static final String HMAC_ALGORITHM = "HmacSHA256";
047    public static final String JCEKS_KEYSTORE_TYPE = "JCEKS";
048
049    private KeyStoreHandlerDecorator keyStoreHandler;
050    private Signature verifier;
051    private Signature signer;
052
053    /**
054     * Creates the storage with a keystore handler, initialized to verify only.
055     *
056     * @param keyStoreHandler
057     *            Handler of a keystore.
058     * @param privateKey
059     *            The private key used to initialize the signer
060     */
061    public KeyStoreSecureStorage(KeyStoreHandler keyStoreHandler, PrivateKey privateKey) {
062        this(keyStoreHandler, null, privateKey);
063    }
064
065    /**
066     * Creates the storage with a keystore handler, initialized to verify only.
067     *
068     * @param keyStoreHandler
069     *            Handler of a keystore.
070     * @param publicKey
071     *            The public key used to initialize the verifier
072     */
073    public KeyStoreSecureStorage(KeyStoreHandler keyStoreHandler, PublicKey publicKey) {
074        this(keyStoreHandler, publicKey, null);
075    }
076
077    /**
078     * Creates the storage with a keystore handler, initialized to verify only.
079     *
080     * @param keyStoreHandler
081     *            Handler of a keystore.
082     * @param publicKey
083     *            The public key used to initialize the verifier
084     * @param privateKey
085     *            The private key used to initialize the signer
086     */
087    public KeyStoreSecureStorage(KeyStoreHandler keyStoreHandler, PublicKey publicKey, PrivateKey privateKey) {
088        setKeyStoreHandler(keyStoreHandler);
089
090        if (privateKey != null) {
091            try {
092                signer = Signature.getInstance(KeyStoreSecureStorage.SIGNATURE_ALGORITHM);
093                signer.initSign(privateKey);
094            } catch (InvalidKeyException | NoSuchAlgorithmException e) {
095                throw new IllegalArgumentException(e);
096            }
097        }
098
099        if (publicKey != null) {
100            try {
101                verifier = Signature.getInstance(KeyStoreSecureStorage.SIGNATURE_ALGORITHM);
102                verifier.initVerify(publicKey);
103            } catch (InvalidKeyException | NoSuchAlgorithmException e) {
104                throw new IllegalArgumentException(e);
105            }
106        }
107    }
108
109    public void setKeyStoreHandler(KeyStoreHandler keyStoreHandler) {
110        this.keyStoreHandler = new KeyStoreHandlerDecorator(keyStoreHandler);
111    }
112
113    @Override
114    public String getPassword() {
115        return keyStoreHandler.getPassword();
116    }
117
118    @Override
119    public SecretKey readCurrentKey() throws SecureStorageException {
120        return keyStoreHandler.readSecretKeyFromKeyStore(KeyStoreSecureStorage.ENTRY_CURRENT_KEY);
121    }
122
123    @Override
124    public SecretKey readInitialKey() throws SecureStorageException {
125        return keyStoreHandler.readSecretKeyFromKeyStore(KeyStoreSecureStorage.ENTRY_INITIAL_KEY);
126    }
127
128    @Override
129    public void writeCurrentSignatureKey(SecretKey key) throws SecureStorageException {
130        keyStoreHandler.writeToKeyStore(key, KeyStoreSecureStorage.ENTRY_CURRENT_SIGNATURE, keyStoreHandler.getPassword());
131        try {
132            keyStoreHandler.store();
133        } catch (Exception ex) {
134            throw new SecureStorageException(ex);
135        }
136    }
137
138    @Override
139    public void writeCurrentKey(SecretKey key) throws SecureStorageException {
140        writeKey(key, KeyStoreSecureStorage.ENTRY_CURRENT_KEY);
141    }
142
143
144    @Override
145    public void writeInitialKey(SecretKey key) throws SecureStorageException {
146        writeKey(key, KeyStoreSecureStorage.ENTRY_INITIAL_KEY);
147    }
148
149    private void writeKey(SecretKey key, String alias) throws SecureStorageException {
150        keyStoreHandler.writeToKeyStore(key, alias, keyStoreHandler.getPassword());
151        try {
152            keyStoreHandler.store();
153        } catch (Exception ex) {
154            throw new SecureStorageException(ex);
155        }
156    }
157
158    @Override
159    public byte[] sign(byte[] signedData) throws SecureStorageException {
160        try {
161            signer.update(signedData);
162            return signer.sign();
163        } catch (SignatureException e) {
164            throw new SecureStorageException(e);
165        }
166    }
167
168    @Override
169    public boolean verify(byte[] signedData, byte[] signature) throws SecureStorageException {
170        try {
171            verifier.update(signedData);
172            return verifier.verify(signature);
173        } catch (SignatureException e) {
174            throw new SecureStorageException(e);
175        }
176    }
177}