| opends/src/ads/org/opends/admin/ads/util/ApplicationKeyManager.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/messages/messages/utility.properties | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/util/CertificateManager.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/util/Platform.java | ●●●●● patch | view | raw | blame | history | |
| opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java | ●●●●● patch | view | raw | blame | history |
opends/src/ads/org/opends/admin/ads/util/ApplicationKeyManager.java
@@ -44,6 +44,8 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509KeyManager; import org.opends.server.util.Platform; /** * This class is in charge of checking whether the certificates that are @@ -80,6 +82,13 @@ String userSpecifiedProvider = System.getProperty("org.opends.admin.keymanagerprovider"); //Handle IBM specific cases if the user did not specify a algorithm and/or //provider. if(userSpecifiedAlgo == null && Platform.isVendor("IBM")) userSpecifiedAlgo = "IbmX509"; if(userSpecifiedProvider == null && Platform.isVendor("IBM")) userSpecifiedProvider = "IBMJSEE2"; // Have some fallbacks to choose the provider and algorith of the key // manager. First see if the user wanted to use something specific, // then try with the SunJSSE provider and SunX509 algorithm. Finally, opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java
@@ -43,6 +43,8 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import org.opends.server.util.Platform; /** * This class is in charge of checking whether the certificates that are * presented are trusted or not. @@ -108,6 +110,13 @@ String userSpecifiedProvider = System.getProperty("org.opends.admin.trustmanagerprovider"); //Handle IBM specific cases if the user did not specify a algorithm and/or //provider. if(userSpecifiedAlgo == null && Platform.isVendor("IBM")) userSpecifiedAlgo = "IbmX509"; if(userSpecifiedProvider == null && Platform.isVendor("IBM")) userSpecifiedProvider = "IBMJSEE2"; // Have some fallbacks to choose the provider and algorith of the key // manager. First see if the user wanted to use something specific, // then try with the SunJSSE provider and SunX509 algorithm. Finally, @@ -126,48 +135,48 @@ "SunX509", TrustManagerFactory.getDefaultAlgorithm() }; for (int i=0; i<preferredProvider.length && trustManager == null; i++) { String provider = preferredProvider[i]; String algo = preferredAlgo[i]; if (algo == null) for (int i=0; i<preferredProvider.length && trustManager == null; i++) { continue; } try { if (provider != null) String provider = preferredProvider[i]; String algo = preferredAlgo[i]; if (algo == null) { tmf = TrustManagerFactory.getInstance(algo, provider); continue; } else try { tmf = TrustManagerFactory.getInstance(algo); } tmf.init(keystore); TrustManager[] trustManagers = tmf.getTrustManagers(); for (int j=0; j < trustManagers.length; j++) { if (trustManagers[j] instanceof X509TrustManager) if (provider != null) { trustManager = (X509TrustManager)trustManagers[j]; break; tmf = TrustManagerFactory.getInstance(algo, provider); } else { tmf = TrustManagerFactory.getInstance(algo); } tmf.init(keystore); TrustManager[] trustManagers = tmf.getTrustManagers(); for (int j=0; j < trustManagers.length; j++) { if (trustManagers[j] instanceof X509TrustManager) { trustManager = (X509TrustManager)trustManagers[j]; break; } } } catch (NoSuchProviderException e) { LOG.log(Level.WARNING, "Error with the provider: "+provider, e); } catch (NoSuchAlgorithmException e) { LOG.log(Level.WARNING, "Error with the algorithm: "+algo, e); } catch (KeyStoreException e) { LOG.log(Level.WARNING, "Error with the keystore", e); } } catch (NoSuchProviderException e) { LOG.log(Level.WARNING, "Error with the provider: "+provider, e); } catch (NoSuchAlgorithmException e) { LOG.log(Level.WARNING, "Error with the algorithm: "+algo, e); } catch (KeyStoreException e) { LOG.log(Level.WARNING, "Error with the keystore", e); } } } /** opends/src/messages/messages/utility.properties
@@ -557,3 +557,61 @@ INFO_ADMIN_CONN_PROMPT_PORT_NUMBER_270=Directory server administration port number [%d]: MILD_ERR_LDIF_INVALID_ATTR_OPTION_271=Unable to parse LDIF entry %s starting \ at line %d because it has an invalid binary option for attribute %s SEVERE_ERR_CERTMGR_INVALID_PKCS11_PATH_272=Invalid key store path for PKCS11 \ keystore, it must be %s SEVERE_ERR_CERTMGR_INVALID_KEYSTORE_PATH_273=Key store path %s exists but is \ not a file SEVERE_ERR_CERTMGR_INVALID_PARENT_274=Parent directory for key store path \ %s does not exist or is not a directory SEVERE_ERR_CERTMGR_INVALID_STORETYPE_275=Invalid key store type, it must \ be one of the following: %s, %s, %s or %s SEVERE_ERR_CERTMGR_KEYSTORE_NONEXISTANT_276=Keystore does not exist, \ it must exist to retrieve an alias, delete an alias or generate a \ certificate request SEVERE_ERR_CERTMGR_VALIDITY_277=Validity value %d is invalid, it must \ be a positive integer SEVERE_ERR_CERTMGR_ALIAS_ALREADY_EXISTS_278= A certificate with the alias \ %s already exists in the key store SEVERE_ERR_CERTMGR_ADD_CERT_279=The following error occured when \ adding a certificate with alias %s to the keystore: %s SEVERE_ERR_CERTMGR_ALIAS_INVALID_280=The alias %s is cannot be added to the \ keystore for one of the following reasons: it already exists in the \ keystore, or, it is not an instance of a trusted certificate class SEVERE_ERR_CERTMGR_CERT_REPLIES_INVALID_281=The alias %s is an instance of \ a private key entry, which is not supported being added to the keystore \ at this time SEVERE_ERR_CERTMGR_DELETE_ALIAS_282=The following error occured when \ deleting a certificate with alias %s from the keystore: %s SEVERE_ERR_CERTMGR_CERT_REQUEST_283=The following error occured when \ generating a certificate request with alias %s: %s SEVERE_ERR_CERTMGR_GEN_SELF_SIGNED_CERT_284=The following error occured when \ generating a self-signed certificate using the alias %s: %s SEVERE_ERR_CERTMGR_INVALID_CERT_FILE_285=The certificate file %s is \ invalid because it does not exists, or exists, but is not a file SEVERE_ERR_CERTMGR_ALIAS_CAN_NOT_DELETE_286=The alias %s cannot be \ deleted from the keystore because it does not exist SEVERE_ERR_CERTMGR_ALIAS_DOES_NOT_EXIST_287=The alias %s does not exist \ in the keystore so its key information cannot be retrieved SEVERE_ERR_CERTMGR_ALIAS_INVALID_ENTRY_TYPE_288=The alias %s is not a \ valid keystore entry type, so its key information cannot be retrieved SEVERE_ERR_CERTMGR_GET_KEY_289=The key information for alias %s \ cannot be retrieved because of the following reason: %s SEVERE_ERR_CERTMGR_PRIVATE_KEY_290=The private key for alias %s \ could not be retrieved because it was not a key related entry SEVERE_ERR_CERTMGR_ALIAS_NO_CERTIFICATE_291=The alias %s does not \ does not have a certificate associated with it SEVERE_ERR_CERTMGR_TRUSTED_CERT_292=The trusted certificate associated \ with alias %s could not be added to keystore because of the following \ reason: %s SEVERE_ERR_CERTMGR_FILE_NAME_INVALID_293=The %s is invalid because it is \ null SEVERE_ERR_CERTMGR_VALUE_INVALID_294=The argument %s is invalid because it \ is either null, or has zero length SEVERE_ERR_CERTMGR_CLASS_NOT_FOUND_295=A security class cannot be found \ in this JVM because of the following reason: %s SEVERE_ERR_CERTMGR_SECURITY_296=The security classes could not be \ initialized because of the following reason: %s SEVERE_ERR_CERTMGR_NO_METHOD_297=A method needed in the security classes \ could not be located because of the following reason: %s SEVERE_ERR_CERTMGR_CERT_SIGN_REQ_NOT_SUPPORTED_298=Certificate signing \ request generation is not supported on JVM supplied by this vendor: %s opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Copyright 2006-2009 Sun Microsystems, Inc. */ package org.opends.server.crypto; @@ -2884,7 +2884,13 @@ } final Cipher cipher = getCipher(keyEntry, Cipher.DECRYPT_MODE, iv); return cipher.doFinal(data, readIndex, data.length - readIndex); if(data.length - readIndex > 0) return cipher.doFinal(data, readIndex, data.length - readIndex); else { //IBM Java 6 throws an IllegalArgumentException when there's n // data to process. return cipher.doFinal(); } } opends/src/server/org/opends/server/util/CertificateManager.java
@@ -24,59 +24,33 @@ * * Copyright 2008-2009 Sun Microsystems, Inc. */ package org.opends.server.util; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.File; import java.io.OutputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.io.*; import java.security.*; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Enumeration; import org.opends.server.types.OperatingSystem; import org.opends.messages.Message; import static org.opends.messages.UtilityMessages.*; /** * This class provides an interface for generating self-signed certificates and * certificate signing requests, and for importing, exporting, and deleting * certificates from a key store. It supports JKS, JCEKS PKCS11, and PKCS12 key * store types. * certificates from a key store. It supports JKS, PKCS11, and PKCS12 key store * types. * <BR><BR> * Note that for some operations, particularly those that require updating the * contents of a key store (including generating certificates and/or certificate * signing requests, importing certificates, or removing certificates), this * class relies on the keytool utility provided with Sun's implementation of the * Java runtime environment. It will perform the associated operations by * invoking the appropriate command. It is possible that the keytool command * will not exist in all Java runtime environments, especially those not created * by Sun. In those cases, it will not be possible to invoke operations that * require altering the contents of the key store. Therefore, it is strongly * recommended that any code that may want to make use of this facility should * first call {@code mayUseCertificateManager} and if it returns {@code false} * the caller should gracefully degrade and suggest that the user perform the * operation manually. This code uses the Platform class to perform all of the certificate management. */ @org.opends.server.types.PublicAPI( stability=org.opends.server.types.StabilityLevel.VOLATILE, mayInstantiate=true, mayExtend=false, mayInvoke=true) public final class CertificateManager { /** * The path to the keytool command, which will be required to perform * operations that modify the contents of a key store. */ public static final String KEYTOOL_COMMAND; public final class CertificateManager { /** * The key store type value that should be used for the "JKS" key store. @@ -93,85 +67,43 @@ */ public static final String KEY_STORE_TYPE_PKCS11 = "PKCS11"; /** * The key store type value that should be used for the "PKCS12" key store. */ public static final String KEY_STORE_TYPE_PKCS12 = "PKCS12"; /** * The key store path value that must be used in conjunction with the PKCS11 * key store type. */ public static final String KEY_STORE_PATH_PKCS11 = "NONE"; //Error message strings. private static final String KEYSTORE_PATH_MSG = "key store path"; private static final String KEYSTORE_TYPE_MSG = "key store type"; private static final String KEYSTORE_PWD_MSG = "key store password"; private static final String SUBJECT_DN_MSG = "subject DN"; private static final String CERT_ALIAS_MSG = "certificate alias"; private static final String CERT_REQUEST_FILE_MSG = "certificate request file"; // The parsed key store backing this certificate manager. private KeyStore keyStore; // The password that should be used to interact with the key store. private String keyStorePIN; // The path to the key store that we should be using. private String keyStorePath; private final String keyStorePath; // The name of the key store type we are using. private String keyStoreType; private final String keyStoreType; static { String keytoolCommand = null; try { String cmd = System.getProperty("java.home") + File.separator + "bin" + File.separator + "keytool"; File cmdFile = new File(cmd); if (cmdFile.exists()) { keytoolCommand = cmdFile.getAbsolutePath(); } else { cmd = cmd + ".exe"; cmdFile = new File(cmd); if (cmdFile.exists()) { keytoolCommand = cmdFile.getAbsolutePath(); } else { keytoolCommand = null; } } } catch (Exception e) { keytoolCommand = null; } KEYTOOL_COMMAND = SetupUtils.getScriptPath(keytoolCommand); } private final char[] password; /** * Indicates whether it is possible to use this certificate manager code to * perform operations which may alter the contents of a key store. * Always return true. * * @return {@code true} if it appears that the keytool utility is available * and may be used to execute commands that may alter the contents of * a key store, or {@code false} if not. * @return This always returns true; */ public static boolean mayUseCertificateManager() { return (KEYTOOL_COMMAND != null); public static boolean mayUseCertificateManager() { return true; } @@ -192,89 +124,49 @@ * {@code KEY_STORE_TYPE_JCEKS}, * {@code KEY_STORE_TYPE_PKCS11}, or * {@code KEY_STORE_TYPE_PKCS12}. * @param keyStorePIN The PIN required to access the key store. It must * not be {@code null}. * @param keyStorePassword The password required to access the key store. * It must not be {@code null}. * @throws IllegalArgumentException If an argument is invalid or {@code null}. * * @throws IllegalArgumentException If any of the provided arguments is * invalid. * * @throws NullPointerException If any of the provided arguments is * {@code null}. * * @throws UnsupportedOperationException If it is not possible to use the * certificate manager on the * underlying platform. */ public CertificateManager(String keyStorePath, String keyStoreType, String keyStorePIN) throws IllegalArgumentException, NullPointerException, UnsupportedOperationException { if ((keyStorePath == null) || (keyStorePath.length() == 0)) { throw new NullPointerException("keyStorePath"); } else if ((keyStoreType == null) || (keyStoreType.length() == 0)) { throw new NullPointerException("keyStoreType"); } else if ((keyStorePIN == null) || (keyStorePIN.length() == 0)) { throw new NullPointerException("keyStorePIN"); } if (keyStoreType.equals(KEY_STORE_TYPE_PKCS11)) { if (! keyStorePath.equals(KEY_STORE_PATH_PKCS11)) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("Invalid key store path for " + "PKCS11 keystore -- it must be " + KEY_STORE_PATH_PKCS11); String keyStorePassword) throws IllegalArgumentException { ensureValid(keyStorePath, KEYSTORE_PATH_MSG); ensureValid(keyStoreType, KEYSTORE_TYPE_MSG); ensureValid(keyStorePassword, KEYSTORE_PWD_MSG); if (keyStoreType.equals(KEY_STORE_TYPE_PKCS11)) { if (! keyStorePath.equals(KEY_STORE_PATH_PKCS11)) { Message msg = ERR_CERTMGR_INVALID_PKCS11_PATH.get(KEY_STORE_PATH_PKCS11); throw new IllegalArgumentException(msg.toString()); } } else if (keyStoreType.equals(KEY_STORE_TYPE_JKS) || } else if (keyStoreType.equals(KEY_STORE_TYPE_JKS) || keyStoreType.equals(KEY_STORE_TYPE_JCEKS) || keyStoreType.equals(KEY_STORE_TYPE_PKCS12)) { keyStoreType.equals(KEY_STORE_TYPE_PKCS12)) { File keyStoreFile = new File(keyStorePath); if (keyStoreFile.exists()) { if (! keyStoreFile.isFile()) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("Key store path " + keyStorePath + " exists but is not a file."); if (keyStoreFile.exists()) { if (! keyStoreFile.isFile()) { Message msg = ERR_CERTMGR_INVALID_KEYSTORE_PATH.get(keyStorePath); throw new IllegalArgumentException(msg.toString()); } } else { File keyStoreDirectory = keyStoreFile.getParentFile(); } else { final File keyStoreDirectory = keyStoreFile.getParentFile(); if ((keyStoreDirectory == null) || (! keyStoreDirectory.exists()) || (! keyStoreDirectory.isDirectory())) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("Parent directory for key " + "store path " + keyStorePath + " does not exist or " + "is not a directory."); (! keyStoreDirectory.isDirectory())) { Message msg = ERR_CERTMGR_INVALID_PARENT.get(keyStorePath); throw new IllegalArgumentException(msg.toString()); } } } else { Message msg = ERR_CERTMGR_INVALID_STORETYPE.get( KEY_STORE_TYPE_JKS, KEY_STORE_TYPE_JCEKS, KEY_STORE_TYPE_PKCS11, KEY_STORE_TYPE_PKCS12); throw new IllegalArgumentException(msg.toString()); } else { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("Invalid key store type -- it must " + "be one of " + KEY_STORE_TYPE_JKS + ", " + "be one of " + KEY_STORE_TYPE_JCEKS + ", " + KEY_STORE_TYPE_PKCS11 + ", or " + KEY_STORE_TYPE_PKCS12); } this.keyStorePath = keyStorePath; this.keyStoreType = keyStoreType; this.keyStorePIN = keyStorePIN; this.password = keyStorePassword.toCharArray(); keyStore = null; } @@ -291,25 +183,13 @@ * * @throws KeyStoreException If a problem occurs while attempting to * interact with the key store. * * @throws NullPointerException If the provided alias is {@code null} or a * zero-length string. */ public boolean aliasInUse(String alias) throws KeyStoreException, NullPointerException { if ((alias == null) || (alias.length() == 0)) { throw new NullPointerException("alias"); } public boolean aliasInUse(final String alias) throws KeyStoreException { ensureValid(alias, CERT_ALIAS_MSG); KeyStore keyStore = getKeyStore(); if (keyStore == null) { return false; } return keyStore.containsAlias(alias); } @@ -324,28 +204,17 @@ * @throws KeyStoreException If a problem occurs while attempting to * interact with the key store. */ public String[] getCertificateAliases() throws KeyStoreException { public String[] getCertificateAliases() throws KeyStoreException { Enumeration<String> aliasEnumeration = null; KeyStore keyStore = getKeyStore(); if (keyStore == null) { return null; } Enumeration<String> aliasEnumeration = keyStore.aliases(); aliasEnumeration = keyStore.aliases(); if (aliasEnumeration == null) { return new String[0]; } ArrayList<String> aliasList = new ArrayList<String>(); while (aliasEnumeration.hasMoreElements()) { aliasList.add(aliasEnumeration.nextElement()); } String[] aliases = new String[aliasList.size()]; return aliasList.toArray(aliases); } @@ -362,31 +231,22 @@ * certificate does not exist. * * @throws KeyStoreException If a problem occurs while interacting with the * key store, or the key store does not exist. * * @throws NullPointerException If the provided alias is {@code null} or a * zero-length string. * key store, or the key store does not exist.. */ public Certificate getCertificate(String alias) throws KeyStoreException, NullPointerException { if ((alias == null) || (alias.length() == 0)) { throw new NullPointerException("alias"); public Certificate getCertificate(String alias) throws KeyStoreException { ensureValid(alias, CERT_ALIAS_MSG); Certificate cert = null; KeyStore ks = getKeyStore(); if (ks == null) { Message msg = ERR_CERTMGR_KEYSTORE_NONEXISTANT.get(); throw new KeyStoreException(msg.toString()); } KeyStore keyStore = getKeyStore(); if (keyStore == null) { // FIXME -- Make this an internationalizeable string. throw new KeyStoreException("The key store does not exist."); } return keyStore.getCertificate(alias); cert = ks.getCertificate(alias); return cert; } /** * Generates a self-signed certificate using the provided information. * @@ -398,89 +258,30 @@ * @param validity The length of time in days that the certificate should * be valid, starting from the time the certificate is * generated. It must be a positive integer value. * * @throws IllegalArgumentException If the validity is not positive. * * @throws KeyStoreException If a problem occurs while actually attempting * to generate the certificate in the key store. * * @throws NullPointerException If either the alias or subject DN is null or * a zero-length string. * * @throws UnsupportedOperationException If it is not possible to use the * keytool utility to alter the * contents of the key store. *@throws IllegalArgumentException If the validity parameter is not a * positive integer, or the alias is already * in the keystore. */ public void generateSelfSignedCertificate(String alias, String subjectDN, int validity) throws KeyStoreException, IllegalArgumentException, NullPointerException, UnsupportedOperationException { if ((alias == null) || (alias.length() == 0)) { throw new NullPointerException("alias"); throws KeyStoreException, IllegalArgumentException { ensureValid(alias, CERT_ALIAS_MSG); ensureValid(subjectDN, SUBJECT_DN_MSG); if (validity <= 0) { Message msg = ERR_CERTMGR_VALIDITY.get(validity); throw new IllegalArgumentException(msg.toString()); } else if ((subjectDN == null) || (subjectDN.length() == 0)) { throw new NullPointerException("subjectDN"); if (aliasInUse(alias)) { Message msg = ERR_CERTMGR_ALIAS_ALREADY_EXISTS.get(alias); throw new IllegalArgumentException(msg.toString()); } else if (validity <= 0) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("The validity must be positive."); } if (KEYTOOL_COMMAND == null) { // FIXME -- Make this an internationalizeable string. throw new UnsupportedOperationException("The certificate manager may " + "not be used to alter the contents of key stores on " + "this system."); } if (aliasInUse(alias)) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("A certificate with alias " + alias + " already exists in the key store."); } // Clear the reference to the key store, since it will be altered by // invoking the KeyTool command. keyStore = null; // First, we need to run with the "-genkey" command to create the private // key. String[] commandElements = { KEYTOOL_COMMAND, getGenKeyCommand(), "-alias", alias, "-dname", subjectDN, "-keyalg", "rsa", "-keystore", keyStorePath, "-storetype", keyStoreType }; runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); // Next, we need to run with the "-selfcert" command to self-sign the // certificate. commandElements = new String[] { KEYTOOL_COMMAND, "-selfcert", "-alias", alias, "-sigalg", "SHA256withRSA", "-validity", String.valueOf(validity), "-keystore", keyStorePath, "-storetype", keyStoreType }; runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); Platform.generateSelfSignedCertificate(getKeyStore(), keyStoreType, keyStorePath, alias, password, subjectDN, validity); } /** * Generates a certificate signing request (CSR) using the provided * information. @@ -497,82 +298,21 @@ * to generate the private key in the key store or * generate the certificate signing request based * on that key. * * @throws IOException If a problem occurs while attempting to create the * file to which the certificate signing request will be * written. * * @throws NullPointerException If either the alias or subject DN is null or * a zero-length string. * * @throws UnsupportedOperationException If it is not possible to use the * keytool utility to alter the * contents of the key store. *@throws IllegalArgumentException If the alias already exists in the * keystore. */ public File generateCertificateSigningRequest(String alias, String subjectDN) throws KeyStoreException, IOException, NullPointerException, UnsupportedOperationException { if ((alias == null) || (alias.length() == 0)) { throw new NullPointerException("alias"); public File generateCertificateSigningRequest(final String alias, final String subjectDN) throws KeyStoreException, IllegalArgumentException { ensureValid(alias, CERT_ALIAS_MSG); ensureValid(subjectDN, SUBJECT_DN_MSG); if (aliasInUse(alias)) { Message msg = ERR_CERTMGR_ALIAS_ALREADY_EXISTS.get(alias); throw new IllegalArgumentException(msg.toString()); } else if ((subjectDN == null) || (subjectDN.length() == 0)) { throw new NullPointerException("subjectDN"); } if (KEYTOOL_COMMAND == null) { // FIXME -- Make this an internationalizeable string. throw new UnsupportedOperationException("The certificate manager may " + "not be used to alter the contents of key stores on " + "this system."); } if (aliasInUse(alias)) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("A certificate with alias " + alias + " already exists in the key store."); } // Clear the reference to the key store, since it will be altered by // invoking the KeyTool command. keyStore = null; // First, we need to run with the "-genkey" command to create the private // key. String[] commandElements = { KEYTOOL_COMMAND, getGenKeyCommand(), "-alias", alias, "-dname", subjectDN, "-keyalg", "rsa", "-keystore", keyStorePath, "-storetype", keyStoreType }; runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); // Next, we need to run with the "-certreq" command to generate the // certificate signing request. File csrFile = File.createTempFile("CertificateManager-", ".csr"); csrFile.deleteOnExit(); commandElements = new String[] { KEYTOOL_COMMAND, "-certreq", "-alias", alias, "-file", csrFile.getAbsolutePath(), "-keystore", keyStorePath, "-storetype", keyStoreType }; runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); return csrFile; return Platform.generateCertificateRequest(getKeyStore(), keyStoreType, keyStorePath, alias, password, subjectDN); } @@ -586,68 +326,25 @@ * be {@code null} or empty. * @param certificateFile The file containing the encoded certificate. It * must not be {@code null}, and the file must exist. * * @throws IllegalArgumentException If the provided certificate file does * not exist. * * @throws KeyStoreException If a problem occurs while interacting with the * key store. * * @throws NullPointerException If the provided alias is {@code null} or a * zero-length string, or the certificate file * is {@code null}. * * @throws UnsupportedOperationException If it is not possible to use the * keytool utility to alter the * contents of the key store. *@throws IllegalArgumentException If the certificate file is not valid. */ public void addCertificate(String alias, File certificateFile) throws IllegalArgumentException, KeyStoreException, NullPointerException, UnsupportedOperationException { if ((alias == null) || (alias.length() == 0)) { throw new NullPointerException("alias"); throws KeyStoreException, IllegalArgumentException { ensureValid(alias, CERT_ALIAS_MSG); ensureFileValid(certificateFile, CERT_REQUEST_FILE_MSG); if ((! certificateFile.exists()) || (! certificateFile.isFile())) { Message msg = ERR_CERTMGR_INVALID_CERT_FILE.get( certificateFile.getAbsolutePath()); throw new IllegalArgumentException(msg.toString()); } if (certificateFile == null) { throw new NullPointerException("certificateFile"); } else if ((! certificateFile.exists()) || (! certificateFile.isFile())) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("Certificate file " + certificateFile.getAbsolutePath() + " does not exist or is not a file."); } if (KEYTOOL_COMMAND == null) { // FIXME -- Make this an internationalizeable string. throw new UnsupportedOperationException("The certificate manager may " + "not be used to alter the contents of key stores on " + "this system."); } // Clear the reference to the key store, since it will be altered by // invoking the KeyTool command. keyStore = null; String[] commandElements = { KEYTOOL_COMMAND, "-import", "-noprompt", "-alias", alias, "-file", certificateFile.getAbsolutePath(), "-keystore", keyStorePath, "-storetype", keyStoreType }; runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); Platform.addCertificate(getKeyStore(), keyStoreType, keyStorePath, alias, password, certificateFile.getAbsolutePath()); } @@ -658,226 +355,23 @@ * be {@code null} or an empty string, and it must exist in * the key store. * * @throws IllegalArgumentException If the specified certificate does not * exist in the key store. * * @throws KeyStoreException If a problem occurs while interacting with the * key store. * * @throws NullPointerException If the provided alias is {@code null} or a * zero-length string, or the certificate file * is {@code null}. * * @throws UnsupportedOperationException If it is not possible to use the * keytool utility to alter the * contents of the key store. *@throws IllegalArgumentException If the alias is in use and cannot be * deleted. */ public void removeCertificate(String alias) throws IllegalArgumentException, KeyStoreException, NullPointerException, UnsupportedOperationException { if ((alias == null) || (alias.length() == 0)) { throw new NullPointerException("alias"); throws KeyStoreException, IllegalArgumentException { ensureValid(alias, CERT_ALIAS_MSG); if (!aliasInUse(alias)) { Message msg = ERR_CERTMGR_ALIAS_CAN_NOT_DELETE.get(alias); throw new IllegalArgumentException(msg.toString()); } if (KEYTOOL_COMMAND == null) { // FIXME -- Make this an internationalizeable string. throw new UnsupportedOperationException("The certificate manager may " + "not be used to alter the contents of key stores on " + "this system."); } if (! aliasInUse(alias)) { // FIXME -- Make this an internationalizeable string. throw new IllegalArgumentException("There is no certificate with alias " + alias + " in the key store."); } // Clear the reference to the key store, since it will be altered by // invoking the KeyTool command. keyStore = null; String[] commandElements = { KEYTOOL_COMMAND, "-delete", "-alias", alias, "-keystore", keyStorePath, "-storetype", keyStoreType }; runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); Platform.deleteAlias(getKeyStore(), keyStorePath, alias, password); } /** * Attempts to run the keytool utility with the provided arguments. * * @param commandElements The command and arguments to execute. The first * element of the array must be the command, and the * remaining elements must be the arguments. * @param keyStorePassword The password of the key store. * @param storePassword The password of the certificate. * @param outputAcceptable Indicates whether it is acceptable for the * command to generate output, as long as the exit * code is zero. Some commands (like "keytool * -import") may generate output even on successful * completion. If the command generates output and * this is {@code false}, then an exception will * be thrown. * * @throws KeyStoreException If a problem occurs while attempting to invoke * the keytool utility, if it does not exit with * the expected exit code, or if any unexpected * output is generated while running the tool. */ private void runKeyTool(String[] commandElements, String keyStorePassword, String storePassword, boolean outputAcceptable) throws KeyStoreException { String lineSeparator = System.getProperty("line.separator"); if (lineSeparator == null) { lineSeparator = "\n"; } boolean keyStoreDefined; File keyStoreFile = new File(keyStorePath); keyStoreDefined = (keyStoreFile.exists() && (keyStoreFile.length() > 0)) || KEY_STORE_TYPE_PKCS11.equals(keyStoreType); boolean isNewKeyStorePassword = !keyStoreDefined && (getGenKeyCommand().equalsIgnoreCase(commandElements[1]) || "-import".equalsIgnoreCase(commandElements[1])); boolean isNewStorePassword = getGenKeyCommand().equalsIgnoreCase(commandElements[1]); boolean askForStorePassword = !"-import".equalsIgnoreCase(commandElements[1]); try { ProcessBuilder processBuilder = new ProcessBuilder(commandElements); processBuilder.redirectErrorStream(true); ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; Process process = processBuilder.start(); InputStream inputStream = process.getInputStream(); OutputStream out = process.getOutputStream(); if (!isJDK15() && (SetupUtils.getOperatingSystem() == OperatingSystem.AIX)) { // This is required when using JDK 1.6 on AIX to be able to write // on the OutputStream. try { Thread.sleep(1500); } catch (Throwable t) {} } out.write(keyStorePassword.getBytes()) ; out.write(lineSeparator.getBytes()) ; out.flush() ; // With Java6 and above, keytool asks for the password twice. if (!isJDK15() && isNewKeyStorePassword) { if (SetupUtils.getOperatingSystem() == OperatingSystem.AIX) { // This is required when using JDK 1.6 on AIX to be able to write // on the OutputStream. try { Thread.sleep(1500); } catch (Throwable t) {} } out.write(keyStorePassword.getBytes()) ; out.write(lineSeparator.getBytes()) ; out.flush() ; } if (askForStorePassword) { out.write(storePassword.getBytes()) ; out.write(lineSeparator.getBytes()) ; out.flush() ; // With Java6 and above, keytool asks for the password twice (if we // are not running AIX). if (!isJDK15() && isNewStorePassword && (SetupUtils.getOperatingSystem() != OperatingSystem.AIX)) { out.write(storePassword.getBytes()) ; out.write(lineSeparator.getBytes()) ; out.flush() ; } } // Close the output stream since it can generate a deadlock on IBM JVM // (issue 2795). out.close(); while (true) { int bytesRead = inputStream.read(buffer); if (bytesRead < 0) { break; } else if (bytesRead > 0) { output.write(buffer, 0, bytesRead); } } process.waitFor(); int exitValue = process.exitValue(); byte[] outputBytes = output.toByteArray(); if (exitValue != 0) { // FIXME -- Make this an internationalizeable string. StringBuilder message = new StringBuilder(); message.append("Unexpected exit code of "); message.append(exitValue); message.append(" returned from the keytool utility."); if ((outputBytes != null) && (outputBytes.length > 0)) { message.append(" The generated output was: '"); message.append(new String(outputBytes)); message.append("'."); } throw new KeyStoreException(message.toString()); } else if ((! outputAcceptable) && (outputBytes != null) && (outputBytes.length > 0)) { // FIXME -- Make this an internationalizeable string. StringBuilder message = new StringBuilder(); message.append("Unexpected output generated by the keytool " + "utility: '"); message.append(new String(outputBytes)); message.append("'."); throw new KeyStoreException(message.toString()); } } catch (KeyStoreException kse) { throw kse; } catch (Exception e) { // FIXME -- Make this an internationalizeable string. throw new KeyStoreException("Could not invoke the KeyTool.run method: " + e, e); } } /** * Retrieves a handle to the key store. * @@ -888,97 +382,74 @@ * key store. */ private KeyStore getKeyStore() throws KeyStoreException throws KeyStoreException { if (keyStore != null) { return keyStore; } // For JKS, JCEKS and PKCS12 key stores, we should make sure the file // exists, and we'll need an input stream that we can use to read it. // For PKCS11 key stores there won't be a file and the input stream should // be null. FileInputStream keyStoreInputStream = null; if (keyStoreType.equals(KEY_STORE_TYPE_JKS) || keyStoreType.equals(KEY_STORE_TYPE_JCEKS) || keyStoreType.equals(KEY_STORE_TYPE_PKCS12)) { File keyStoreFile = new File(keyStorePath); if (! keyStoreFile.exists()) if (keyStore != null) { return null; return keyStore; } // For JKS and PKCS12 key stores, we should make sure the file exists, and // we'll need an input stream that we can use to read it. For PKCS11 key // stores there won't be a file and the input stream should be null. FileInputStream keyStoreInputStream = null; if (keyStoreType.equals(KEY_STORE_TYPE_JKS) || keyStoreType.equals(KEY_STORE_TYPE_JCEKS) || keyStoreType.equals(KEY_STORE_TYPE_PKCS12)) { final File keyStoreFile = new File(keyStorePath); if (! keyStoreFile.exists()) { return null; } try { keyStoreInputStream = new FileInputStream(keyStoreFile); } catch (final Exception e) { throw new KeyStoreException(String.valueOf(e), e); } } final KeyStore keyStore = KeyStore.getInstance(keyStoreType); try { keyStoreInputStream = new FileInputStream(keyStoreFile); keyStore.load(keyStoreInputStream, password); return this.keyStore = keyStore; } catch (Exception e) catch (final Exception e) { throw new KeyStoreException(String.valueOf(e), e); throw new KeyStoreException(String.valueOf(e), e); } } KeyStore keyStore = KeyStore.getInstance(keyStoreType); try { keyStore.load(keyStoreInputStream, keyStorePIN.toCharArray()); return this.keyStore = keyStore; } catch (Exception e) { throw new KeyStoreException(String.valueOf(e), e); } finally { if (keyStoreInputStream != null) finally { try { keyStoreInputStream.close(); } catch (Throwable t) { } if (keyStoreInputStream != null) { try { keyStoreInputStream.close(); } catch (final Throwable t) { } } } } private static void ensureFileValid(File arg, String msgStr) { if(arg == null) { Message msg = ERR_CERTMGR_FILE_NAME_INVALID.get(msgStr); throw new NullPointerException(msg.toString()); } } /** * Returns whether we are running JDK 1.5 or not. * @return <CODE>true</CODE> if we are running JDK 1.5 and <CODE>false</CODE> * otherwise. */ private boolean isJDK15() { boolean isJDK15 = false; try { String javaRelease = System.getProperty ("java.version"); isJDK15 = javaRelease.startsWith("1.5"); private static void ensureValid(String arg, String msgStr) { if(arg == null || arg.length() == 0) { Message msg = ERR_CERTMGR_VALUE_INVALID.get(msgStr); throw new NullPointerException(msg.toString()); } catch (Throwable t) { System.err.println("Cannot get the java version: " + t); } return isJDK15; } private String getGenKeyCommand() { String genKeyCommand; if (!isJDK15()) { genKeyCommand = "-genkeypair"; } else { genKeyCommand = "-genkey"; } return genKeyCommand; } } opends/src/server/org/opends/server/util/Platform.java
New file @@ -0,0 +1,683 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2009 Sun Microsystems, Inc. */ package org.opends.server.util; import java.security.KeyStoreException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.opends.messages.Message; import static org.opends.messages.UtilityMessages.*; /** * Provides a wrapper class that collects all of the JVM vendor * and JDK version specific code in a single place. * */ public final class Platform { //Prefix that determines which security package to use. private static String pkgPrefix; //IBM security package doesn't appear to support PCKS10, this flags turns //off support for that. private static boolean certReqAllowed; //The two security package prefixes (IBM and SUN). private static final String IBM_SEC = "com.ibm.security"; private static final String SUN_SEC = "sun.security"; private static final PlatformIMPL IMPL; static { String vendor = System.getProperty("java.vendor"); String ver = System.getProperty("java.version"); if(vendor.startsWith("IBM")) { pkgPrefix = IBM_SEC; certReqAllowed = false; if(ver.startsWith("1.5")) { IMPL = new IBM5PlatformIMPL(); } else { IMPL = new DefaultPlatformIMPL(); } } else { pkgPrefix = SUN_SEC; certReqAllowed = true; if(ver.startsWith("1.5")) { IMPL = new Sun5PlatformIMPL(); } else { IMPL = new DefaultPlatformIMPL(); } } } /** * Platform base class. Performs all of the certificate management functions. */ private abstract static class PlatformIMPL { //Key size, key algorithm and signature algorithms used. private static final int KEY_SIZE = 1024; private static final String KEY_ALGORITHM = "rsa"; private static final String SIG_ALGORITHM = "SHA1WithRSA"; //Time values used in validity calculations. private static final int SEC_IN_DAY = 24 * 60 * 60; private static final int DEFAULT_VALIDITY = 90 * SEC_IN_DAY; //These two are used to build certificate request files. private static final String TMPFILE_PREFIX = "CertificateManager-"; private static final String TMPFILE_EXT = ".csr"; //Methods pulled from the classes. private static final String ENCODE_SIGN_METHOD = "encodeAndSign"; private static final String GENERATE_METHOD = "generate"; private static final String GET_PRIVATE_KEY_METHOD = "getPrivateKey"; private static final String GET_SELFSIGNED_CERT_METHOD = "getSelfCertificate"; private static final String PRINT_METHOD = "print"; //Classes needed to manage certificates. private static Class<?> certKeyGenClass, X500NameClass, X500SignerClass, PKCS10Class; //Constructors for each of the above classes. private static Constructor<?> certKeyGenCons, X500NameCons, X500SignerCons, pkcs10Cons; static { String x509pkg = pkgPrefix + ".x509"; String pkcs10Pkg = pkgPrefix + ".pkcs"; String certAndKeyGen= x509pkg + ".CertAndKeyGen"; String X500Name = x509pkg + ".X500Name"; String X500Signer = x509pkg + ".X500Signer"; try { certKeyGenClass = Class.forName(certAndKeyGen); X500NameClass = Class.forName(X500Name); X500SignerClass = Class.forName(X500Signer); if(certReqAllowed) { String pkcs10 = pkcs10Pkg + ".PKCS10"; PKCS10Class = Class.forName(pkcs10); pkcs10Cons = PKCS10Class.getConstructor(PublicKey.class); } certKeyGenCons = certKeyGenClass.getConstructor(String.class, String.class); X500NameCons = X500NameClass.getConstructor(String.class); X500SignerCons = X500SignerClass.getConstructor(Signature.class, X500NameClass); } catch (ClassNotFoundException e) { Message msg = ERR_CERTMGR_CLASS_NOT_FOUND.get(e.getMessage()); throw new ExceptionInInitializerError(msg.toString()); } catch (SecurityException e) { Message msg = ERR_CERTMGR_SECURITY.get(e.getMessage()); throw new ExceptionInInitializerError(msg.toString()); } catch (NoSuchMethodException e) { Message msg = ERR_CERTMGR_NO_METHOD.get(e.getMessage()); throw new ExceptionInInitializerError(msg.toString()); } } protected PlatformIMPL() {} /** * Generate a certificate request. Note that this methods checks if * the certificate request generation is allowed and throws an * exception if it isn't supported. Some vendors JDKs aren't compatible * with Sun's certificate request generation classes so they aren't * supported. * * @param ks The keystore to use in the request creation. * @param ksType The keystore type. * @param ksPath The path to the keystore. * @param alias The alias to use in the request generation. * @param pwd The keystore password to use. * @param dn A dn string to use as the certificate subject. * * @return A file object pointing at the created certificate request. * @throws KeyStoreException If the certificate request failed. */ public final File generateCertificateRequest(KeyStore ks, String ksType, String ksPath, String alias, char[] pwd, String dn) throws KeyStoreException { if(!certReqAllowed) { String vendor = System.getProperty("java.vendor"); Message msg = ERR_CERTMGR_CERT_SIGN_REQ_NOT_SUPPORTED.get(vendor); throw new KeyStoreException(msg.toString()); } KeyStore keyStore = generateSelfSignedCertificate(ks, ksType, ksPath, alias, pwd, dn, DEFAULT_VALIDITY); File csrFile; try { csrFile = File.createTempFile(TMPFILE_PREFIX, TMPFILE_EXT); csrFile.deleteOnExit(); PrintStream printStream = new PrintStream(new FileOutputStream(csrFile.getAbsolutePath())); if(keyStore == null) { Message msg = ERR_CERTMGR_KEYSTORE_NONEXISTANT.get(); throw new KeyStoreException(msg.toString()); } PrivateKey privateKey = getPrivateKey(keyStore, alias, pwd); if(privateKey == null) { Message msg = ERR_CERTMGR_PRIVATE_KEY.get(alias); throw new KeyStoreException(msg.toString()); } Certificate cert = keyStore.getCertificate(alias); if(cert == null) { Message msg = ERR_CERTMGR_ALIAS_NO_CERTIFICATE.get(alias); throw new KeyStoreException(msg.toString()); } Signature signature = Signature.getInstance(SIG_ALGORITHM); signature.initSign(privateKey); Object request = pkcs10Cons.newInstance(cert.getPublicKey()); Object subject = X500NameCons.newInstance(dn); Object signer = X500SignerCons.newInstance(signature, subject); Method encodeAndSign = PKCS10Class.getMethod(ENCODE_SIGN_METHOD, X500SignerClass); Method print = PKCS10Class.getMethod(PRINT_METHOD, PrintStream.class); encodeAndSign.invoke(request, signer); print.invoke(request, printStream); printStream.close(); } catch (Exception e) { Message msg = ERR_CERTMGR_CERT_REQUEST.get(alias,e.getMessage()); throw new KeyStoreException(msg.toString()); } return csrFile; } /** * Delete the specified alias from the specified keystore. * * @param ks The keystore to delete the alias from. * @param ksPath The path to the keystore. * @param alias The alias to use in the request generation. * @param pwd The keystore password to use. * * @throws KeyStoreException If an error occurred deleting the alias. */ public final void deleteAlias(KeyStore ks, String ksPath, String alias, char[] pwd) throws KeyStoreException { try { if(ks == null) { Message msg = ERR_CERTMGR_KEYSTORE_NONEXISTANT.get(); throw new KeyStoreException(msg.toString()); } ks.deleteEntry(alias); FileOutputStream fs = new FileOutputStream(ksPath); ks.store(fs, pwd); fs.close(); } catch (Exception e) { Message msg = ERR_CERTMGR_DELETE_ALIAS.get(alias,e.getMessage()); throw new KeyStoreException(msg.toString()); } } /** * Add the certificate in the specified path to the specified keystore, * creating the keystore using the specified type and path if it the * keystore doesn't exist. * * @param ks The keystore to add the certificate to, may be null if it * doesn't exist. * @param ksType The type to use if the keystore is created. * @param ksPath The path to the keystore if it is created. * @param alias The alias to store the certificate under. * @param pwd The password to use in saving the certificate. * @param certPath The path to the file containing the certificate. * @throws KeyStoreException If an error occurred adding the * certificate to the keystore. */ public final void addCertificate(KeyStore ks, String ksType, String ksPath, String alias, char[] pwd, String certPath) throws KeyStoreException { try { CertificateFactory cf = CertificateFactory.getInstance("X509"); InputStream inStream = new FileInputStream(certPath); if(ks == null) { ks = KeyStore.getInstance(ksType); ks.load(null, pwd); } //Do not support certificate replies. if (ks.entryInstanceOf(alias ,KeyStore.PrivateKeyEntry.class)) { Message msg = ERR_CERTMGR_CERT_REPLIES_INVALID.get(alias); throw new KeyStoreException(msg.toString()); } else if(!ks.containsAlias(alias) || ks.entryInstanceOf(alias, KeyStore.TrustedCertificateEntry.class)) trustedCert(alias, cf, ks, inStream); else { Message msg = ERR_CERTMGR_ALIAS_INVALID.get(alias); throw new KeyStoreException(msg.toString()); } FileOutputStream fileOutStream = new FileOutputStream(ksPath); ks.store(fileOutStream, pwd); fileOutStream.close(); inStream.close(); } catch (Exception e) { Message msg = ERR_CERTMGR_ADD_CERT.get(alias, e.getMessage()); throw new KeyStoreException(msg.toString()); } } /** * Generate a self-signed certificate using the specified alias, dn * string and validity period. If the keystore does not exist, create it * using the specified type and path. * * @param ks The keystore to save the certificate in. May be null if it * does not exist. * @param ksType The keystore type to use if the keystore is created. * @param ksPath The path to the keystore if the keystore is created. * @param alias The alias to store the certificate under. * @param pwd The password to us in saving the certificate. * @param dn The dn string used as the certificate subject. * @param validity The validity of the certificate in days. * @return The keystore that the self-signed certificate was stored in. * * @throws KeyStoreException If the self-signed certificate cannot be * generated. */ public final KeyStore generateSelfSignedCertificate(KeyStore ks, String ksType, String ksPath, String alias, char[] pwd, String dn, int validity) throws KeyStoreException { try { if(ks == null) { ks = KeyStore.getInstance(ksType); ks.load(null, pwd); } else if(ks.containsAlias(alias)) { Message msg = ERR_CERTMGR_ALIAS_ALREADY_EXISTS.get(alias); throw new KeyStoreException(msg.toString()); } Object keypair = certKeyGenCons.newInstance(KEY_ALGORITHM, SIG_ALGORITHM); Object subject = X500NameCons.newInstance(dn); Method certAndKeyGenGenerate = certKeyGenClass.getMethod(GENERATE_METHOD, int.class); certAndKeyGenGenerate.invoke(keypair, KEY_SIZE); Method certAndKeyGetPrivateKey = certKeyGenClass.getMethod(GET_PRIVATE_KEY_METHOD); PrivateKey privatevKey = (PrivateKey) certAndKeyGetPrivateKey.invoke(keypair); Certificate[] certificateChain = new Certificate[1]; Method getSelfCertificate = certKeyGenClass.getMethod(GET_SELFSIGNED_CERT_METHOD, X500NameClass,long.class); int days = validity * SEC_IN_DAY; certificateChain[0] = (Certificate) getSelfCertificate.invoke(keypair, subject, days); ks.setKeyEntry(alias, privatevKey, pwd, certificateChain); FileOutputStream fileOutStream = new FileOutputStream(ksPath); ks.store(fileOutStream, pwd); fileOutStream.close(); } catch (Exception e) { Message msg = ERR_CERTMGR_GEN_SELF_SIGNED_CERT.get(alias, e.getMessage()); throw new KeyStoreException(msg.toString()); } return ks; } /** * Generate a x509 certificate from the input stream. Verification is * done only if it is self-signed. * * @param alias The alias to save the certificate under. * @param cf The x509 certificate factory. * @param ks The keystore to add the certificate in. * @param in The input stream to read the certificate from. * @throws KeyStoreException If the alias exists already in the * keystore, if the self-signed certificate didn't verify, or * the certificate could not be stored. */ private void trustedCert(String alias, CertificateFactory cf, KeyStore ks, InputStream in) throws KeyStoreException { try { if (ks.containsAlias(alias) == true) { Message msg = ERR_CERTMGR_ALIAS_ALREADY_EXISTS.get(alias); throw new KeyStoreException(msg.toString()); } X509Certificate cert = (X509Certificate) cf.generateCertificate(in); if (isSelfSigned(cert)) cert.verify(cert.getPublicKey()); ks.setCertificateEntry(alias, cert); } catch (Exception e) { Message msg = ERR_CERTMGR_TRUSTED_CERT.get(alias,e.getMessage()); throw new KeyStoreException(msg.toString()); } } /** * Check that the issuer and subject DNs match. * * @param cert The certificate to examine. * @return {@code true} if the certificate is self-signed. */ private boolean isSelfSigned(X509Certificate cert) { return cert.getSubjectDN().equals(cert.getIssuerDN()); } /** * Returns the private key associated with specified alias and keystore. * The keystore was already checked for existance. * * @param ks The keystore to get the private key from, it must exist. * @param alias The alias to get the private key of. * @param pwd The password used to get the key from the keystore. * @return The private key of related to the alias. * * @throws KeyStoreException If the alias is not in the keystore, the * entry related to the alias is not of */ private PrivateKey getPrivateKey(KeyStore ks, String alias, char[] pwd) throws KeyStoreException { PrivateKey key = null; try { if(!ks.containsAlias(alias)) { Message msg = ERR_CERTMGR_ALIAS_DOES_NOT_EXIST.get(alias); throw new KeyStoreException(msg.toString()); } if(!ks.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) && !ks.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { Message msg = ERR_CERTMGR_ALIAS_INVALID_ENTRY_TYPE.get(alias); throw new KeyStoreException(msg.toString()); } key = (PrivateKey)ks.getKey(alias, pwd); } catch (Exception e) { Message msg = ERR_CERTMGR_GET_KEY.get(alias,e.getMessage()); throw new KeyStoreException(msg.toString()); } return key; } /** * Normalize the data in the specified buffer. * * @param buffer The buffer to normalize. */ public abstract void normalize(StringBuilder buffer); } //Prevent instantiation. private Platform() {} /** * Add the certificate in the specified path to the provided keystore; * creating the keystore with the provided type and path if it doesn't * exist. * * @param ks The keystore to add the certificate to, may be null if it * doesn't exist. * @param ksType The type to use if the keystore is created. * @param ksPath The path to the keystore if it is created. * @param alias The alias to store the certificate under. * @param pwd The password to use in saving the certificate. * @param certPath The path to the file containing the certificate. * * @throws KeyStoreException If an error occurred adding the * certificate to the keystore. */ public static void addCertificate(KeyStore ks, String ksType, String ksPath, String alias, char[] pwd, String certPath) throws KeyStoreException { IMPL.addCertificate(ks,ksType, ksPath, alias, pwd, certPath); } /** * Delete the specified alias from the provided keystore. * * @param ks The keystore to delete the alias from. * @param ksPath The path to the keystore. * @param alias The alias to use in the request generation. * @param pwd The keystore password to use. * * @throws KeyStoreException If an error occurred deleting the alias. */ public static void deleteAlias(KeyStore ks, String ksPath, String alias, char[] pwd) throws KeyStoreException { IMPL.deleteAlias(ks, ksPath, alias, pwd); } /** * Generate a certificate request using the specified parameters. * * @param ks The keystore to use in the request creation. * @param ksType The keystore type. * @param ksPath The path to the keystore. * @param alias The alias to use in the request generation. * @param pwd The keystore password to use. * @param dn A dn string to use as the certificate subject. * @return A file object pointing at the created certificate request. * * @throws KeyStoreException If the certificate request failed. */ public static File generateCertificateRequest(KeyStore ks, String ksType, String ksPath, String alias, char[] pwd, String dn) throws KeyStoreException { return IMPL.generateCertificateRequest(ks, ksType, ksPath, alias, pwd, dn); } /** * Generate a self-signed certificate using the specified alias, dn * string and validity period. If the keystore does not exist, it will be * created using the specified keystore type and path. * * @param ks The keystore to save the certificate in. May be null if it * does not exist. * @param ksType The keystore type to use if the keystore is created. * @param ksPath The path to the keystore if the keystore is created. * @param alias The alias to store the certificate under. * @param pwd The password to us in saving the certificate. * @param dn The dn string used as the certificate subject. * @param validity The validity of the certificate in days. * * @throws KeyStoreException If the self-signed certificate cannot be * generated. */ public static void generateSelfSignedCertificate(KeyStore ks, String ksType, String ksPath, String alias, char[] pwd, String dn, int validity) throws KeyStoreException { IMPL.generateSelfSignedCertificate(ks, ksType, ksPath, alias, pwd, dn, validity); } /** * Sun 5 JDK platform class. */ private static class Sun5PlatformIMPL extends PlatformIMPL { //normalize method. private static final Method NORMALIZE; //Normalized form method. private static final Object FORM_NFKC; static { Method normalize = null; Object formNFKC = null; try { Class<?> normalizer = Class.forName("sun.text.Normalizer"); formNFKC = normalizer.getField("DECOMP_COMPAT").get(null); Class<?> normalizerForm = Class.forName("sun.text.Normalizer$Mode"); normalize = normalizer.getMethod("normalize", String.class, normalizerForm, Integer.TYPE); } catch (Exception ex) { // Do not use Normalizer. The values are already set to null. } NORMALIZE = normalize; FORM_NFKC = formNFKC; } private Sun5PlatformIMPL() { super(); } @Override public void normalize(StringBuilder buffer) { try { String normal = (String) NORMALIZE.invoke(null, buffer.toString(), FORM_NFKC,0); buffer.replace(0,buffer.length(),normal); } catch(Exception ex) { //Don't do anything. buffer should be used. } } } /** * Default platform class. */ private static class DefaultPlatformIMPL extends PlatformIMPL { //normalize method. private static final Method NORMALIZE; //Normalized form method. private static final Object FORM_NFKC; static { Method normalize = null; Object formNFKC = null; try { Class<?> normalizer = Class.forName("java.text.Normalizer"); Class<?> normalizerForm = Class.forName("java.text.Normalizer$Form"); normalize = normalizer.getMethod("normalize", CharSequence.class, normalizerForm); formNFKC = normalizerForm.getField("NFKD").get(null); } catch (Exception ex) { // Do not use Normalizer. The values are already set to null. } NORMALIZE = normalize; FORM_NFKC = formNFKC; } private DefaultPlatformIMPL() { super(); } @Override public void normalize(StringBuilder buffer) { try { String normal = (String) NORMALIZE.invoke(null, buffer, FORM_NFKC); buffer.replace(0,buffer.length(),normal); } catch(Exception ex) { //Don't do anything. buffer should be used. } } } /** * IBM JDK 5 platform class. */ private static class IBM5PlatformIMPL extends PlatformIMPL { private IBM5PlatformIMPL() { super(); } @Override public void normalize(StringBuilder buffer) { //No implementation. } } /** * Normalize the specified buffer. * * @param buffer The buffer to normalize. */ public static void normalize(StringBuilder buffer) { IMPL.normalize(buffer); } /** * Test if a platform java vendor property starts with the specified * vendor string. * * @param vendor The vendor to check for. * @return {@code true} if the java vendor starts with the specified vendor * string. */ public static boolean isVendor(String vendor) { String javaVendor = System.getProperty("java.vendor"); return javaVendor.startsWith(vendor); } } opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Copyright 2008-2009 Sun Microsystems, Inc. */ package org.opends.server.util; @@ -920,7 +920,7 @@ public void testGenerateCSRAliasInUse() throws Exception { if (! CERT_MANAGER_AVAILABLE) if (! CERT_MANAGER_AVAILABLE || Platform.isVendor("IBM")) { return; } @@ -1002,7 +1002,7 @@ public void testGenerateCSRInvalidSubject() throws Exception { if (! CERT_MANAGER_AVAILABLE) if (! CERT_MANAGER_AVAILABLE || Platform.isVendor("IBM")) { return; } @@ -1037,7 +1037,7 @@ public void testGenerateCSRJKS() throws Exception { if (! CERT_MANAGER_AVAILABLE) if (! CERT_MANAGER_AVAILABLE || Platform.isVendor("IBM")) { return; } @@ -1068,7 +1068,7 @@ public void testGenerateCSRPKCS12() throws Exception { if (! CERT_MANAGER_AVAILABLE) if (! CERT_MANAGER_AVAILABLE || Platform.isVendor("IBM")) { return; }