[xmlsectool] branch main updated: XSTJ82 - Revise --pkcs11Config handling under Java 11

Ian Young ian at iay.org.uk
Wed Sep 30 14:07:01 UTC 2020


This is an automated email from the git hooks/post-receive script.

iay pushed a commit to branch main
in repository xmlsectool.

View the commit online:
http://git.shibboleth.net/view/?p=xmlsectool.git;a=commit;h=69d3c45cf8041bb8c33e0a7817a25db48e51064c

The following commit(s) were added to refs/heads/main by this push:
       new  69d3c45   XSTJ82 - Revise --pkcs11Config handling under Java 11
69d3c45 is described below

commit 69d3c45cf8041bb8c33e0a7817a25db48e51064c
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Wed Sep 30 15:06:36 2020 +0100

    XSTJ82 - Revise --pkcs11Config handling under Java 11
    
    https://issues.shibboleth.net/jira/browse/XSTJ-82
---
 .../tool/xmlsectool/CommandLineArguments.java      |   5 -
 .../tool/xmlsectool/CredentialHelper.java          |  81 ++++++++------
 .../net/shibboleth/tool/xmlsectool/XSTJ82Test.java |  93 ++++++++++++++++
 .../net/shibboleth/tool/xmlsectool/XSTJ82-in.xml   | 119 +++++++++++++++++++++
 .../net/shibboleth/tool/xmlsectool/softhsm.cfg     |   8 ++
 5 files changed, 270 insertions(+), 36 deletions(-)

diff --git a/src/main/java/net/shibboleth/tool/xmlsectool/CommandLineArguments.java b/src/main/java/net/shibboleth/tool/xmlsectool/CommandLineArguments.java
index 2e484a0..897a01d 100644
--- a/src/main/java/net/shibboleth/tool/xmlsectool/CommandLineArguments.java
+++ b/src/main/java/net/shibboleth/tool/xmlsectool/CommandLineArguments.java
@@ -684,11 +684,6 @@ public class CommandLineArguments {
         out.println(String.format("  --%-20s %s", KEY_ARG,
                 "Specifies the key alias for the signing key is read."));
         out.println(String.format("  --%-20s %s", KEY_PASSWORD_ARG, "Specifies the pin for the signing key."));
-        out.println(String.format(
-                "  --%-20s %s",
-                KEYSTORE_PROVIDER_ARG,
-                "The fully qualified class name of the PKCS#11 keystore provider implementation. "
-                + "(e.g., sun.security.pkcs11.SunPKCS11)"));
 
         out.println();
         out.println("Signature verification algorithm blacklist options:");
diff --git a/src/main/java/net/shibboleth/tool/xmlsectool/CredentialHelper.java b/src/main/java/net/shibboleth/tool/xmlsectool/CredentialHelper.java
index 11a7c56..a228260 100644
--- a/src/main/java/net/shibboleth/tool/xmlsectool/CredentialHelper.java
+++ b/src/main/java/net/shibboleth/tool/xmlsectool/CredentialHelper.java
@@ -20,20 +20,21 @@ package net.shibboleth.tool.xmlsectool;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.lang.reflect.Constructor;
 import java.security.GeneralSecurityException;
 import java.security.KeyException;
 import java.security.KeyStore;
 import java.security.KeyStore.PrivateKeyEntry;
 import java.security.KeyStore.TrustedCertificateEntry;
 import java.security.KeyStoreException;
-import java.security.Provider;
+import java.security.NoSuchProviderException;
 import java.security.Security;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Collection;
 
+import javax.annotation.Nonnull;
+
 import org.opensaml.security.crypto.KeySupport;
 import org.opensaml.security.x509.BasicX509Credential;
 import org.opensaml.security.x509.X509Support;
@@ -129,53 +130,71 @@ public final class CredentialHelper {
     }
 
     /**
-     * Reads in the X509 credentials from a PKCS11 source.
+     * Dump the list of available security providers for diagnostic purposes.
+     *
+     * @param message heading message to use before the list of providers
+     */
+    private static void dumpSecurityProviders(@Nonnull final String message) {
+        if (LOG.isDebugEnabled() ) {
+            LOG.debug(message);
+            for (final var provider : Security.getProviders()) {
+                LOG.debug("   available security provider: {}", provider.getName());
+            }
+        }
+    }
+
+    /**
+     * Reads in an X.509 credential from a PKCS11 source.
      * 
-     * @param keystoreProvider keystore provider class
-     * @param pkcs11Config PKCS11 configuration file used by the keystore provider
+     * @param keystoreProvider keystore provider class (legacy: should now be <code>null</code>)
+     * @param pkcs11Config configuration file used by the PKCS#11 provider
      * @param keyAlias private key keystore alias
      * @param keyPassword private key password, may not be null
      * 
-     * @return the credentials
+     * @return the credential
      * 
      * @throws IOException if it is not possible to read the keystore
      * @throws GeneralSecurityException if there is a problem loading the keystore, or loading the credential from it
      */
     protected static BasicX509Credential getPKCS11Credential(final String keystoreProvider, final String pkcs11Config,
             final String keyAlias, final String keyPassword) throws IOException, GeneralSecurityException {
-        LOG.debug("Install PKCS11 provider");
 
-        KeyStore keystore = null;
+        // Warn about the legacy use of --keystoreProvider
+        if (keystoreProvider != null) {
+            LOG.warn("The --keystoreProvider option is now ignored when used with --pkcs11Config; remove it");
+        }
+
+        dumpSecurityProviders("Acquiring PKCS11 keystore");
+
+        // Head off legacy use of --pkcs11Config DUMMY
+        if (!new File(pkcs11Config).exists()) {
+            LOG.error("PKCS#11 configuration file '{}' does not exist", pkcs11Config);
+            throw new IOException("PKCS#11 configuration file '" + pkcs11Config + "' does not exist");
+        }
+
         try {
-            if (keystoreProvider != null) {
-                LOG.debug("Creating PKCS11 keystore with provider {} and configuration file {}", keystoreProvider,
-                        pkcs11Config);
-                final Class<Provider> providerClass =
-                        (Class<Provider>) CredentialHelper.class.getClassLoader().loadClass(keystoreProvider);
-                final Constructor<Provider> providerConstructor = providerClass.getConstructor(String.class);
-                final Provider pkcs11Provider = providerConstructor.newInstance(pkcs11Config);
-                pkcs11Provider.load(new FileInputStream(pkcs11Config));
-                Security.addProvider(pkcs11Provider);
-                keystore = KeyStore.getInstance("PKCS11", pkcs11Provider);
-            } else {
-                LOG.debug("Creating PKCS11 keystore with system wide provider and configuration file");
-                keystore = KeyStore.getInstance("PKCS11");
+            LOG.debug("Creating PKCS11 keystore with configuration file {}", pkcs11Config);
+            
+            final var baseProvider = Security.getProvider("SunPKCS11");
+            if (baseProvider == null) {
+                throw new NoSuchProviderException("could not acquire unconfigured PKCS#11 provider");
             }
-        } catch (final ClassNotFoundException e) {
-            LOG.error((new StringBuilder("Unable to load keystore provider class: ")).append(keystoreProvider)
-                    .toString());
-            throw new Terminator(ReturnCode.RC_INIT);
-        } catch (final NoSuchMethodException e) {
-            LOG.error("Keystore provider class does not provide a String-argument constructor");
-            throw new Terminator(ReturnCode.RC_INIT);
+            LOG.debug("Unconfigured PKCS#11 provider: " + baseProvider.getName());
+            final var pkcs11Provider = baseProvider.configure(pkcs11Config);
+            LOG.debug("Configured PKCS#11 provider: " + pkcs11Provider.getName());
+
+            Security.addProvider(pkcs11Provider);
+
+            final var keystore = KeyStore.getInstance("PKCS11", pkcs11Provider);
+            LOG.debug("Initializing PKCS11 keystore");
+            keystore.load(null, keyPassword.toCharArray());
+            return getCredentialFromKeystore(keystore, keyAlias, keyPassword);
+
         } catch (final Exception e) {
             LOG.error("Unable to read PKCS11 keystore: {}", e.getMessage());
             throw new IOException("Unable to read PKCS11 keystore", e);
         }
 
-        LOG.debug("Initializing PKCS11 keystore");
-        keystore.load(null, keyPassword.toCharArray());
-        return getCredentialFromKeystore(keystore, keyAlias, keyPassword);
     }
 
     /**
diff --git a/src/test/java/net/shibboleth/tool/xmlsectool/XSTJ82Test.java b/src/test/java/net/shibboleth/tool/xmlsectool/XSTJ82Test.java
new file mode 100644
index 0000000..b76ce8b
--- /dev/null
+++ b/src/test/java/net/shibboleth/tool/xmlsectool/XSTJ82Test.java
@@ -0,0 +1,93 @@
+package net.shibboleth.tool.xmlsectool;
+
+import java.security.interfaces.RSAPublicKey;
+import java.util.List;
+
+import javax.xml.transform.dom.DOMSource;
+
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.opensaml.xmlsec.signature.KeyValue;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import net.shibboleth.utilities.java.support.xml.ElementSupport;
+import net.shibboleth.utilities.java.support.xml.SchemaBuilder.SchemaLanguage;
+
+/**
+ * Test for <code>--pkcs11Config</code> option under Java 11+.
+ * 
+ * <p>
+ * It's really hard to provide tests for this bean that will run anywhere, because
+ * by definition it requires access to a PKCS#11 token, which is not something you'll
+ * find on any street corner.
+ * </p>
+ * 
+ * <p>
+ * The approach taken here is to have a number of (by default <em>disabled</em> tests
+ * exercising specific scenarios with specific tokens.
+ * </p>
+ */
+public class XSTJ82Test extends BaseTest {
+
+    XSTJ82Test() {
+        super(XSTJ82Test.class);
+    }
+
+    @Test(enabled = false)
+    public void xstj82() throws Exception {
+        // Locate the PKCS#11 configuration file
+        final var configFile = packageRelativeFile("softhsm.cfg");
+
+        // build command-line arguments
+        final String[] args = {
+                "--sign",
+                "--inFile", "in.xml",
+                "--outFile", "out.xml",
+                "--pkcs11Config", configFile.getAbsolutePath(),
+                "--keystoreProvider", "dummy",
+                "--keyPassword", "1234",
+                "--key", "key2048",
+                };
+        final CommandLineArguments cli = new CommandLineArguments();
+        cli.parseCommandLineArguments(args);
+        XMLSecTool.initLogging(cli);
+
+        // Deep unit test on the CredentialHelper's credential acquisition method
+        var cred = CredentialHelper.getPKCS11Credential(cli.getKeystoreProvider(), cli.getPkcs11Config(),
+                cli.getKey(), cli.getKeyPassword());
+        Assert.assertNotNull(cred);
+
+        var pubKey = cred.getPublicKey();
+        Assert.assertNotNull(pubKey);
+        Assert.assertEquals(pubKey.getAlgorithm(), "RSA");
+        Assert.assertTrue(pubKey instanceof RSAPublicKey);
+        var rsaPubKey = (RSAPublicKey) pubKey;
+        Assert.assertEquals(rsaPubKey.getModulus().bitLength(), 2048);
+
+        // acquire a document to sign
+        final Document xml = readXMLDocument("in.xml");
+        
+        // perform signature operation
+        XMLSecTool.sign(cli, cred, xml);
+        
+        // verify the signature using our own code for consistency
+        XMLSecTool.verifySignature(cli, cred, xml);
+
+        // take a careful look at the signature
+        final Element signatureElement = XMLSecTool.getSignatureElement(xml);
+        final Element keyInfoElement = ElementSupport.getFirstChildElement(signatureElement,
+                KeyInfo.DEFAULT_ELEMENT_NAME);
+        final List<Element> keyInfoChildren = ElementSupport.getChildElements(keyInfoElement);
+        Assert.assertFalse(keyInfoChildren.isEmpty());
+        final List<Element> keyValues = ElementSupport.getChildElements(keyInfoElement, KeyValue.DEFAULT_ELEMENT_NAME);
+        for (final Element keyValue : keyValues) {
+            Assert.assertNotNull(ElementSupport.getFirstChildElement(keyValue), "empty KeyValue element");
+        }
+
+        // validate the resulting XML; this will also show up any error
+        final SchemaValidator validator = new SchemaValidator(SchemaLanguage.XML, getSchemaDirectory());
+        validator.validate(new DOMSource(xml));
+    }
+}
diff --git a/src/test/resources/net/shibboleth/tool/xmlsectool/XSTJ82-in.xml b/src/test/resources/net/shibboleth/tool/xmlsectool/XSTJ82-in.xml
new file mode 100644
index 0000000..cd3c3f0
--- /dev/null
+++ b/src/test/resources/net/shibboleth/tool/xmlsectool/XSTJ82-in.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+    xmlns:ukfedlabel="http://ukfederation.org.uk/2006/11/label"
+    xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata ../xml/saml-schema-metadata-2.0.xsd
+    urn:oasis:names:tc:SAML:metadata:algsupport ../xml/sstc-saml-metadata-algsupport-v1.0.xsd
+    urn:oasis:names:tc:SAML:metadata:ui ../xml/sstc-saml-metadata-ui-v1.0.xsd
+    urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol ../xml/sstc-saml-idp-discovery.xsd
+    urn:oasis:names:tc:SAML:profiles:SSO:request-init ../xml/sstc-request-initiation.xsd
+    urn:mace:shibboleth:metadata:1.0 ../xml/shibboleth-metadata-1.0.xsd
+    http://ukfederation.org.uk/2006/11/label ../xml/uk-fed-label.xsd
+    http://www.w3.org/2001/04/xmlenc# ../xml/xenc-schema.xsd
+    http://www.w3.org/2000/09/xmldsig# ../xml/xmldsig-core-schema.xsd"
+    ID="uk001480" entityID="https://idp.shibboleth.net/idp/shibboleth">
+    <!--
+        This is a shibboleth.net Shibboleth 2 IdP for the JISC Services Management Company Ltd. 
+    -->
+    <Extensions>
+        <shibmd:Scope regexp="false">shibboleth.net</shibmd:Scope>
+        <ukfedlabel:UKFederationMember/>
+        <ukfedlabel:ExportOptIn date="2011-12-07"/>
+        <ukfedlabel:Software fullVersion="2.3.8" version="2" name="Shibboleth" date="2012-12-07"/>
+        <alg:DigestMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
+        <alg:DigestMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>
+        <alg:DigestMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+        <alg:DigestMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+        <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
+        <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
+        <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+        <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
+            Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+    </Extensions>
+    <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+        <Extensions>
+            <shibmd:Scope regexp="false">shibboleth.net</shibmd:Scope>
+            <mdui:UIInfo xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
+                <mdui:DisplayName xml:lang="en">Shibboleth.net</mdui:DisplayName>
+                <mdui:Description xml:lang="en">An identity provider hosted and used by the
+                    developers of Shibboleth.</mdui:Description>
+                <mdui:Logo height="82" width="64">https://shibboleth.net/images/gryphon_64x82.png</mdui:Logo>
+            </mdui:UIInfo>
+        </Extensions>
+        <KeyDescriptor>
+            <ds:KeyInfo>
+                <ds:X509Data>
+                    <ds:X509Certificate>
+                        MIIDNDCCAhygAwIBAgIVAKyBWnv1/h1U11C7kHvV33FIrEsJMA0GCSqGSIb3DQEB
+                        BQUAMB0xGzAZBgNVBAMTEmlkcC5zaGliYm9sZXRoLm5ldDAeFw0xMDEyMjkwMDA5
+                        MTlaFw0zMDEyMjkwMDA5MTlaMB0xGzAZBgNVBAMTEmlkcC5zaGliYm9sZXRoLm5l
+                        dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKjWAdpUx/82FUzrRMfA
+                        M63PkZZYCm3RnT3eiL+DeJcbGdcEJx/o+32vgHXJgJOBt14YdVam5GErIYgk4SGq
+                        5Z5RYl0PpQn6HQG/9prGnYCu6p5zfb0557o51Eh8TcVehS6Y2ruyCjAF0jgVMwh5
+                        /0Oh8EE9wG93pSpm70DAiiaTVCb8WoT1aZYtxbBmmuH10bU+wge/NMmaHuVAe599
+                        pyezFIL4FoI2g+1Q6nG4Yl1Z07I81tTApXKVMWRt/4/M3m2D7PUMOQ9qsxthp2L/
+                        LovIeNo0bTyeW290T2Y/JRZhKOgeDqkhuu82DPri2Vm5G/unB69KfRB7CF9QWIc3
+                        y80CAwEAAaNrMGkwSAYDVR0RBEEwP4ISaWRwLnNoaWJib2xldGgubmV0hilodHRw
+                        czovL2lkcC5zaGliYm9sZXRoLm5ldC9pZHAvc2hpYmJvbGV0aDAdBgNVHQ4EFgQU
+                        3uZ32tKXJBzPCTp2dtHSLV0FvGgwDQYJKoZIhvcNAQEFBQADggEBAAYXYuzp0UTj
+                        3yLRvUCbEtaw9b80+weOELkVv3WFY3QAG8pIKEblrMMtzrzLFWZwYwwMZDab/HnH
+                        egmgjZBthrOedEmoJ+OHRmIiS8zdZxVGEadJhTUaeIkO6kwK7Ht3nQePoiXV7TI5
+                        +A9SpmZGoukC85Za4wGDw4xWGs5t5l6tBuuV+1s0oC6T8ih5n/NyThfpbihSW0d7
+                        iBfSUickgpoM2BLM3FCnbO8HOsX1rGV4ypG9ZGDDvr2jrzalXXmc05gSlL2qd9ce
+                        Q1M+9vavusPCqlj2zZf2/HfzhyiFcb/OgA0oTFWW2ynXji6UarIV5QaPoi/XmGmx
+                        BXD36HfGBXk=
+                    </ds:X509Certificate>
+                </ds:X509Data>
+            </ds:KeyInfo>
+            <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
+            <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes192-cbc"/>
+            <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
+            <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
+            <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
+        </KeyDescriptor>
+        <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+        <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+            Location="https://shibboleth.net/idp/profile/SAML2/POST/SSO"/>
+        <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
+            Location="https://shibboleth.net/idp/profile/SAML2/POST-SimpleSign/SSO"/>
+        <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+            Location="https://shibboleth.net/idp/profile/SAML2/Redirect/SSO"/>
+    </IDPSSODescriptor>
+    <Organization>
+        <OrganizationName xml:lang="en">JISC Services Management Company Ltd</OrganizationName>
+        <OrganizationDisplayName xml:lang="en">Shibboleth.net</OrganizationDisplayName>
+        <OrganizationURL xml:lang="en">http://www.shibboleth.net/</OrganizationURL>
+    </Organization>
+    <ContactPerson contactType="support">
+        <GivenName>Shibboleth.Net Technical Support</GivenName>
+        <EmailAddress>mailto:contact at shibboleth.net</EmailAddress>
+    </ContactPerson>
+    <ContactPerson contactType="technical">
+        <GivenName>Scott</GivenName>
+        <SurName>Cantor</SurName>
+        <EmailAddress>mailto:cantor.2 at osu.edu</EmailAddress>
+    </ContactPerson>
+    <ContactPerson contactType="technical">
+        <GivenName>Ian</GivenName>
+        <SurName>Young</SurName>
+        <EmailAddress>mailto:ukfed at iay.org.uk</EmailAddress>
+    </ContactPerson>
+    <ContactPerson contactType="administrative">
+        <GivenName>Scott</GivenName>
+        <SurName>Cantor</SurName>
+        <EmailAddress>mailto:cantor.2 at osu.edu</EmailAddress>
+    </ContactPerson>
+    <ContactPerson contactType="administrative">
+        <GivenName>Ian</GivenName>
+        <SurName>Young</SurName>
+        <EmailAddress>mailto:ian at iay.org.uk</EmailAddress>
+    </ContactPerson>
+</EntityDescriptor>
diff --git a/src/test/resources/net/shibboleth/tool/xmlsectool/softhsm.cfg b/src/test/resources/net/shibboleth/tool/xmlsectool/softhsm.cfg
new file mode 100644
index 0000000..3ca23ff
--- /dev/null
+++ b/src/test/resources/net/shibboleth/tool/xmlsectool/softhsm.cfg
@@ -0,0 +1,8 @@
+#
+# PKCS#11 provider configuration for SoftHSM running under macOS.
+#
+# Note: this configuration assumes that SoftHSM has been installed using
+# the MacPorts system.
+#
+name = SoftHSM-resource
+library = /opt/local/lib/softhsm/libsofthsm2.so

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list