From 8aa9e3c4288898f16445e768320b6dad1de612e1 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Wed, 25 Mar 2009 21:26:49 +0000
Subject: [PATCH] These changes:
---
opends/src/server/org/opends/server/util/CertificateManager.java | 873 +++++------------------------
opends/src/ads/org/opends/admin/ads/util/ApplicationKeyManager.java | 9
opends/src/server/org/opends/server/util/Platform.java | 683 +++++++++++++++++++++++
opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java | 77 +-
opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java | 10
opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java | 10
opends/src/messages/messages/utility.properties | 58 ++
7 files changed, 978 insertions(+), 742 deletions(-)
diff --git a/opends/src/ads/org/opends/admin/ads/util/ApplicationKeyManager.java b/opends/src/ads/org/opends/admin/ads/util/ApplicationKeyManager.java
index d121e66..222c72b 100644
--- a/opends/src/ads/org/opends/admin/ads/util/ApplicationKeyManager.java
+++ b/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,
diff --git a/opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java b/opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java
index a5ea34d..9b8fdee 100644
--- a/opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java
+++ b/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);
- }
- }
}
/**
diff --git a/opends/src/messages/messages/utility.properties b/opends/src/messages/messages/utility.properties
index a2cc05b..c55c1d2 100644
--- a/opends/src/messages/messages/utility.properties
+++ b/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
diff --git a/opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java b/opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java
index b5351a6..6f8d314 100644
--- a/opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java
+++ b/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();
+ }
}
diff --git a/opends/src/server/org/opends/server/util/CertificateManager.java b/opends/src/server/org/opends/server/util/CertificateManager.java
index 81b17a3..0647062 100644
--- a/opends/src/server/org/opends/server/util/CertificateManager.java
+++ b/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;
}
}
-
-
diff --git a/opends/src/server/org/opends/server/util/Platform.java b/opends/src/server/org/opends/server/util/Platform.java
new file mode 100644
index 0000000..d5ce295
--- /dev/null
+++ b/opends/src/server/org/opends/server/util/Platform.java
@@ -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);
+ }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java
index c71a573..fe1b2be 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java
+++ b/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;
}
--
Gitblit v1.10.0