From 402f57cb40dd3da66e32212d6994407d80ad2556 Mon Sep 17 00:00:00 2001
From: david_page <david_page@localhost>
Date: Fri, 28 Sep 2007 03:44:00 +0000
Subject: [PATCH] Issue 466 (partial) Secret key encoding
---
opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java | 29 ++++
opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java | 273 +++++++++++++++++++++++++++++++++------------
2 files changed, 229 insertions(+), 73 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java b/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
index c00ffc9..6c6ef58 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
@@ -32,7 +32,10 @@
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.ByteArrayInputStream;
import java.security.*;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
import java.util.*;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
@@ -60,6 +63,7 @@
import org.opends.server.util.Validator;
import org.opends.server.util.SelectableCertificateKeyManager;
import org.opends.server.util.StaticUtils;
+import org.opends.server.util.Base64;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
@@ -94,6 +98,9 @@
private static final Random pseudoRandom
= new Random(secureRandom.nextLong());
+ // The preferred key wrapping transformation
+ private final String preferredKeyWrappingTransformation;
+
// The preferred message digest algorithm for the Directory Server.
private final String preferredDigestAlgorithm;
@@ -148,75 +155,107 @@
public CryptoManager(CryptoManagerCfg cfg)
throws ConfigException, InitializationException
{
- // TODO -- Get the defaults from the configuration rather than
- // hard-coding them.
+ // TODO -- Get the crypto defaults from the configuration.
+
+ // Preferred digest and validation.
+ preferredDigestAlgorithm = "SHA-1";
+ try{
+ MessageDigest.getInstance(preferredDigestAlgorithm);
+ }
+ catch (Exception e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new InitializationException(
+ // TODO: i18n
+ Message.raw("Cannot get preferred digest: " +
+ getExceptionMessage(e).toString()), e);
+ }
+
+ // Preferred MAC engine and validation.
+ preferredMACAlgorithm = "HmacSHA1";
+ preferredMACAlgorithmKeyLengthBits = 128;
+ try {
+ MacKeyEntry.generateKeyEntry(null,
+ preferredMACAlgorithm,
+ preferredMACAlgorithmKeyLengthBits);
+ }
+ catch (Exception e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new InitializationException(
+ // TODO: i18n
+ Message.raw("Cannot get preferred MAC engine: " +
+ getExceptionMessage(e).toString()), e);
+ }
+
+ // Preferred encryption cipher and validation.
preferredCipherTransformation = "AES/CBC/PKCS5Padding";
preferredCipherTransformationKeyLengthBits = 128;
- preferredDigestAlgorithm = "SHA-1";
- preferredMACAlgorithm = "HmacSHA1";
- preferredMACAlgorithmKeyLengthBits = 128;
+ try {
+ CipherKeyEntry.generateKeyEntry(null,
+ preferredCipherTransformation,
+ preferredCipherTransformationKeyLengthBits);
+ }
+ catch (Exception e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new InitializationException(
+ // TODO: i18n
+ Message.raw("Cannot get preferred encryption cipher: " +
+ getExceptionMessage(e).toString()), e);
+ }
+
+
+ // Preferred secret key wrapping cipher and validation. Depends
+ // on MAC cipher for secret key. Note that the TrustStoreBackend
+ // not available at this point, hence a "dummy" certificate must
+ // be used to validate the choice of secret key wrapping cipher.
+ // TODO: Trying OAEPWITHSHA-512ANDMGF1PADDING throws an exception
+ // "Key too small...".
+ preferredKeyWrappingTransformation
+ = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING";
+ try {
+ final String certificateBase64 =
+ "MIIB2jCCAUMCBEb7wpYwDQYJKoZIhvcNAQEEBQAwNDEbMBkGA1UEChMST3B" +
+ "lbkRTIENlcnRpZmljYXRlMRUwEwYDVQQDEwwxMC4wLjI0OC4yNTEwHhcNMD" +
+ "cwOTI3MTQ0NzUwWhcNMjcwOTIyMTQ0NzUwWjA0MRswGQYDVQQKExJPcGVuR" +
+ "FMgQ2VydGlmaWNhdGUxFTATBgNVBAMTDDEwLjAuMjQ4LjI1MTCBnzANBgkq" +
+ "hkiG9w0BAQEFAAOBjQAwgYkCgYEAnIm6ELyuNVbpaacBQ7fzHlHMmQO/CYJ" +
+ "b2gPTdb9n1HLOBqh2lmLLHvt2SgBeN5TSa1PAHW8zJy9LDhpWKZvsUOIdQD" +
+ "8Ula/0d/jvMEByEj/hr00P6yqgLXk+EudPgOkFXHA+IfkkOSghMooWc/L8H" +
+ "nD1REdqeZuxp+ARNU+cc/ECAwEAATANBgkqhkiG9w0BAQQFAAOBgQBemyCU" +
+ "jucN34MZwvzbmFHT/leUu3/cpykbGM9HL2QUX7iKvv2LJVqexhj7CLoXxZP" +
+ "oNL+HHKW0vi5/7W5KwOZsPqKI2SdYV7nDqTZklm5ZP0gmIuNO6mTqBRtC2D" +
+ "lplX1Iq+BrQJAmteiPtwhdZD+EIghe51CaseImjlLlY2ZK8w==";
+ final byte[] certificate = Base64.decode(certificateBase64);
+ final String keyID = StaticUtils.bytesToHexNoSpace(
+ getInstanceKeyID(certificate));
+ final SecretKey macKey = MacKeyEntry.generateKeyEntry(null,
+ preferredMACAlgorithm,
+ preferredMACAlgorithmKeyLengthBits).getSecretKey();
+ encodeSymmetricKeyAttribute(keyID, certificate, macKey);
+ }
+ catch (Exception e) {
+ if (debugEnabled()) {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new InitializationException(
+ // TODO: i18n
+ Message.raw("Cannot get preferred key wrapping cipher: " +
+ getExceptionMessage(e).toString()), e);
+ }
sslCertNickname = cfg.getSSLCertNickname();
sslEncryption = cfg.isSSLEncryption();
sslProtocols = cfg.getSSLProtocol();
sslCipherSuites = cfg.getSSLCipherSuite();
-
- // Make sure that we can create instances of the preferred digest,
- // MAC, and cipher algorithms.
- try
- {
- MessageDigest.getInstance(preferredDigestAlgorithm);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // TODO: i18n
- throw new InitializationException(
- Message.raw("Can't get preferred digest: " +
- getExceptionMessage(e).toString()), e);
- }
-
- try
- {
- MacKeyEntry.generateKeyEntry(null,
- preferredMACAlgorithm,
- preferredMACAlgorithmKeyLengthBits);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // TODO: i18n
- throw new InitializationException(
- Message.raw("Can't get preferred MAC provider: " +
- getExceptionMessage(e).toString()), e);
- }
-
- try
- {
- CipherKeyEntry.generateKeyEntry(null,
- preferredCipherTransformation,
- preferredCipherTransformationKeyLengthBits);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // TODO: i18n
- throw new InitializationException(
- Message.raw("Can't get preferred cipher: " +
- getExceptionMessage(e).toString()), e);
- }
}
@@ -316,6 +355,7 @@
* Return the identifier of this instance's instance-key. An
* instance-key identifier is the MD5 hash of an instance's
* instance-key public-key certificate.
+ * @see #getInstanceKeyID(byte[])
* @return This instance's instance-key identifier.
* @throws CryptoManagerException If there is a problem retrieving
* the instance-key public-key certificate or computing its MD5
@@ -323,6 +363,23 @@
*/
public byte[] getInstanceKeyID()
throws CryptoManagerException {
+ return getInstanceKeyID(getInstanceKeyCertificate());
+ }
+
+
+ /**
+ * Return the identifier of an instance's instance key. An
+ * instance-key identifier is the MD5 hash of an instance's
+ * instance-key public-key certificate.
+ * @see #getInstanceKeyID()
+ * @param instanceKeyCertificate The instance key for which to
+ * return an identifier.
+ * @return The identifier of the supplied instance key.
+ * @throws CryptoManagerException If there is a problem computing
+ * the identifier from the instance key.
+ */
+ public byte[] getInstanceKeyID(byte[] instanceKeyCertificate)
+ throws CryptoManagerException {
MessageDigest md;
final String mdAlgorithmName = "MD5";
try {
@@ -334,16 +391,86 @@
Message.raw("Failed to get MessageDigest instance for %s",
mdAlgorithmName), ex);
}
- return md.digest(getInstanceKeyCertificate());
+ return md.digest(instanceKeyCertificate);
}
- // The syntax of the ds-cfg-symmetric-key
- //hexBytes[16]:keyWrappingCipher:wrappedKeyAlgorithm:
- // wrappedKeyType:hexifiedwrappedKey
-// private String encocdeSymmetricKeyAttribute(){
-// return "hello";
-// }
+ /**
+ * Encodes a ds-cfg-symmetric-key attribute value using the supplied
+ * arguments.
+ *
+ * The syntax of the ds-cfg-symmetric-key attribute:
+ * <pre>
+ * wrappingKeyID:wrappingTransformation:wrappedKeyAlgorithm:\
+ * wrappedKeyType:hexWrappedKey
+ *
+ * wrappingKeyID ::= hexBytes[16]
+ * wrappingTransformation
+ * ::= e.g., RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING
+ * wrappedKeyAlgorithm ::= e.g., DESede
+ * wrappedKeyType ::= SECRET_KEY
+ * hexifiedwrappedKey ::= 0123456789abcdef01...
+ * </pre>
+ *
+ * @param wrappingKeyID The key identifier of the wrapping key. This
+ * parameter is the first field in the encoded value and identifies
+ * the instance that will be able to unwrap the secret key.
+ *
+ * @param wrappingKeyCertificateData The public key certificate used
+ * to derive the wrapping key.
+ *
+ * @param secretKey The secret key value to be wrapped for the
+ * encoded value.
+ *
+ * @return The encoded representation of the ds-cfg-symmetric-key
+ * attribute with the secret key wrapped with the supplied public
+ * key.
+ *
+ * @throws CryptoManagerException If there is a problem wrapping
+ * the secret key.
+ */
+ private String encodeSymmetricKeyAttribute(
+ final String wrappingKeyID,
+ final byte[] wrappingKeyCertificateData,
+ final SecretKey secretKey)
+ throws CryptoManagerException {
+ // Wrap secret key.
+ final String wrappingTransformationName
+ = preferredKeyWrappingTransformation;
+ String wrappedKeyElement;
+ try {
+ final CertificateFactory cf
+ = CertificateFactory.getInstance("X.509");
+ final Certificate certificate = cf.generateCertificate(
+ new ByteArrayInputStream(wrappingKeyCertificateData));
+ final Cipher wrapper
+ = Cipher.getInstance(wrappingTransformationName);
+ wrapper.init(Cipher.WRAP_MODE, certificate);
+ byte[] wrappedKey = wrapper.wrap(secretKey);
+ wrappedKeyElement = StaticUtils.bytesToHexNoSpace(wrappedKey);
+ }
+ catch (GeneralSecurityException ex) {
+ throw new CryptoManagerException(
+ // TODO: i18n
+ Message.raw("Failed to wrap secret key: " +
+ getExceptionMessage(ex)), ex);
+ }
+
+ // Compose ds-cfg-symmetric-key value.
+ StringBuilder symmetricKeyAttribute = new StringBuilder();
+ symmetricKeyAttribute.append(wrappingKeyID);
+ symmetricKeyAttribute.append(":");
+ symmetricKeyAttribute.append(wrappingTransformationName);
+ symmetricKeyAttribute.append(":");
+ symmetricKeyAttribute.append(secretKey.getAlgorithm());
+ symmetricKeyAttribute.append(":");
+ symmetricKeyAttribute.append("SECRET_KEY");
+ symmetricKeyAttribute.append(":");
+ symmetricKeyAttribute.append(wrappedKeyElement);
+
+ return symmetricKeyAttribute.toString();
+ }
+
/**
* Takes an encoded ds-cfg-symmetric-key attribute value and the
@@ -361,7 +488,7 @@
final String symmetricKeyAttribute)
throws CryptoManagerException {
// Initial decomposition.
- byte[] keyIDElement;
+ byte[] wrappingKeyIDElement;
String wrappingTransformationElement;
String wrappedKeyAlgorithmElement;
int wrappedKeyTypeElement;
@@ -376,7 +503,8 @@
0);
}
fieldName = "instance key identifier";
- keyIDElement = StaticUtils.hexStringToByteArray(elements[0]);
+ wrappingKeyIDElement
+ = StaticUtils.hexStringToByteArray(elements[0]);
fieldName = "key wrapping transformation";
wrappingTransformationElement = elements[1];
fieldName = "wrapped key algorithm";
@@ -403,7 +531,6 @@
= StaticUtils.hexStringToByteArray(elements[4]);
}
catch (ParseException ex) {
-
throw new CryptoManagerException(((null == fieldName)
// TODO: i18n
? Message.raw("The syntax of the symmetric key" +
@@ -417,7 +544,7 @@
// Confirm key can be unwrapped at this instance.
final byte[] instanceKeyID = getInstanceKeyID();
- if (! Arrays.equals(keyIDElement, instanceKeyID)) {
+ if (! Arrays.equals(wrappingKeyIDElement, instanceKeyID)) {
return null;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java b/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
index 6f771db..e944ad7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
@@ -747,6 +747,35 @@
/**
* Retrieves a string representation of the contents of the provided byte
+ * array using hexadecimal characters with no space between each byte.
+ *
+ * @param b The byte array containing the data.
+ *
+ * @return A string representation of the contents of the provided byte
+ * array using hexadecimal characters.
+ */
+ public static String bytesToHexNoSpace(byte[] b)
+ {
+ if ((b == null) || (b.length == 0))
+ {
+ return "";
+ }
+
+ int arrayLength = b.length;
+ StringBuilder buffer = new StringBuilder(arrayLength * 2);
+
+ for (int i=0; i < arrayLength; i++)
+ {
+ buffer.append(byteToHex(b[i]));
+ }
+
+ return buffer.toString();
+ }
+
+
+
+ /**
+ * Retrieves a string representation of the contents of the provided byte
* array using hexadecimal characters and a space between each byte.
*
* @param b The byte array containing the data.
--
Gitblit v1.10.0