[java-opensaml] 17/19: Add support for auto registering all NamedCurves known to Bouncy Castle.
Brent Putman
putmanb at georgetown.edu
Mon Mar 1 05:56:54 UTC 2021
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch dev/OSJ-82
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=029e9b8c82457490e4a05bfc1bb895793d295541
commit 029e9b8c82457490e4a05bfc1bb895793d295541
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Wed Feb 10 22:48:30 2021 -0500
Add support for auto registering all NamedCurves known to Bouncy Castle.
---
.../GlobalNamedCurveRegistryInitializer.java | 36 +++++++++-
.../org/opensaml/security/crypto/ec/ECSupport.java | 47 +++++++++++-
.../security/crypto/ec/curves/BasicNamedCurve.java | 84 ++++++++++++++++++++++
.../opensaml/security/crypto/ec/ECSupportTest.java | 8 +++
.../security/crypto/ec/NamedCurveRegistryTest.java | 30 ++++++++
5 files changed, 201 insertions(+), 4 deletions(-)
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/config/GlobalNamedCurveRegistryInitializer.java b/opensaml-security-api/src/main/java/org/opensaml/security/config/GlobalNamedCurveRegistryInitializer.java
index e4eac5caa..31edfbdc7 100644
--- a/opensaml-security-api/src/main/java/org/opensaml/security/config/GlobalNamedCurveRegistryInitializer.java
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/config/GlobalNamedCurveRegistryInitializer.java
@@ -18,11 +18,15 @@
package org.opensaml.security.config;
import java.util.Iterator;
+import java.util.Properties;
import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.opensaml.core.config.ConfigurationService;
import org.opensaml.core.config.InitializationException;
import org.opensaml.core.config.Initializer;
+import org.opensaml.security.crypto.ec.ECSupport;
import org.opensaml.security.crypto.ec.NamedCurve;
import org.opensaml.security.crypto.ec.NamedCurveRegistry;
import org.slf4j.Logger;
@@ -36,6 +40,10 @@ import net.shibboleth.utilities.java.support.component.InitializableComponent;
*/
public class GlobalNamedCurveRegistryInitializer implements Initializer {
+ /** Configuration property name for registering curves from Bouncy Castle. */
+ public static final String CONFIG_PROPERTY_REGISTER_BOUNCY_CASTLE_CURVES =
+ "opensaml.config.ec.registerBouncyCastleCurves";
+
/** Logger. */
private Logger log = LoggerFactory.getLogger(GlobalNamedCurveRegistryInitializer.class);
@@ -52,15 +60,39 @@ public class GlobalNamedCurveRegistryInitializer implements Initializer {
InitializableComponent.class.cast(curve).initialize();
}
} catch (final ComponentInitializationException e) {
- log.warn("Error initing NamedCurve with name '{}', OID '{}', URI '{}': {}",
+ log.warn("Error initing NamedCurve, name '{}', OID '{}', URI '{}': {}",
curve.getName(), curve.getObjectIdentifier(), curve.getURI(), curve.getClass().getName());
continue;
}
- log.debug("Registering NamedCurve with name '{}', OID '{}' and name '{}': {}'",
+ log.debug("Registering NamedCurve, name '{}', OID '{}', URI '{}': {}'",
curve.getName(), curve.getObjectIdentifier(), curve.getURI(), curve.getClass().getName());
registry.register(curve);
}
+ final Properties props = ConfigurationService.getConfigurationProperties();
+ final boolean registerBCCurves =
+ (props != null) ? Boolean.parseBoolean(props.getProperty(CONFIG_PROPERTY_REGISTER_BOUNCY_CASTLE_CURVES))
+ : false;
+
+ if (registerBCCurves) {
+ // Don't register if already done above. Use the OID as the canonical unique identifier. Names may differ.
+ final Set<String> oids = registry.getRegisteredCurves().stream()
+ .map(NamedCurve::getObjectIdentifier)
+ .collect(Collectors.toSet());
+
+ final Set<NamedCurve> curves = ECSupport.getCurvesFromBouncyCastle();
+ for (final NamedCurve curve : curves) {
+ if (!oids.contains(curve.getObjectIdentifier())) {
+ log.debug("Registering BC NamedCurve, name '{}', OID '{}', URI '{}': {}'",
+ curve.getName(), curve.getObjectIdentifier(), curve.getURI(), curve.getClass().getName());
+ registry.register(curve);
+ } else {
+ log.debug("Skipping BC NamedCurve because already registered, name '{}', OID '{}', URI '{}': {}'",
+ curve.getName(), curve.getObjectIdentifier(), curve.getURI(), curve.getClass().getName());
+ }
+ }
+ }
+
ConfigurationService.register(NamedCurveRegistry.class, registry);
}
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/ECSupport.java b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/ECSupport.java
index 30c7fbebb..e36da818f 100644
--- a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/ECSupport.java
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/ECSupport.java
@@ -30,6 +30,9 @@ import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -41,9 +44,12 @@ import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.opensaml.core.config.ConfigurationService;
import org.opensaml.security.crypto.JCAConstants;
import org.opensaml.security.crypto.KeySupport;
+import org.opensaml.security.crypto.ec.curves.BasicNamedCurve;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
@@ -164,7 +170,6 @@ public final class ECSupport {
@Nullable public static String getNamedCurveURI(@Nonnull final ECPublicKey publicKey) {
Constraint.isNotNull(publicKey, "ECPublicKey was null");
- //TODO implement fallback to do nasty parsing of the ASN.1 encoded form for the OID?
final NamedCurve namedCurve = getNamedCurve(publicKey);
if (namedCurve == null) {
LOG.warn("Could not resolve NamedCurve for ECPublicKey");
@@ -185,7 +190,6 @@ public final class ECSupport {
@Nullable public static ECParameterSpec getParameterSpecForURI(@Nonnull final String uri) {
Constraint.isNotNull(uri, "NamedCurve URI was null");
- //TODO implement fallback to use BCNamedCurveTable to lookup up OID -> param spec?
final NamedCurve namedCurve = getNamedCurve(uri);
if (namedCurve == null) {
LOG.warn("Could not resolve NamedCurve for URI: {}", uri);
@@ -302,5 +306,44 @@ public final class ECSupport {
bcSpec.getN(),
bcSpec.getH().intValue());
}
+
+ /**
+ * Return a set of all curves known to Bouncy Castle as instances of {@link NamedCurve}.
+ *
+ * @return the set of curves known to Bouncy Castle
+ */
+ @Nonnull @NonnullElements @NotLive
+ public static Set<NamedCurve> getCurvesFromBouncyCastle() {
+ final HashSet<NamedCurve> curves = new HashSet<>();
+
+ // There seems to be duplication between the main and custom curve tables, so use OID to only find unique ones.
+ final HashSet<String> oids = new HashSet<>();
+
+ final Enumeration<String> standardNames = org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames();
+ while (standardNames.hasMoreElements()) {
+ final String name = standardNames.nextElement();
+ final String oid = org.bouncycastle.asn1.x9.ECNamedCurveTable.getOID(name).getId();
+ if (!oids.contains(oid)) {
+ final ECParameterSpec paramSpec =
+ EC5Util.convertToSpec(org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName(name));
+ curves.add(new BasicNamedCurve(oid, name, paramSpec));
+ oids.add(oid);
+ }
+ }
+
+ final Enumeration<String> customNames = org.bouncycastle.crypto.ec.CustomNamedCurves.getNames();
+ while (customNames.hasMoreElements()) {
+ final String name = customNames.nextElement();
+ final String oid = org.bouncycastle.crypto.ec.CustomNamedCurves.getOID(name).getId();
+ if (!oids.contains(oid)) {
+ final ECParameterSpec paramSpec =
+ EC5Util.convertToSpec(org.bouncycastle.crypto.ec.CustomNamedCurves.getByName(name));
+ curves.add(new BasicNamedCurve(oid, name, paramSpec));
+ oids.add(oid);
+ }
+ }
+
+ return curves;
+ }
}
\ No newline at end of file
diff --git a/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/curves/BasicNamedCurve.java b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/curves/BasicNamedCurve.java
new file mode 100644
index 000000000..09b534a6b
--- /dev/null
+++ b/opensaml-security-api/src/main/java/org/opensaml/security/crypto/ec/curves/BasicNamedCurve.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.security.crypto.ec.curves;
+
+import java.security.spec.ECParameterSpec;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.crypto.ec.NamedCurve;
+
+import com.google.common.base.MoreObjects;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Basic implementations of {@link NamedCurve} where all required properties are simply supplied at
+ * construction time.
+ */
+public class BasicNamedCurve implements NamedCurve {
+
+ /** Curve name. */
+ @Nonnull private String name;
+
+ /** Curve OID. */
+ @Nonnull private String oid;
+
+ /** Curve's parameters as an instance of {@link ECParameterSpec}. */
+ @Nonnull private ECParameterSpec params;
+
+ /**
+ * Constructor.
+ *
+ * @param objectIdentifier the curve's object identifier (OID)
+ * @param standardName the curve's standard name
+ * @param parameters the curve's parameters as an {@link ECParameterSpec}
+ */
+ public BasicNamedCurve(@Nonnull final String objectIdentifier, @Nonnull final String standardName,
+ @Nonnull final ECParameterSpec parameters) {
+ oid = Constraint.isNotNull(StringSupport.trimOrNull(objectIdentifier), "Curve identifier was null");
+ name = Constraint.isNotNull(StringSupport.trimOrNull(standardName), "Curve name was null");
+ params = Constraint.isNotNull(parameters, "Curve parameters was null");
+ }
+
+ /** {@inheritDoc} */
+ public String getObjectIdentifier() {
+ return oid;
+ }
+
+ /** {@inheritDoc} */
+ public String getName() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ public ECParameterSpec getParameterSpec() {
+ return params;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("name", getName())
+ .add("OID", getObjectIdentifier())
+ .toString();
+ }
+
+}
+
diff --git a/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/ECSupportTest.java b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/ECSupportTest.java
index b7dc1cedd..82d0d564c 100644
--- a/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/ECSupportTest.java
+++ b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/ECSupportTest.java
@@ -25,6 +25,7 @@ import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
+import java.util.Set;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.opensaml.security.crypto.JCAConstants;
@@ -107,4 +108,11 @@ public class ECSupportTest extends BaseNamedCurveTest {
Assert.assertNotNull(decoded);
Assert.assertEquals(decoded, spec.getGenerator());
}
+
+ @Test
+ public void getCurvesFromBouncyCastle() {
+ Set<NamedCurve> curves = ECSupport.getCurvesFromBouncyCastle();
+ Assert.assertNotNull(curves);
+ Assert.assertFalse(curves.isEmpty());
+ }
}
diff --git a/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/NamedCurveRegistryTest.java b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/NamedCurveRegistryTest.java
index 6ac04dead..3731e8497 100644
--- a/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/NamedCurveRegistryTest.java
+++ b/opensaml-security-api/src/test/java/org/opensaml/security/crypto/ec/NamedCurveRegistryTest.java
@@ -18,10 +18,14 @@
package org.opensaml.security.crypto.ec;
import java.security.spec.ECParameterSpec;
+import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import org.bouncycastle.jce.ECNamedCurveTable;
+import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.core.config.provider.ThreadLocalConfigurationPropertiesHolder;
+import org.opensaml.security.config.GlobalNamedCurveRegistryInitializer;
import org.opensaml.security.crypto.ec.curves.Secp256r1;
import org.opensaml.security.crypto.ec.curves.Secp384r1;
import org.opensaml.security.crypto.ec.curves.Secp521r1;
@@ -159,5 +163,31 @@ public class NamedCurveRegistryTest extends BaseNamedCurveTest {
Assert.assertTrue(curveNames.contains("secp384r1"));
Assert.assertTrue(curveNames.contains("secp521r1"));
}
+
+ @Test
+ public void registerBouncyCastleCurves() throws Exception {
+ String propName = GlobalNamedCurveRegistryInitializer.CONFIG_PROPERTY_REGISTER_BOUNCY_CASTLE_CURVES;
+ NamedCurveRegistry origRegistry = ECSupport.getGlobalNamedCurveRegistry();
+ try {
+ Properties props = new Properties();
+ props.setProperty(propName, "true");
+ ThreadLocalConfigurationPropertiesHolder.setProperties(props);
+
+ new GlobalNamedCurveRegistryInitializer().init();
+ NamedCurveRegistry bcRegistry = ECSupport.getGlobalNamedCurveRegistry();
+ Assert.assertNotSame(bcRegistry, origRegistry);
+
+ Set<NamedCurve> origCurves = origRegistry.getRegisteredCurves();
+ Set<NamedCurve> bcCurves = bcRegistry.getRegisteredCurves();
+ Assert.assertTrue(bcCurves.size() > origCurves.size());
+
+ Set<String> origOIDs = origCurves.stream().map(NamedCurve::getObjectIdentifier).collect(Collectors.toSet());
+ Set<String> bcOIDs = bcCurves.stream().map(NamedCurve::getObjectIdentifier).collect(Collectors.toSet());
+ Assert.assertTrue(bcOIDs.containsAll(origOIDs));
+ } finally {
+ ThreadLocalConfigurationPropertiesHolder.clear();
+ ConfigurationService.register(NamedCurveRegistry.class, origRegistry);
+ }
+ }
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list