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.Map;
023import javax.net.ssl.SSLSocketFactory;
024
025import com.nimbusds.oauth2.sdk.ParseException;
026import com.nimbusds.oauth2.sdk.http.HTTPRequest;
027import com.nimbusds.oauth2.sdk.id.ClientID;
028import com.nimbusds.oauth2.sdk.util.URLUtils;
029import net.jcip.annotations.Immutable;
030import org.apache.commons.lang3.StringUtils;
031
032
033/**
034 * Client TLS / X.509 certificate authentication at the Token endpoint.
035 * Implements {@link ClientAuthenticationMethod#TLS_CLIENT_AUTH}.
036 *
037 * <p>Related specifications:
038 *
039 * <ul>
040 *     <li>Mutual TLS Profile for OAuth 2.0 (draft-ietf-oauth-mtls-02), section
041 *         2.
042 * </ul>
043 */
044@Immutable
045public class TLSClientAuthentication extends ClientAuthentication {
046        
047        
048        /**
049         * The SSL socket factory for the outgoing HTTPS requests, {@code null}
050         * to use the default one.
051         */
052        private final SSLSocketFactory sslSocketFactory;
053        
054        
055        /**
056         * The validated client X.509 certificate from the received HTTPS
057         * request, {@code null} for an outgoing HTTPS request.
058         */
059        private final X509Certificate x509Certificate;
060        
061        
062        /**
063         * Creates a new TLS / X.509 certificate client authentication. This
064         * constructor is intended for an outgoing token request.
065         *
066         * @param clientID         The client identifier. Must not be
067         *                         {@code null}.
068         * @param sslSocketFactory The SSL socket factory to use for the
069         *                         outgoing HTTPS request and to present the
070         *                         client certificate(s), {@code null} to use
071         *                         the default one.
072         */
073        public TLSClientAuthentication(final ClientID clientID,
074                                       final SSLSocketFactory sslSocketFactory) {
075                
076                super(ClientAuthenticationMethod.TLS_CLIENT_AUTH, clientID);
077                this.sslSocketFactory = sslSocketFactory;
078                x509Certificate = null;
079        }
080        
081        
082        /**
083         * Creates a new TLS / X.509 certificate client authentication. This
084         * constructor is intended for a received token request.
085         *
086         * @param clientID        The client identifier. Must not be
087         *                        {@code null}.
088         * @param x509Certificate The validated client X.509 certificate from
089         *                        the received HTTPS request. Must not be
090         *                        {@code null}.
091         */
092        public TLSClientAuthentication(final ClientID clientID,
093                                       final X509Certificate x509Certificate) {
094                super(ClientAuthenticationMethod.TLS_CLIENT_AUTH, clientID);
095                sslSocketFactory = null;
096                
097                if (x509Certificate == null) {
098                        throw new IllegalArgumentException("The client X.509 certificate must not be null");
099                }
100                
101                this.x509Certificate = x509Certificate;
102        }
103        
104        
105        /**
106         * Returns the SSL socket factory to use for the outgoing HTTPS request
107         * and to present the client certificate(s).
108         *
109         * @return The SSL socket factory, {@code null} to use the default one.
110         */
111        public SSLSocketFactory getSSLSocketFactory() {
112                
113                return sslSocketFactory;
114        }
115        
116        
117        /**
118         * Returns the validated client X.509 certificate from the received
119         * HTTPS request.
120         *
121         * @return The client X.509 certificate, {@code null} for an outgoing
122         *         HTTPS request.
123         */
124        public X509Certificate getClientX509Certificate() {
125                
126                return x509Certificate;
127        }
128        
129        
130        @Override
131        public void applyTo(final HTTPRequest httpRequest) {
132        
133                httpRequest.setSSLSocketFactory(sslSocketFactory);
134        }
135        
136        
137        /**
138         * Parses a client TLS / X.509 certificate authentication from the
139         * specified HTTP request.
140         *
141         * @param httpRequest The HTTP request to parse. Must not be
142         *                    {@code null} and must include a validated client
143         *                    X.509 certificate.
144         *
145         * @return The client TLS / X.509 certificate authentication.
146         *
147         * @throws ParseException If the {@code client_id} or client X.509
148         *                        certificate is missing.
149         */
150        public static TLSClientAuthentication parse(final HTTPRequest httpRequest)
151                throws ParseException {
152                
153                String query = httpRequest.getQuery();
154                
155                if (query == null) {
156                        throw new ParseException("Missing HTTP POST request entity body");
157                }
158                
159                Map<String,String> params = URLUtils.parseParameters(query);
160                
161                String clientIDString = params.get("client_id");
162                
163                if (StringUtils.isBlank(clientIDString)) {
164                        throw new ParseException("Missing client_id parameter");
165                }
166                
167                X509Certificate cert = httpRequest.getClientX509Certificate();
168                
169                if (cert == null) {
170                        throw new ParseException("Missing client X.509 certificate");
171                }
172                
173                return new TLSClientAuthentication(new ClientID(clientIDString), cert);
174        }
175}