summaryrefslogtreecommitdiff
path: root/qpid/java
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2014-08-03 23:14:07 +0000
committerRobert Godfrey <rgodfrey@apache.org>2014-08-03 23:14:07 +0000
commit1912f79bd9b292a2c8ebf79e206992be99658908 (patch)
tree45f8d76a31162fb1b24d61ffbc2e67955f0b59a2 /qpid/java
parent50ef5c1742d607847dc0e5080278bcdd7e84b69a (diff)
downloadqpid-python-1912f79bd9b292a2c8ebf79e206992be99658908.tar.gz
QPID-5946 : [Java Broker] Add ability to parse PKCS#1 format private keys and improve the presentation of keys stores on the broker tab in the HTTP management UI
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1615462 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java')
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java66
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java3
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java13
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileTrustStore.java4
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStore.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java100
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStore.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStoreImpl.java17
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js6
9 files changed, 183 insertions, 30 deletions
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java
index 2755c8dba4..8b9effbd16 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java
@@ -595,21 +595,81 @@ public abstract class AbstractConfiguredObject<X extends ConfiguredObject<X>> im
protected void onResolve()
{
+ Set<ConfiguredObjectAttribute<?,?>> unresolved = new HashSet<>();
+ Set<ConfiguredObjectAttribute<?,?>> derived = new HashSet<>();
+
+
+ for (ConfiguredObjectAttribute<?, ?> attr : _attributeTypes.values())
+ {
+ if (attr.isAutomated())
+ {
+ unresolved.add(attr);
+ }
+ else if(attr.isDerived())
+ {
+ derived.add(attr);
+ }
+ }
+
// If there is a context attribute, resolve it first, so that other attribute values
// may support values containing references to context keys.
ConfiguredObjectAttribute<?, ?> contextAttribute = _attributeTypes.get("context");
if (contextAttribute != null && contextAttribute.isAutomated())
{
resolveAutomatedAttribute((ConfiguredAutomatedAttribute<?, ?>) contextAttribute);
+ unresolved.remove(contextAttribute);
}
- for (ConfiguredObjectAttribute<?, ?> attr : _attributeTypes.values())
+ boolean changed = true;
+ while(!unresolved.isEmpty() || !changed)
{
- if (attr != contextAttribute && attr.isAutomated())
+ changed = false;
+ Iterator<ConfiguredObjectAttribute<?,?>> attrIter = unresolved.iterator();
+
+ while (attrIter.hasNext())
{
- resolveAutomatedAttribute((ConfiguredAutomatedAttribute<?, ?>) attr);
+ ConfiguredObjectAttribute<?, ?> attr = attrIter.next();
+
+ if(!(dependsOn(attr, unresolved) || (!derived.isEmpty() && dependsOn(attr, derived))))
+ {
+ resolveAutomatedAttribute((ConfiguredAutomatedAttribute<?, ?>) attr);
+ attrIter.remove();
+ changed = true;
+ }
}
+ // TODO - really we should define with meta data which attributes any given derived attr is dependent upon
+ // and only remove the derived attr as an obstacle when those fields are themselves resolved
+ if(!changed && !derived.isEmpty())
+ {
+ changed = true;
+ derived.clear();
+ }
+ }
+ }
+
+ private boolean dependsOn(final ConfiguredObjectAttribute<?, ?> attr,
+ final Set<ConfiguredObjectAttribute<?, ?>> unresolved)
+ {
+ Object value = _attributes.get(attr.getName());
+ if(value == null && !"".equals(((ConfiguredAutomatedAttribute)attr).defaultValue()))
+ {
+ value = ((ConfiguredAutomatedAttribute)attr).defaultValue();
}
+ if(value instanceof String)
+ {
+ String interpolated = interpolate(this, (String)value);
+ if(interpolated.contains("${this:"))
+ {
+ for(ConfiguredObjectAttribute<?,?> unresolvedAttr : unresolved)
+ {
+ if(interpolated.contains("${this:"+unresolvedAttr.getName()))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
private void resolveAutomatedAttribute(final ConfiguredAutomatedAttribute<?, ?> autoAttr)
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
index d8a8c3f5cf..899e98fa22 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStore.java
@@ -57,6 +57,9 @@ public interface FileKeyStore<X extends FileKeyStore<X>> extends KeyStore<X>
}
};
+ @ManagedAttribute(defaultValue = "${this:path}")
+ String getDescription();
+
@ManagedAttribute( mandatory = true)
String getPath();
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
index d755380210..ac28619d2d 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileKeyStoreImpl.java
@@ -21,7 +21,6 @@
package org.apache.qpid.server.security;
import java.io.IOException;
-import java.lang.reflect.Type;
import java.security.AccessControlException;
import java.security.GeneralSecurityException;
import java.security.KeyStoreException;
@@ -29,8 +28,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -56,16 +53,6 @@ import org.apache.qpid.transport.network.security.ssl.SSLUtil;
@ManagedObject( category = false )
public class FileKeyStoreImpl extends AbstractConfiguredObject<FileKeyStoreImpl> implements FileKeyStore<FileKeyStoreImpl>
{
- @SuppressWarnings("serial")
- public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{
- put(NAME, String.class);
- put(PATH, String.class);
- put(PASSWORD, String.class);
- put(KEY_STORE_TYPE, String.class);
- put(CERTIFICATE_ALIAS, String.class);
- put(KEY_MANAGER_FACTORY_ALGORITHM, String.class);
- }});
-
@ManagedAttributeField
private String _type;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileTrustStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileTrustStore.java
index 5f220848cc..86d7d5e4b8 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileTrustStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/FileTrustStore.java
@@ -57,6 +57,10 @@ public interface FileTrustStore<X extends FileTrustStore<X>> extends TrustStore<
}
};
+
+ @ManagedAttribute(defaultValue = "${this:path}")
+ String getDescription();
+
@ManagedAttribute( mandatory = true )
String getPath();
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStore.java
index 9563f98579..458daa4b7a 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStore.java
@@ -28,6 +28,8 @@ import org.apache.qpid.server.model.ManagedObject;
@ManagedObject( category = false, type = "NonJavaKeyStore" )
public interface NonJavaKeyStore<X extends NonJavaKeyStore<X>> extends KeyStore<X>
{
+ @ManagedAttribute(defaultValue = "${this:subjectName}")
+ String getDescription();
@ManagedAttribute( mandatory = true, secure = true )
String getPrivateKeyUrl();
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
index efcd40c638..299ba6c249 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaKeyStoreImpl.java
@@ -26,8 +26,10 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
+import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.AccessControlException;
@@ -38,7 +40,9 @@ import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -393,12 +397,102 @@ public class NonJavaKeyStoreImpl extends AbstractConfiguredObject<NonJavaKeyStor
content = DatatypeConverter.parseBase64Binary(keyBuilder.toString());
}
}
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
- KeyFactory kf = KeyFactory.getInstance("RSA");
- PrivateKey key = kf.generatePrivate(keySpec);
+ PrivateKey key;
+ try
+ {
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ key = kf.generatePrivate(keySpec);
+ }
+ catch(InvalidKeySpecException e)
+ {
+ // not in PCKS#8 format - try parsing as PKCS#1
+ RSAPrivateCrtKeySpec keySpec = getRSAKeySpec(content);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ try
+ {
+ key = kf.generatePrivate(keySpec);
+ }
+ catch(InvalidKeySpecException e2)
+ {
+ throw new InvalidKeySpecException("Cannot parse the provided key as either PKCS#1 or PCKS#8 format");
+ }
+
+ }
return key;
}
+ private static RSAPrivateCrtKeySpec getRSAKeySpec(byte[] keyBytes) throws InvalidKeySpecException
+ {
+
+ ByteBuffer buffer = ByteBuffer.wrap(keyBytes);
+ try
+ {
+ // PKCS#1 is encoded as a DER sequence of:
+ // (version, modulus, publicExponent, privateExponent, primeP, primeQ,
+ // primeExponentP, primeExponentQ, crtCoefficient)
+
+ int tag = ((int)buffer.get()) & 0xff;
+
+ // check tag is that of a sequence
+ if(((tag & 0x20) != 0x20) || ((tag & 0x1F) != 0x10))
+ {
+ throw new InvalidKeySpecException("Unable to parse key as PKCS#1 format");
+ }
+
+ int length = getLength(buffer);
+
+ buffer = buffer.slice();
+ buffer.limit(length);
+
+ // first tlv is version - which we'll ignore
+ byte versionTag = buffer.get();
+ int versionLength = getLength(buffer);
+ buffer.position(buffer.position()+versionLength);
+
+
+ RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(
+ getInteger(buffer), getInteger(buffer), getInteger(buffer), getInteger(buffer), getInteger(buffer),
+ getInteger(buffer), getInteger(buffer), getInteger(buffer));
+
+ return keySpec;
+ }
+ catch(BufferUnderflowException e)
+ {
+ throw new InvalidKeySpecException("Unable to parse key as PKCS#1 format");
+ }
+ }
+
+ private static int getLength(ByteBuffer buffer)
+ {
+
+ int i = ((int) buffer.get()) & 0xff;
+
+ // length 0 <= i <= 127 encoded as a single byte
+ if ((i & ~0x7F) == 0)
+ {
+ return i;
+ }
+
+ // otherwise the first octet gives us the number of octets needed to read the length
+ byte[] bytes = new byte[i & 0x7f];
+ buffer.get(bytes);
+
+ return new BigInteger(1, bytes).intValue();
+ }
+
+ private static BigInteger getInteger(ByteBuffer buffer) throws InvalidKeySpecException
+ {
+ int tag = ((int) buffer.get()) & 0xff;
+ // 0x02 indicates an integer type
+ if((tag & 0x1f) != 0x02)
+ {
+ throw new InvalidKeySpecException("Unable to parse key as PKCS#1 format");
+ }
+ byte[] num = new byte[getLength(buffer)];
+ buffer.get(num);
+ return new BigInteger(num);
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStore.java
index 3537e5911f..f7d970074d 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStore.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStore.java
@@ -31,6 +31,8 @@ import org.apache.qpid.server.model.TrustStore;
@ManagedObject( category = false, type = "NonJavaTrustStore" )
public interface NonJavaTrustStore<X extends NonJavaTrustStore<X>> extends TrustStore<X>
{
+ @ManagedAttribute(defaultValue = "${this:certificateDetails}")
+ String getDescription();
@ManagedAttribute( mandatory = true )
String getCertificatesUrl();
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStoreImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStoreImpl.java
index c709d5d8e7..4f7f913776 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStoreImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/NonJavaTrustStoreImpl.java
@@ -101,15 +101,18 @@ public class NonJavaTrustStoreImpl
public List<Map<CertificateDetails,Object>> getCertificateDetails()
{
List<Map<CertificateDetails,Object>> certificateDetails = new ArrayList<>();
- for(X509Certificate certificate : _certificates)
+ if(_certificates != null)
{
- Map<CertificateDetails,Object> details = new EnumMap<>(CertificateDetails.class);
+ for (X509Certificate certificate : _certificates)
+ {
+ Map<CertificateDetails, Object> details = new EnumMap<>(CertificateDetails.class);
- details.put(CertificateDetails.SUBJECT_NAME, getNameFromCertificate(certificate));
- details.put(CertificateDetails.ISSUER_NAME, certificate.getIssuerX500Principal().getName());
- details.put(CertificateDetails.VALID_START, certificate.getNotBefore());
- details.put(CertificateDetails.VALID_END, certificate.getNotAfter());
- certificateDetails.add(details);
+ details.put(CertificateDetails.SUBJECT_NAME, getNameFromCertificate(certificate));
+ details.put(CertificateDetails.ISSUER_NAME, certificate.getIssuerX500Principal().getName());
+ details.put(CertificateDetails.VALID_START, certificate.getNotBefore());
+ details.put(CertificateDetails.VALID_END, certificate.getNotAfter());
+ certificateDetails.add(details);
+ }
}
return certificateDetails;
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js
index ff33d8e7c6..3ae4537807 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js
+++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js
@@ -544,10 +544,8 @@ define(["dojo/_base/xhr",
that.keyStoresGrid =
new UpdatableStore(that.brokerData.keystores, query(".broker-key-stores")[0],
[ { name: "Name", field: "name", width: "20%"},
- { name: "Path", field: "path", width: "40%"},
- { name: "Type", field: "keyStoreType", width: "5%"},
- { name: "Key Manager Algorithm", field: "keyManagerFactoryAlgorithm", width: "20%"},
- { name: "Alias", field: "certificateAlias", width: "15%"}
+ { name: "Type", field: "type", width: "20%"},
+ { name: "Description", field: "description", width: "60%"}
], function(obj) {
connect.connect(obj.grid, "onRowDblClick", obj.grid,
function(evt){