001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.auth;
019
020
021import java.security.cert.X509Certificate;
022import java.util.List;
023import java.util.Map;
024import javax.net.ssl.SSLSocketFactory;
025
026import com.nimbusds.oauth2.sdk.ParseException;
027import com.nimbusds.oauth2.sdk.http.HTTPRequest;
028import com.nimbusds.oauth2.sdk.id.ClientID;
029import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
030import com.nimbusds.oauth2.sdk.util.StringUtils;
031import com.nimbusds.oauth2.sdk.util.URLUtils;
032import net.jcip.annotations.Immutable;
033
034
035/**
036 * Self-signed certificate mutual TLS client authentication at the Token
037 * endpoint. The client certificate is self-signed, as opposed to
038 * {@link TLSClientAuthentication tls_client_auth} which relies on PKI binding.
039 * Implements {@link ClientAuthenticationMethod#SELF_SIGNED_TLS_CLIENT_AUTH}.
040 *
041 * <p>Related specifications:
042 *
043 * <ul>
044 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
045 *         Access Tokens (draft-ietf-oauth-mtls-08), section 2.2.
046 * </ul>
047 */
048@Immutable
049public class SelfSignedTLSClientAuthentication extends AbstractTLSClientAuthentication {
050        
051        
052        /**
053         * The validated client X.509 certificate from the received HTTPS
054         * request, {@code null} for an outgoing HTTPS request.
055         */
056        private final X509Certificate x509Certificate;
057        
058        
059        /**
060         * Creates a new self-signed certificate mutual TLS client
061         * authentication. This constructor is intended for an outgoing token
062         * request.
063         *
064         * @param clientID         The client identifier. Must not be
065         *                         {@code null}.
066         * @param sslSocketFactory The SSL socket factory to use for the
067         *                         outgoing HTTPS request and to present the
068         *                         client certificate(s), {@code null} to use
069         *                         the default one.
070         */
071        public SelfSignedTLSClientAuthentication(final ClientID clientID,
072                                                 final SSLSocketFactory sslSocketFactory) {
073                
074                super(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH, clientID, sslSocketFactory);
075                x509Certificate = null;
076        }
077        
078        
079        /**
080         * Creates a new self-signed certificate mutual TLS client
081         * authentication. This constructor is intended for a received token
082         * request.
083         *
084         * @param clientID        The client identifier. Must not be
085         *                        {@code null}.
086         * @param x509Certificate The validated client X.509 certificate from
087         *                        the received HTTPS request. Must not be
088         *                        {@code null}.
089         */
090        public SelfSignedTLSClientAuthentication(final ClientID clientID,
091                                                 final X509Certificate x509Certificate) {
092                
093                super(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH, clientID);
094                
095                if (x509Certificate == null) {
096                        throw new IllegalArgumentException("The client X.509 certificate must not be null");
097                }
098                
099                this.x509Certificate = x509Certificate;
100        }
101        
102        
103        /**
104         * Returns the validated client X.509 certificate from the received
105         * HTTPS request.
106         *
107         * @return The client X.509 certificate, {@code null} for an outgoing
108         *         HTTPS request.
109         */
110        public X509Certificate getClientX509Certificate() {
111                
112                return x509Certificate;
113        }
114        
115        
116        /**
117         * Parses a self-signed certificate mutual TLS client authentication
118         * from the specified HTTP request.
119         *
120         * @param httpRequest The HTTP request to parse. Must not be
121         *                    {@code null} and must include a validated client
122         *                    X.509 certificate.
123         *
124         * @return The self-signed TLS / X.509 certificate client
125         *         authentication.
126         *
127         * @throws ParseException If the {@code client_id} or client X.509
128         *                        certificate is missing.
129         */
130        public static SelfSignedTLSClientAuthentication parse(final HTTPRequest httpRequest)
131                throws ParseException {
132                
133                String query = httpRequest.getQuery();
134                
135                if (query == null) {
136                        throw new ParseException("Missing HTTP POST request entity body");
137                }
138                
139                Map<String,List<String>> params = URLUtils.parseParameters(query);
140                
141                String clientIDString = MultivaluedMapUtils.getFirstValue(params, "client_id");
142                
143                if (StringUtils.isBlank(clientIDString)) {
144                        throw new ParseException("Missing client_id parameter");
145                }
146                
147                X509Certificate cert = httpRequest.getClientX509Certificate();
148                
149                if (cert == null) {
150                        throw new ParseException("Missing client X.509 certificate");
151                }
152                
153                return new SelfSignedTLSClientAuthentication(new ClientID(clientIDString), cert);
154        }
155}