001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2010 Sun Microsystems, Inc. 025 * Portions copyright 2012 ForgeRock AS. 026 */ 027 028package org.forgerock.opendj.ldap; 029 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.IOException; 033import java.net.Socket; 034import java.security.GeneralSecurityException; 035import java.security.KeyStore; 036import java.security.NoSuchAlgorithmException; 037import java.security.Principal; 038import java.security.PrivateKey; 039import java.security.cert.X509Certificate; 040 041import javax.net.ssl.KeyManager; 042import javax.net.ssl.KeyManagerFactory; 043import javax.net.ssl.SSLEngine; 044import javax.net.ssl.X509ExtendedKeyManager; 045import javax.net.ssl.X509KeyManager; 046 047import org.forgerock.util.Reject; 048 049/** 050 * This class contains methods for creating common types of key manager. 051 */ 052public final class KeyManagers { 053 /** 054 * This class implements an X.509 key manager that will be used to wrap an 055 * existing key manager and makes it possible to configure which 056 * certificate(s) should be used for client and/or server operations. The 057 * certificate selection will be based on the alias (also called the 058 * nickname) of the certificate. 059 */ 060 private static final class SelectCertificate extends X509ExtendedKeyManager { 061 private final String alias; 062 private final X509KeyManager keyManager; 063 064 private SelectCertificate(final X509KeyManager keyManager, final String alias) { 065 this.keyManager = keyManager; 066 this.alias = alias; 067 } 068 069 /** {@inheritDoc} */ 070 public String chooseClientAlias(final String[] keyType, final Principal[] issuers, 071 final Socket socket) { 072 for (final String type : keyType) { 073 final String[] clientAliases = keyManager.getClientAliases(type, issuers); 074 if (clientAliases != null) { 075 for (final String clientAlias : clientAliases) { 076 if (clientAlias.equals(alias)) { 077 return alias; 078 } 079 } 080 } 081 } 082 083 return null; 084 } 085 086 /** {@inheritDoc} */ 087 @Override 088 public String chooseEngineClientAlias(final String[] keyType, final Principal[] issuers, 089 final SSLEngine engine) { 090 for (final String type : keyType) { 091 final String[] clientAliases = keyManager.getClientAliases(type, issuers); 092 if (clientAliases != null) { 093 for (final String clientAlias : clientAliases) { 094 if (clientAlias.equals(alias)) { 095 return alias; 096 } 097 } 098 } 099 } 100 101 return null; 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public String chooseEngineServerAlias(final String keyType, final Principal[] issuers, 107 final SSLEngine engine) { 108 final String[] serverAliases = keyManager.getServerAliases(keyType, issuers); 109 if (serverAliases != null) { 110 for (final String serverAlias : serverAliases) { 111 if (serverAlias.equalsIgnoreCase(alias)) { 112 return serverAlias; 113 } 114 } 115 } 116 117 return null; 118 } 119 120 /** {@inheritDoc} */ 121 public String chooseServerAlias(final String keyType, final Principal[] issuers, 122 final Socket socket) { 123 final String[] serverAliases = keyManager.getServerAliases(keyType, issuers); 124 if (serverAliases != null) { 125 for (final String serverAlias : serverAliases) { 126 if (serverAlias.equals(alias)) { 127 return alias; 128 } 129 } 130 } 131 132 return null; 133 } 134 135 /** {@inheritDoc} */ 136 public X509Certificate[] getCertificateChain(final String alias) { 137 return keyManager.getCertificateChain(alias); 138 } 139 140 /** {@inheritDoc} */ 141 public String[] getClientAliases(final String keyType, final Principal[] issuers) { 142 return keyManager.getClientAliases(keyType, issuers); 143 } 144 145 /** {@inheritDoc} */ 146 public PrivateKey getPrivateKey(final String alias) { 147 return keyManager.getPrivateKey(alias); 148 } 149 150 /** {@inheritDoc} */ 151 public String[] getServerAliases(final String keyType, final Principal[] issuers) { 152 return keyManager.getServerAliases(keyType, issuers); 153 } 154 } 155 156 /** 157 * Creates a new {@code X509KeyManager} which will use the named key store 158 * file for retrieving certificates. It will use the default key store 159 * format for the JVM (e.g. {@code JKS}) and will not use a password to open 160 * the key store. 161 * 162 * @param file 163 * The key store file name. 164 * @return A new {@code X509KeyManager} which will use the named key store 165 * file for retrieving certificates. 166 * @throws GeneralSecurityException 167 * If the key store could not be loaded, perhaps due to 168 * incorrect format, or missing algorithms. 169 * @throws IOException 170 * If the key store file could not be found or could not be 171 * read. 172 * @throws NullPointerException 173 * If {@code file} was {@code null}. 174 */ 175 public static X509KeyManager useKeyStoreFile(final String file) 176 throws GeneralSecurityException, IOException { 177 return useKeyStoreFile(file, null, null); 178 } 179 180 /** 181 * Creates a new {@code X509KeyManager} which will use the named key store 182 * file for retrieving certificates. It will use the provided key store 183 * format and password. 184 * 185 * @param file 186 * The key store file name. 187 * @param password 188 * The key store password, which may be {@code null}. 189 * @param format 190 * The key store format, which may be {@code null} to indicate 191 * that the default key store format for the JVM (e.g. 192 * {@code JKS}) should be used. 193 * @return A new {@code X509KeyManager} which will use the named key store 194 * file for retrieving certificates. 195 * @throws GeneralSecurityException 196 * If the key store could not be loaded, perhaps due to 197 * incorrect format, or missing algorithms. 198 * @throws IOException 199 * If the key store file could not be found or could not be 200 * read. 201 * @throws NullPointerException 202 * If {@code file} was {@code null}. 203 */ 204 public static X509KeyManager useKeyStoreFile(final String file, final char[] password, 205 final String format) throws GeneralSecurityException, IOException { 206 Reject.ifNull(file); 207 208 final File keyStoreFile = new File(file); 209 final String keyStoreFormat = format != null ? format : KeyStore.getDefaultType(); 210 211 final KeyStore keyStore = KeyStore.getInstance(keyStoreFormat); 212 213 FileInputStream fos = null; 214 try { 215 fos = new FileInputStream(keyStoreFile); 216 keyStore.load(fos, password); 217 } finally { 218 if (fos != null) { 219 try { 220 fos.close(); 221 } catch (final IOException ignored) { 222 // Ignore. 223 } 224 } 225 } 226 227 final KeyManagerFactory kmf = 228 KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 229 kmf.init(keyStore, password); 230 231 X509KeyManager x509km = null; 232 for (final KeyManager km : kmf.getKeyManagers()) { 233 if (km instanceof X509KeyManager) { 234 x509km = (X509KeyManager) km; 235 break; 236 } 237 } 238 239 if (x509km == null) { 240 throw new NoSuchAlgorithmException(); 241 } 242 243 return x509km; 244 } 245 246 /** 247 * Creates a new {@code X509KeyManager} which will use a PKCS#11 token for 248 * retrieving certificates. 249 * 250 * @param password 251 * The password to use for accessing the PKCS#11 token, which may 252 * be {@code null} if no password is required. 253 * @return A new {@code X509KeyManager} which will use a PKCS#11 token for 254 * retrieving certificates. 255 * @throws GeneralSecurityException 256 * If the PKCS#11 token could not be accessed, perhaps due to 257 * incorrect password, or missing algorithms. 258 * @throws IOException 259 * If the PKCS#11 token could not be found or could not be read. 260 */ 261 public static X509KeyManager usePKCS11Token(final char[] password) 262 throws GeneralSecurityException, IOException { 263 final KeyStore keyStore = KeyStore.getInstance("PKCS11"); 264 keyStore.load(null, password); 265 final KeyManagerFactory kmf = 266 KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 267 kmf.init(keyStore, password); 268 269 X509KeyManager x509km = null; 270 for (final KeyManager km : kmf.getKeyManagers()) { 271 if (km instanceof X509KeyManager) { 272 x509km = (X509KeyManager) km; 273 break; 274 } 275 } 276 277 if (x509km == null) { 278 throw new NoSuchAlgorithmException(); 279 } 280 281 return x509km; 282 } 283 284 /** 285 * Returns a new {@code X509KeyManager} which selects the named certificate 286 * from the provided {@code X509KeyManager}. 287 * 288 * @param alias 289 * The nickname of the certificate that should be selected for 290 * operations involving this key manager. 291 * @param keyManager 292 * The key manager to be filtered. 293 * @return The filtered key manager. 294 * @throws NullPointerException 295 * If {@code keyManager} or {@code alias} was {@code null}. 296 */ 297 public static X509KeyManager useSingleCertificate(final String alias, 298 final X509KeyManager keyManager) { 299 Reject.ifNull(alias, keyManager); 300 return new SelectCertificate(keyManager, alias); 301 } 302 303 /** Prevent insantiation. */ 304 private KeyManagers() { 305 // Nothing to do. 306 } 307 308}