[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