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 com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.oauth2.sdk.ParseException;
023import com.nimbusds.oauth2.sdk.SerializeException;
024import com.nimbusds.oauth2.sdk.http.HTTPRequest;
025import com.nimbusds.oauth2.sdk.id.ClientID;
026import com.nimbusds.oauth2.sdk.util.URLUtils;
027
028import javax.net.ssl.SSLSocketFactory;
029import java.security.cert.X509Certificate;
030import java.util.*;
031
032
033/**
034 * The base abstract class for mutual TLS client authentication at the Token
035 * endpoint.
036 */
037public abstract class TLSClientAuthentication extends ClientAuthentication {
038        
039        
040        /**
041         * The validated client X.509 certificate from the received HTTPS
042         * request, {@code null} for an outgoing HTTPS request.
043         */
044        protected final X509Certificate certificate;
045        
046        
047        /**
048         * The SSL socket factory for an outgoing HTTPS request, {@code null}
049         * to use the default one.
050         */
051        private final SSLSocketFactory sslSocketFactory;
052        
053        
054        /**
055         * Creates a new abstract mutual TLS client authentication. This
056         * constructor is intended for an outgoing token request.
057         *
058         * @param method           The client authentication method. Must not
059         *                         be {@code null}.
060         * @param clientID         The client identifier. Must not be
061         *                         {@code null}.
062         * @param sslSocketFactory The SSL socket factory to use for the
063         *                         outgoing HTTPS request and to present the
064         *                         client certificate(s), {@code null} to use
065         *                         the default one.
066         */
067        protected TLSClientAuthentication(final ClientAuthenticationMethod method,
068                                          final ClientID clientID,
069                                          final SSLSocketFactory sslSocketFactory) {
070                
071                super(method, clientID);
072                this.sslSocketFactory = sslSocketFactory;
073                certificate = null;
074        }
075        
076        
077        /**
078         * Creates a new abstract mutual TLS client authentication. This
079         * constructor is intended for a received token request.
080         *
081         * @param method      The client authentication method. Must not be
082         *                    {@code null}.
083         * @param clientID    The client identifier. Must not be {@code null}.
084         * @param certificate The validated client X.509 certificate from the
085         *                    received HTTPS request. Should not be
086         *                    {@code null}.
087         */
088        protected TLSClientAuthentication(final ClientAuthenticationMethod method,
089                                          final ClientID clientID,
090                                          final X509Certificate certificate) {
091                super(method, clientID);
092                sslSocketFactory = null;
093                this.certificate = certificate;
094        }
095        
096        
097        /**
098         * Returns the SSL socket factory to use for an outgoing HTTPS request
099         * and to present the client certificate(s).
100         *
101         * @return The SSL socket factory, {@code null} to use the default one.
102         */
103        public SSLSocketFactory getSSLSocketFactory() {
104                
105                return sslSocketFactory;
106        }
107        
108        
109        /**
110         * The validated client X.509 certificate from the received HTTPS
111         * request.
112         *
113         * @return The validated client X.509 certificate from the received
114         *         HTTPS request, {@code null} for an outgoing HTTPS request.
115         */
116        public X509Certificate getClientX509Certificate() {
117                
118                return certificate;
119        }
120        
121        
122        @Override
123        public Set<String> getFormParameterNames() {
124                
125                return Collections.singleton("client_id");
126        }
127        
128        
129        @Override
130        public void applyTo(final HTTPRequest httpRequest) {
131                
132                if (httpRequest.getMethod() != HTTPRequest.Method.POST)
133                        throw new SerializeException("The HTTP request method must be POST");
134                
135                ContentType ct = httpRequest.getEntityContentType();
136                
137                if (ct == null)
138                        throw new SerializeException("Missing HTTP Content-Type header");
139                
140                if (ct.matches(ContentType.APPLICATION_JSON)) {
141                        
142                        // Possibly request object POST request, nothing to set
143                        
144                } else if (ct.matches(ContentType.APPLICATION_URLENCODED)) {
145                        
146                        // Token or similar request
147                        Map<String, List<String>> params = new LinkedHashMap<>();
148                        try {
149                                params.putAll(httpRequest.getBodyAsFormParameters());
150                        } catch (ParseException e) {
151                                throw new SerializeException(e.getMessage(), e);
152                        }
153                        params.put("client_id", Collections.singletonList(getClientID().getValue()));
154                        httpRequest.setBody(URLUtils.serializeParameters(params));
155
156                } else {
157                        throw new SerializeException("The HTTP Content-Type header must be " + ContentType.APPLICATION_URLENCODED);
158                }
159                
160                // If set for an outgoing request
161                httpRequest.setSSLSocketFactory(sslSocketFactory);
162        }
163}