/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2010 Sun Microsystems, Inc. * Portions copyright 2012 ForgeRock AS. */ package org.forgerock.opendj.ldap; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.Socket; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLEngine; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509KeyManager; import com.forgerock.opendj.util.Validator; /** * This class contains methods for creating common types of key manager. */ public final class KeyManagers { /** * This class implements an X.509 key manager that will be used to wrap an * existing key manager and makes it possible to configure which * certificate(s) should be used for client and/or server operations. The * certificate selection will be based on the alias (also called the * nickname) of the certificate. */ private static final class SelectCertificate extends X509ExtendedKeyManager { private final String alias; private final X509KeyManager keyManager; private SelectCertificate(final X509KeyManager keyManager, final String alias) { this.keyManager = keyManager; this.alias = alias; } /** * {@inheritDoc} */ public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) { for (final String type : keyType) { final String[] clientAliases = keyManager.getClientAliases(type, issuers); if (clientAliases != null) { for (final String clientAlias : clientAliases) { if (clientAlias.equals(alias)) { return alias; } } } } return null; } /** * {@inheritDoc} */ @Override public String chooseEngineClientAlias(final String[] keyType, final Principal[] issuers, final SSLEngine engine) { for (final String type : keyType) { final String[] clientAliases = keyManager.getClientAliases(type, issuers); if (clientAliases != null) { for (final String clientAlias : clientAliases) { if (clientAlias.equals(alias)) { return alias; } } } } return null; } /** * {@inheritDoc} */ @Override public String chooseEngineServerAlias(final String keyType, final Principal[] issuers, final SSLEngine engine) { final String[] serverAliases = keyManager.getServerAliases(keyType, issuers); if (serverAliases != null) { for (final String serverAlias : serverAliases) { if (serverAlias.equalsIgnoreCase(alias)) { return serverAlias; } } } return null; } /** * {@inheritDoc} */ public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) { final String[] serverAliases = keyManager.getServerAliases(keyType, issuers); if (serverAliases != null) { for (final String serverAlias : serverAliases) { if (serverAlias.equals(alias)) { return alias; } } } return null; } /** * {@inheritDoc} */ public X509Certificate[] getCertificateChain(final String alias) { return keyManager.getCertificateChain(alias); } /** * {@inheritDoc} */ public String[] getClientAliases(final String keyType, final Principal[] issuers) { return keyManager.getClientAliases(keyType, issuers); } /** * {@inheritDoc} */ public PrivateKey getPrivateKey(final String alias) { return keyManager.getPrivateKey(alias); } /** * {@inheritDoc} */ public String[] getServerAliases(final String keyType, final Principal[] issuers) { return keyManager.getServerAliases(keyType, issuers); } } /** * Creates a new {@code X509KeyManager} which will use the named key store * file for retrieving certificates. It will use the default key store * format for the JVM (e.g. {@code JKS}) and will not use a password to open * the key store. * * @param file * The key store file name. * @return A new {@code X509KeyManager} which will use the named key store * file for retrieving certificates. * @throws GeneralSecurityException * If the key store could not be loaded, perhaps due to * incorrect format, or missing algorithms. * @throws IOException * If the key store file could not be found or could not be * read. * @throws NullPointerException * If {@code file} was {@code null}. */ public static X509KeyManager useKeyStoreFile(final String file) throws GeneralSecurityException, IOException { return useKeyStoreFile(file, null, null); } /** * Creates a new {@code X509KeyManager} which will use the named key store * file for retrieving certificates. It will use the provided key store * format and password. * * @param file * The key store file name. * @param password * The key store password, which may be {@code null}. * @param format * The key store format, which may be {@code null} to indicate * that the default key store format for the JVM (e.g. * {@code JKS}) should be used. * @return A new {@code X509KeyManager} which will use the named key store * file for retrieving certificates. * @throws GeneralSecurityException * If the key store could not be loaded, perhaps due to * incorrect format, or missing algorithms. * @throws IOException * If the key store file could not be found or could not be * read. * @throws NullPointerException * If {@code file} was {@code null}. */ public static X509KeyManager useKeyStoreFile(final String file, final char[] password, final String format) throws GeneralSecurityException, IOException { Validator.ensureNotNull(file); final File keyStoreFile = new File(file); final String keyStoreFormat = format != null ? format : KeyStore.getDefaultType(); final KeyStore keyStore = KeyStore.getInstance(keyStoreFormat); FileInputStream fos = null; try { fos = new FileInputStream(keyStoreFile); keyStore.load(fos, password); } finally { if (fos != null) { try { fos.close(); } catch (final IOException ignored) { // Ignore. } } } final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, password); X509KeyManager x509km = null; for (final KeyManager km : kmf.getKeyManagers()) { if (km instanceof X509KeyManager) { x509km = (X509KeyManager) km; break; } } if (x509km == null) { throw new NoSuchAlgorithmException(); } return x509km; } /** * Creates a new {@code X509KeyManager} which will use a PKCS#11 token for * retrieving certificates. * * @param password * The password to use for accessing the PKCS#11 token, which may * be {@code null} if no password is required. * @return A new {@code X509KeyManager} which will use a PKCS#11 token for * retrieving certificates. * @throws GeneralSecurityException * If the PKCS#11 token could not be accessed, perhaps due to * incorrect password, or missing algorithms. * @throws IOException * If the PKCS#11 token could not be found or could not be read. */ public static X509KeyManager usePKCS11Token(final char[] password) throws GeneralSecurityException, IOException { final KeyStore keyStore = KeyStore.getInstance("PKCS11"); keyStore.load(null, password); final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, password); X509KeyManager x509km = null; for (final KeyManager km : kmf.getKeyManagers()) { if (km instanceof X509KeyManager) { x509km = (X509KeyManager) km; break; } } if (x509km == null) { throw new NoSuchAlgorithmException(); } return x509km; } /** * Returns a new {@code X509KeyManager} which selects the named certificate * from the provided {@code X509KeyManager}. * * @param alias * The nickname of the certificate that should be selected for * operations involving this key manager. * @param keyManager * The key manager to be filtered. * @return The filtered key manager. * @throws NullPointerException * If {@code keyManager} or {@code alias} was {@code null}. */ public static X509KeyManager useSingleCertificate(final String alias, final X509KeyManager keyManager) { Validator.ensureNotNull(alias, keyManager); return new SelectCertificate(keyManager, alias); } // Prevent insantiation. private KeyManagers() { // Nothing to do. } }