Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 58483200C5C for ; Thu, 20 Apr 2017 17:53:25 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 56E43160B9F; Thu, 20 Apr 2017 15:53:25 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 78035160B91 for ; Thu, 20 Apr 2017 17:53:24 +0200 (CEST) Received: (qmail 12305 invoked by uid 500); 20 Apr 2017 15:53:23 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 12296 invoked by uid 99); 20 Apr 2017 15:53:23 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 20 Apr 2017 15:53:23 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 8EA24F4A1F; Thu, 20 Apr 2017 15:53:23 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: sergeyb@apache.org To: commits@cxf.apache.org Message-Id: <76ec825cef79488893e2fa15459f8591@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: cxf git commit: Prototyping the X509 client cert token binding validation cvode, tests to follow next Date: Thu, 20 Apr 2017 15:53:23 +0000 (UTC) archived-at: Thu, 20 Apr 2017 15:53:25 -0000 Repository: cxf Updated Branches: refs/heads/master ba9fa0e7c -> cbb8261fc 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/cbb8261f Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/cbb8261f Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/cbb8261f Branch: refs/heads/master Commit: cbb8261fc8f7261b8ef91a5931a2b343a7177d33 Parents: ba9fa0e Author: Sergey Beryozkin Authored: Thu Apr 20 16:52:54 2017 +0100 Committer: Sergey Beryozkin Committed: Thu Apr 20 16:52:54 2017 +0100 ---------------------------------------------------------------------- .../oauth2/filters/OAuthRequestFilter.java | 15 +++++++++++++++ .../oauth2/provider/AbstractOAuthDataProvider.java | 15 +++++---------- .../cxf/rs/security/oauth2/utils/OAuthUtils.java | 17 +++++++++++++++-- 3 files changed, 35 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/cbb8261f/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 02d238f..a485c1e 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,6 +157,15 @@ 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); @@ -346,4 +358,7 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator this.issuer = issuer; } + private TLSSessionInfo getTlsSessionInfo() { + return (TLSSessionInfo)getMessageContext().get(TLSSessionInfo.class.getName()); + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/cbb8261f/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 22568cb..75e24d4 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; @@ -89,22 +88,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/cbb8261f/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 66eac5a..6e38944 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 base64EncodedCerts) {