[java-idp-oidc] 06/44: JOIDC-5 Initial versions of new credentials + matching keyinfo providers

Henri Mikkonen henri.mikkonen at iki.fi
Thu Oct 22 13:08:18 UTC 2020


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

hjmikkon pushed a commit to branch main
in repository java-idp-oidc.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=76f708f8747c7201e7d61b3938e35eac08b26c6e

commit 76f708f8747c7201e7d61b3938e35eac08b26c6e
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Apr 17 14:59:55 2020 +0300

    JOIDC-5 Initial versions of new credentials + matching keyinfo providers
    
    https://issues.shibboleth.net/jira/browse/JOIDC-5
---
 .../security/jwk/BasicJWKReferenceCredential.java  |  70 +++++++++
 .../security/jwk/BasicNimbusSecretCredential.java  |  70 +++++++++
 .../geant/security/jwk/JWKReferenceCredential.java |  36 +++++
 .../geant/security/jwk/NimbusSecretCredential.java |  38 +++++
 .../ext/impl/provider/ClientSecretProvider.java    |  68 +++++++++
 .../ext/impl/provider/InlineJwksProvider.java      | 163 +++++++++++++++++++++
 .../ext/impl/provider/JWKSReferenceProvider.java   |  92 ++++++++++++
 7 files changed, 537 insertions(+)

diff --git a/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/BasicJWKReferenceCredential.java b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/BasicJWKReferenceCredential.java
new file mode 100644
index 00000000..bcaa2418
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/BasicJWKReferenceCredential.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.geant.security.jwk;
+
+import java.net.URI;
+
+import org.opensaml.security.credential.AbstractCredential;
+import org.opensaml.security.credential.Credential;
+
+/**
+ * A basic implementation of {@link JWKReferenceCredential}.
+ */
+public class BasicJWKReferenceCredential extends AbstractCredential implements JWKReferenceCredential {
+
+    /** A reference to a JWK. */
+    private URI referenceUri;
+    
+    /**
+     * Constructor.
+     */
+    public BasicJWKReferenceCredential() {
+        super();
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param uri A reference to a JWK.
+     */
+    public BasicJWKReferenceCredential(final URI uri) {
+        this();
+        referenceUri = uri;
+    }
+    
+    /**
+     * Set the reference to a JWK.
+     * 
+     * @param uri What to set.
+     */
+    public void setReferenceURI(final URI uri) {
+        referenceUri = uri;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public URI getReferenceURI() {
+        return referenceUri;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Class<? extends Credential> getCredentialType() {
+        return JWKReferenceCredential.class;
+    }
+    
+}
diff --git a/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/BasicNimbusSecretCredential.java b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/BasicNimbusSecretCredential.java
new file mode 100644
index 00000000..ce368d7d
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/BasicNimbusSecretCredential.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.geant.security.jwk;
+
+import org.opensaml.security.credential.AbstractCredential;
+import org.opensaml.security.credential.Credential;
+
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+/**
+ * A basic implementation of {@link NimbusSecretCredential}.
+ */
+public class BasicNimbusSecretCredential extends AbstractCredential implements NimbusSecretCredential {
+
+    /** The client secret. */
+    private Secret clientSecret;
+    
+    /**
+     * Constructor.
+     */
+    public BasicNimbusSecretCredential() {
+        super();
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param uri The client secret.
+     */
+    public BasicNimbusSecretCredential(final Secret secret) {
+        this();
+        clientSecret = secret;
+    }
+    
+    /**
+     * Set the client secret.
+     * 
+     * @param uri What to set.
+     */
+    public void setSecret(final Secret secret) {
+        clientSecret = secret;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public Secret getSecret() {
+        return clientSecret;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Class<? extends Credential> getCredentialType() {
+        return NimbusSecretCredential.class;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/JWKReferenceCredential.java b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/JWKReferenceCredential.java
new file mode 100644
index 00000000..fa19a9de
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/JWKReferenceCredential.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.geant.security.jwk;
+
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+
+/** Credential based on a reference (URI) to a JSON Web Key (JWK). */
+public interface JWKReferenceCredential extends Credential {
+    
+    /**
+     * Get the reference URI to the remote JWK.
+     * 
+     * @return The reference URI to the remote JWK.
+     */
+    @Nonnull
+    public URI getReferenceURI();
+
+}
diff --git a/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/NimbusSecretCredential.java b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/NimbusSecretCredential.java
new file mode 100644
index 00000000..736d3073
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/org/geant/security/jwk/NimbusSecretCredential.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.geant.security.jwk;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.security.credential.Credential;
+
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+/**
+ * Credential wrapping {@link Secret}.
+ */
+public interface NimbusSecretCredential extends Credential {
+    
+    /**
+     * Get the client secret.
+     * 
+     * @return The client secret.
+     */
+    @Nonnull
+    public Secret getSecret();
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/ClientSecretProvider.java b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/ClientSecretProvider.java
new file mode 100644
index 00000000..7c1febbd
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/ClientSecretProvider.java
@@ -0,0 +1,68 @@
+package org.geant.idpextension.keyinfo.ext.impl.provider;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.geant.security.jwk.BasicNimbusSecretCredential;
+import org.geant.security.jwk.NimbusSecretCredential;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.KeyInfoResolutionContext;
+import org.opensaml.xmlsec.keyinfo.impl.provider.AbstractKeyInfoProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+import net.shibboleth.idp.saml.oidc.xmlobject.ClientSecret;
+import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * Implementation of {@link org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider} which provides basic support for
+ * extracting a {@link NimbusSecretCredential} child of KeyInfo.
+ */
+public class ClientSecretProvider extends AbstractKeyInfoProvider {
+
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(ClientSecretProvider.class);
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<Credential> process(KeyInfoCredentialResolver resolver, XMLObject keyInfoChild,
+            CriteriaSet criteriaSet, KeyInfoResolutionContext kiContext) throws SecurityException {
+        final ClientSecret clientSecret = getClientSecret(keyInfoChild);
+        if (clientSecret == null || StringSupport.trimOrNull(clientSecret.getValue()) == null) {
+            log.debug("No value found for the ClientSecret element");
+            return null;
+        }
+        final LazySet<Credential> credentials = new LazySet<>();
+        credentials.add(new BasicNimbusSecretCredential(new Secret(clientSecret.getValue())));
+        return credentials;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean handles(XMLObject keyInfoChild) {
+        return getClientSecret(keyInfoChild) != null;
+    }
+
+    /**
+     * Get the ClientSecret from the passed XML object.
+     * 
+     * @param xmlObject an XML object, presumably a {@link ClientSecret}
+     * @return the ClientSecret which was found, or null if none
+     */
+    @Nullable protected ClientSecret getClientSecret(@Nonnull final XMLObject xmlObject) {
+
+        if (xmlObject instanceof ClientSecret) {
+            return (ClientSecret) xmlObject;
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/InlineJwksProvider.java b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/InlineJwksProvider.java
new file mode 100644
index 00000000..a7df3890
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/InlineJwksProvider.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.geant.idpextension.keyinfo.ext.impl.provider;
+
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.geant.idpextension.oidc.security.impl.CredentialConversionUtil;
+import org.geant.security.jwk.BasicJWKCredential;
+import org.geant.security.jwk.JWKCredential;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialContext;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.KeyInfoResolutionContext;
+import org.opensaml.xmlsec.keyinfo.impl.provider.AbstractKeyInfoProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.jwk.AsymmetricJWK;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.KeyType;
+
+import net.shibboleth.idp.saml.oidc.xmlobject.JwksData;
+import net.shibboleth.utilities.java.support.codec.Base64Support;
+import net.shibboleth.utilities.java.support.codec.DecodingException;
+import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * Implementation of {@link org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider} which provides basic support for
+ * extracting a {@link JWKCredential} child of KeyInfo. The value is expected to be either a Base64-encoded value of
+ * either a single JWK credential or a JSON array of JWK credentials.
+ */
+public class InlineJwksProvider extends AbstractKeyInfoProvider {
+
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(InlineJwksProvider.class);
+
+    /** {@inheritDoc} */
+   @Override
+    public Collection<Credential> process(KeyInfoCredentialResolver resolver, XMLObject keyInfoChild,
+            CriteriaSet criteriaSet, KeyInfoResolutionContext kiContext) throws SecurityException {
+        final JwksData jwksData = getJwksData(keyInfoChild);
+        if (jwksData == null || StringSupport.trimOrNull(jwksData.getValue()) == null) {
+            return null;
+        }
+        final String data;
+        try {
+            data = new String(Base64Support.decode(jwksData.getValue()));
+        } catch (DecodingException e) {
+            log.error("Could not decode the JWKS data", e);
+            return null;
+        }
+        final List<JWK> jwks = parseJWKs(data);
+        if (jwks == null) {
+            return null;
+        }
+        final LazySet<Credential> credentials = new LazySet<>();
+        for (final JWK jwk : jwks) {
+            final BasicJWKCredential credential = new BasicJWKCredential();
+            if (jwk.getKeyType() == KeyType.EC || jwk.getKeyType() == KeyType.RSA) {
+                try {
+                    credential.setPublicKey(((AsymmetricJWK) jwk).toPublicKey());
+                } catch (JOSEException e) {
+                    log.warn("Could not parse public key from JWK", e);
+                }
+            } else {
+                log.warn("Unsupported key type {} found from JWK", jwk.getKeyType());
+            }
+            if (jwk.getKeyID() != null) {
+                credential.getKeyNames().add(jwk.getKeyID());
+                credential.setKid(jwk.getKeyID());
+            } else {
+                credential.getKeyNames().addAll(kiContext.getKeyNames());
+            }
+
+            if (jwk.getKeyUse() != null) {
+                credential.setUsageType(CredentialConversionUtil.getUsageType(jwk));
+            } else {
+                final CredentialContext credContext = buildCredentialContext(kiContext);
+                if (credContext != null) {
+                    credential.getCredentialContextSet().add(credContext);
+                }
+            }
+            credentials.add(credential);
+        }
+        return credentials;
+    }
+    
+    protected List<JWK> parseJWKs(final String input) {
+        final JWKSet jwkSet = parseJWKSet(input);
+        if (jwkSet != null) {
+            return jwkSet.getKeys();
+        }
+        final JWK jwk = parseJWK(input);
+        if (jwk != null) {
+            return Arrays.asList(jwk);
+        }
+        return null;
+    }
+    
+    protected JWKSet parseJWKSet(final String input) {
+        try {
+            return JWKSet.parse(input);
+        } catch (ParseException e) {
+            log.debug("Could not parse JWKSet from the given input", e);
+        }
+        return null;
+    }
+    
+    protected JWK parseJWK(final String input) {
+        try {
+            return JWK.parse(input);
+        } catch (ParseException e) {
+            log.debug("Could not parse JWK from the given input", e);
+        }
+        return null;
+    }
+
+    @Override
+    public boolean handles(XMLObject keyInfoChild) {
+        return getJwksData(keyInfoChild) != null;
+    }
+
+    /**
+     * Get the JwksData from the passed XML object.
+     * 
+     * @param xmlObject an XML object, presumably a {@link JwksData}
+     * @return the JwksData which was found, or null if none
+     */
+    @Nullable protected JwksData getJwksData(@Nonnull final XMLObject xmlObject) {
+
+        if (xmlObject instanceof JwksData) {
+            return (JwksData) xmlObject;
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/JWKSReferenceProvider.java b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/JWKSReferenceProvider.java
new file mode 100644
index 00000000..9b793236
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/org/geant/idpextension/keyinfo/ext/impl/provider/JWKSReferenceProvider.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017 - 2020, GÉANT
+ *
+ * Licensed 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.geant.idpextension.keyinfo.ext.impl.provider;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.geant.security.jwk.BasicJWKReferenceCredential;
+import org.geant.security.jwk.JWKReferenceCredential;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.impl.KeyInfoResolutionContext;
+import org.opensaml.xmlsec.keyinfo.impl.provider.AbstractKeyInfoProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.saml.oidc.xmlobject.JwksUri;
+import net.shibboleth.utilities.java.support.collection.LazySet;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+
+/**
+ * Implementation of {@link org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider} which provides basic support for
+ * extracting a {@link JWKReferenceCredential} child of KeyInfo.
+ */
+public class JWKSReferenceProvider extends AbstractKeyInfoProvider {
+
+    /** Class logger. */
+    private final Logger log = LoggerFactory.getLogger(JWKSReferenceProvider.class);
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<Credential> process(KeyInfoCredentialResolver resolver, XMLObject keyInfoChild,
+            CriteriaSet criteriaSet, KeyInfoResolutionContext kiContext) throws SecurityException {
+        final JwksUri jwksUri = getJwksUri(keyInfoChild);
+        if (jwksUri == null || StringSupport.trimOrNull(jwksUri.getValue()) == null) {
+            return null;
+        }
+        final URI uri;
+        try {
+            uri = new URI(jwksUri.getValue());
+        } catch (URISyntaxException e) {
+            log.warn("Could not build URI from the given value {}", jwksUri.getValue(), e);
+            return null;
+        }
+        final LazySet<Credential> credentials = new LazySet<>();
+        credentials.add(new BasicJWKReferenceCredential(uri));
+        return credentials;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean handles(XMLObject keyInfoChild) {
+        return getJwksUri(keyInfoChild) != null;
+    }
+
+    /**
+     * Get the JwksUri from the passed XML object.
+     * 
+     * @param xmlObject an XML object, presumably a {@link JwksUri}
+     * @return the JwksUri which was found, or null if none
+     */
+    @Nullable protected JwksUri getJwksUri(@Nonnull final XMLObject xmlObject) {
+
+        if (xmlObject instanceof JwksUri) {
+            return (JwksUri) xmlObject;
+        } else {
+            return null;
+        }
+    }
+
+}
\ No newline at end of file

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


More information about the commits mailing list