Repository: cxf Updated Branches: refs/heads/3.1.x-fixes d7478d6ce -> f8eec3e29
Prototyping the X509 client cert token binding validation cvode, tests to follow next Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/f8eec3e2 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/f8eec3e2 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/f8eec3e2 Branch: refs/heads/3.1.x-fixes Commit: f8eec3e2912fb8103e9278f3bea8504520482171 Parents: d7478d6 Author: Sergey Beryozkin <[email protected]> Authored: Thu Apr 20 16:52:54 2017 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Thu Apr 20 17:01:42 2017 +0100 ---------------------------------------------------------------------- .../oauth2/filters/OAuthRequestFilter.java | 20 ++++++++++++++++++-- .../provider/AbstractOAuthDataProvider.java | 15 +++++---------- .../rs/security/oauth2/utils/OAuthUtils.java | 17 +++++++++++++++-- 3 files changed, 38 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/f8eec3e2/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java index 5cc3e6c..1a85f46 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.oauth2.filters; import java.security.Principal; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -46,6 +47,7 @@ import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageUtils; import org.apache.cxf.phase.PhaseInterceptorChain; +import org.apache.cxf.rs.security.jose.common.JoseConstants; import org.apache.cxf.rs.security.oauth2.common.AccessTokenValidation; import org.apache.cxf.rs.security.oauth2.common.AuthenticationMethod; import org.apache.cxf.rs.security.oauth2.common.OAuthContext; @@ -56,6 +58,7 @@ import org.apache.cxf.rs.security.oauth2.utils.AuthorizationUtils; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.security.transport.TLSSessionInfo; /** * JAX-RS OAuth2 filter which can be used to protect the end-user endpoints @@ -154,7 +157,17 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator throw ExceptionUtils.toForbiddenException(null, null); } - + + // Check Client Certificate Binding if any + String certThumbprint = accessTokenV.getExtraProps().get(JoseConstants.HEADER_X509_THUMBPRINT_SHA256); + if (certThumbprint != null) { + TLSSessionInfo tlsInfo = getTlsSessionInfo(); + X509Certificate cert = tlsInfo == null ? null : OAuthUtils.getRootTLSCertificate(tlsInfo); + if (!OAuthUtils.compareCertificateThumbprints(cert, certThumbprint)) { + throw ExceptionUtils.toForbiddenException(null, null); + } + } + // Create the security context and make it available on the message SecurityContext sc = createSecurityContext(req, accessTokenV); m.put(SecurityContext.class, sc); @@ -345,5 +358,8 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator public void setIssuer(String issuer) { this.issuer = issuer; } - + + private TLSSessionInfo getTlsSessionInfo() { + return (TLSSessionInfo)getMessageContext().get(TLSSessionInfo.class.getName()); + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/f8eec3e2/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java index 8c509d7..8e44e48 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java @@ -30,7 +30,6 @@ import javax.ws.rs.core.MultivaluedMap; import org.apache.cxf.jaxrs.ext.MessageContext; import org.apache.cxf.rs.security.jose.common.JoseConstants; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; -import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.rs.security.jose.jwt.JwtToken; import org.apache.cxf.rs.security.oauth2.common.AccessTokenRegistration; import org.apache.cxf.rs.security.oauth2.common.Client; @@ -88,22 +87,18 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl at.setGrantCode(atReg.getGrantCode()); at.getExtraProperties().putAll(atReg.getExtraProperties()); - String certCnf = null; if (messageContext != null) { - certCnf = (String)messageContext.get(JoseConstants.HEADER_X509_THUMBPRINT_SHA256); + String certCnf = (String)messageContext.get(JoseConstants.HEADER_X509_THUMBPRINT_SHA256); + if (certCnf != null) { + // At a later stage we will likely introduce a dedicate Confirmation bean (as it is used in POP etc) + at.getExtraProperties().put(JoseConstants.HEADER_X509_THUMBPRINT_SHA256, certCnf); + } } if (isUseJwtFormatForAccessTokens()) { JwtClaims claims = createJwtAccessToken(at); - // At a later stage we will likely introduce a dedicate Confirmation bean (as it is used in POP etc) - if (certCnf != null) { - claims.setClaim(JwtConstants.CLAIM_CONFIRMATION, - Collections.singletonMap(JoseConstants.HEADER_X509_THUMBPRINT_SHA256, certCnf)); - } String jose = processJwtAccessToken(claims); at.setTokenKey(jose); - } else if (certCnf != null) { - at.getExtraProperties().put(JoseConstants.HEADER_X509_THUMBPRINT_SHA256, certCnf); } return at; http://git-wip-us.apache.org/repos/asf/cxf/blob/f8eec3e2/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java index 9561e0f..6913f31 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.oauth2.utils; import java.lang.reflect.Method; +import java.security.MessageDigest; import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -74,11 +75,13 @@ public final class OAuthUtils { private OAuthUtils() { } + public static byte[] createCertificateThumbprint(X509Certificate cert) throws Exception { + return MessageDigestUtils.createDigest(cert.getEncoded(), MessageDigestUtils.ALGO_SHA_256); + } public static void setCertificateThumbprintConfirmation(MessageContext mc, X509Certificate cert) { try { - byte[] thumbprint = - MessageDigestUtils.createDigest(cert.getEncoded(), MessageDigestUtils.ALGO_SHA_256); + byte[] thumbprint = createCertificateThumbprint(cert); String encodedThumbprint = Base64UrlUtility.encode(thumbprint); mc.put(JoseConstants.HEADER_X509_THUMBPRINT_SHA256, encodedThumbprint); } catch (Exception ex) { @@ -86,6 +89,16 @@ public final class OAuthUtils { } } + public static boolean compareCertificateThumbprints(X509Certificate cert, String encodedThumbprint) { + try { + byte[] thumbprint = createCertificateThumbprint(cert); + byte[] currentThumbrint = Base64UrlUtility.decode(encodedThumbprint); + return MessageDigest.isEqual(thumbprint, currentThumbrint); + } catch (Exception ex) { + return false; + } + } + public static boolean compareTlsCertificates(TLSSessionInfo tlsInfo, List<String> base64EncodedCerts) {
