diff options
| author | Robert Godfrey <rgodfrey@apache.org> | 2014-08-03 23:14:07 +0000 |
|---|---|---|
| committer | Robert Godfrey <rgodfrey@apache.org> | 2014-08-03 23:14:07 +0000 |
| commit | 1912f79bd9b292a2c8ebf79e206992be99658908 (patch) | |
| tree | 45f8d76a31162fb1b24d61ffbc2e67955f0b59a2 /qpid/java | |
| parent | 50ef5c1742d607847dc0e5080278bcdd7e84b69a (diff) | |
| download | qpid-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')
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){ |
