001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: Client.java,v 1.6 2008/10/10 00:15:09 hengming Exp $
026 *
027 * Portions Copyrighted 2014 ForgeRock AS
028 */
029package com.sun.identity.liberty.ws.soapbinding;
030
031import com.sun.identity.common.HttpURLConnectionManager;
032import com.sun.identity.common.SystemConfigurationUtil;
033import com.sun.identity.shared.configuration.SystemPropertiesManager;
034
035import com.sun.identity.liberty.ws.security.SecurityUtils;
036import com.sun.identity.saml.xmlsig.JKSKeyProvider;
037
038import com.sun.identity.shared.xml.XMLUtils;
039import java.io.InputStream;
040import java.io.IOException;
041import java.io.OutputStream;
042
043import java.net.URL;
044import java.net.URLConnection;
045
046import java.security.KeyStore;
047import java.security.NoSuchAlgorithmException;
048import java.security.KeyStoreException;
049import java.security.Provider;
050import java.security.Security;
051
052import javax.net.ssl.HttpsURLConnection;
053import javax.net.ssl.KeyManager;
054import javax.net.ssl.KeyManagerFactory;
055import javax.net.ssl.SSLContext;
056import javax.net.ssl.TrustManager;
057import javax.net.ssl.TrustManagerFactory;
058import javax.net.ssl.X509KeyManager;
059
060import javax.xml.transform.dom.DOMSource;
061import javax.xml.transform.stream.StreamResult;
062import javax.xml.transform.Transformer;
063
064import org.w3c.dom.Document;
065import org.w3c.dom.Element;
066
067/**
068 * The <code>Client</code> class provides web service clients with a method to
069 * send requests using SOAP connection to web service servers.
070 *
071 * @supported.all.api
072 */
073public class Client {
074
075    private static KeyManager[] kms = null;
076    private static TrustManager[] tms = null;
077    private static X509KeyManager defaultX509km = null;
078    private static String defaultCertAlias = null;
079    private final static String SOAP_KEYSTORE_FILE_PROP =
080            "com.sun.identity.liberty.ws.soap.truststore";
081    private final static String SOAP_KEYSTORE_PASS_FILE_PROP =
082            "com.sun.identity.liberty.ws.soap.storepass";
083    private final static String SOAP_KEYSTORE_TYPE_PROP =
084            "com.sun.identity.liberty.ws.soap.storetype";
085    private final static String SOAP_PRIVATE_KEY_PASS_FILE_PROP  =
086            "com.sun.identity.liberty.ws.soap.keypass";
087    private final static String SOAP_TRUST_MNGR_PROP =
088            "com.sun.identity.liberty.ws.soap.trustmanager";
089    private final static String SOAP_TRUST_SECMNGR_ALGO_PROP =
090            "com.sun.identity.liberty.ws.soap.securitymanager.algorithm";
091    
092    static {
093        defaultCertAlias = SystemPropertiesManager.get(
094                "com.sun.identity.liberty.ws.soap.certalias");
095    }
096    
097    private Client() {}
098    
099    /**
100     * Sends a request to a SOAP endpoint and returns the response. The server
101     * only contains one servlet for different web services. So the SOAP
102     * endpoint URL has format 'servlet_URL/key'
103     *
104     * @param req the request
105     * @param connectTo the SOAP endpoint URL
106     * @return a response from the SOAP endpoint
107     * @throws SOAPBindingException if an error occurs while sending the
108     *                                 message
109     * @throws SOAPFaultException if the response is a SOAP Fault
110     */
111    public static Message sendRequest(Message req,String connectTo)
112    throws SOAPBindingException, SOAPFaultException {
113        return sendRequest(req, connectTo, null, null);
114    }
115    
116    /**
117     * Sends a request to a SOAP endpoint and returns the response. The server
118     * only contains one servlet for different web services. So the SOAP
119     * endpoint URL has format 'servlet_URL/key'.
120     *
121     * @param req the request message.
122     * @param connectTo the SOAP endpoint URL
123     * @param certAlias the cert alias of a client certificate being used in
124     *                  SSL
125     * @return a response from the SOAP endpoint
126     * @throws SOAPBindingException if an error occurs while sending the
127     *                                 message
128     * @throws SOAPFaultException if the response is a SOAP Fault
129     */
130    public static Message sendRequest(Message req,String connectTo,
131            String certAlias) throws SOAPBindingException, SOAPFaultException {
132        return sendRequest(req, connectTo, certAlias, null);
133    }
134    
135    /**
136     * Sends a request to a SOAP endpoint and returns the response. The server
137     * only contains one servlet for different web services. So the SOAP
138     * endpoint URL has format 'servlet_URL/key'.
139     *
140     * @param req the request message.
141     * @param connectTo the SOAP endpoint URL
142     * @param certAlias the cert alias of a client certificate
143     * @param soapAction the SOAPAction header
144     * @return a response from the SOAP endpoint
145     * @throws SOAPFaultException if a SOAP Fault occurs
146     * @throws SOAPBindingException if a error occurs while processing,
147     *                                 sending or receiving Message
148     */
149    public static Message sendRequest(Message req,String connectTo,
150            String certAlias,String soapAction)
151            throws SOAPBindingException, SOAPFaultException {
152        URLConnection con = null;
153        
154        try {
155            con = getConnection(connectTo, certAlias);
156        } catch (Exception e) {
157            Utils.debug.error("Client:sendRequest", e);
158            throw new SOAPBindingException(e.getMessage());
159        }
160        
161        if(soapAction == null || soapAction.length() == 0) {
162            soapAction = "";
163        }
164        
165        con.setRequestProperty(SOAPBindingConstants.SOAP_ACTION_HEADER,
166                soapAction);
167        
168        
169        Document doc = null;
170        int securityProfileType = req.getSecurityProfileType();
171        if (securityProfileType == Message.ANONYMOUS ||
172                securityProfileType == Message.BEARER_TOKEN) {
173            doc  = req.toDocument(true);
174        } else {
175            
176            Element sigElem = SecurityUtils.signMessage(req);
177            if (sigElem == null) {
178                String msg = Utils.bundle.getString("cannotSignRequest");
179                Utils.debug.error("Client.sendRequest: " + msg);
180                throw new SOAPBindingException(msg);
181            }
182            doc = sigElem.getOwnerDocument();
183        }
184        if (Utils.debug.messageEnabled()) {
185            Utils.debug.message("Client.sendRequest: signed request\n" + req);
186        }
187        
188        OutputStream os = null;
189        try {
190            os = con.getOutputStream();
191            Transformer transformer = XMLUtils.getTransformerFactory().newTransformer();
192            transformer.setOutputProperty("omit-xml-declaration", "yes");
193            transformer.transform(new DOMSource(doc.getDocumentElement()),
194                    new StreamResult(os));
195        } catch (Exception e) {
196            Utils.debug.error("Client:sendRequest", e);
197            throw new SOAPBindingException(e.getMessage());
198        } finally {
199            if (os != null) {
200                try {
201                    os.close();
202                } catch (Exception e) {
203                    Utils.debug.error("Client:sendRequest", e);
204                }
205            }
206        }
207        
208        Message resp = null;
209        InputStream is = null;
210        try {
211            is = con.getInputStream();
212            resp = new Message(is);
213            if (resp.getSOAPFault() != null) {
214                throw new SOAPFaultException(resp);
215            }
216            Utils.enforceProcessingRules(resp,
217                    req.getCorrelationHeader().getMessageID(), false);
218        } catch (IOException e) {
219            Utils.debug.error("Client:sendRequest", e);
220            throw new SOAPBindingException(e.getMessage());
221        } finally {
222            if (is != null) {
223                try {
224                    is.close();
225                } catch (Exception e) {
226                    Utils.debug.error("Client:sendRequest", e);
227                }
228            }
229        }
230        
231        resp.setProtocol(con.getURL().getProtocol());
232        if (resp.getSecurityProfileType() != Message.ANONYMOUS &&
233                !SecurityUtils.verifyMessage(resp)) {
234            
235            String msg = Utils.bundle.getString("cannotVerifySignature");
236            Utils.debug.error("Client.sendRequest: " + msg);
237            throw new SOAPBindingException(msg);
238        }
239        return resp;
240    }
241    
242    /**
243     * Gets URLConnection associated with the endpoint. If it is a SSL
244     * connection, the certAlias will be used to get the client certificate.
245     *
246     * @param endpoint the url of the SOAP receiver
247     * @param certAlias the cert alias of a client certificate
248     * @return a URLConnection object
249     * @throws Exception if an error occurs while connecting to server
250     */
251    private static URLConnection getConnection(String endpoint,String certAlias)
252    throws Exception {
253        URL url = new URL(endpoint);
254        URLConnection con = HttpURLConnectionManager.getConnection(url); 
255        
256        if (Utils.debug.messageEnabled()) {
257            Utils.debug.message("Client.getConnection: con class = " +
258                    con.getClass());
259        }
260        
261        if (con instanceof HttpsURLConnection) {
262            if (kms == null) {
263                initializeJSSE();
264            }
265            if (certAlias != null) {
266                kms[0] = new WSX509KeyManager(defaultX509km, certAlias);
267            } else {
268                kms[0] = new WSX509KeyManager(defaultX509km, defaultCertAlias);
269            }
270            
271            SSLContext ctx = SSLContext.getInstance("TLS");
272            ctx.init(kms, tms, null);
273            HttpsURLConnection scon = (HttpsURLConnection) con;
274            scon.setSSLSocketFactory(ctx.getSocketFactory());
275        } else {
276            if (Utils.debug.warningEnabled()) {
277                Utils.debug.warning("Client.getConnection: not instance of " +
278                        "HttpsURLConnection, client cert not selected.");
279            }
280        }
281        
282        con.setDoInput(true);
283        con.setDoOutput(true);
284        con.setRequestProperty("content-type", "text/xml");
285        
286        return con;
287    }
288    
289    
290    
291    
292    /**
293     * Initializes JSSE enviroment.
294     *
295     * @throws Exception if an error occurs while initializing JSSE
296     */
297    private static void initializeJSSE() throws Exception {
298        // put SunJSSE at fisrt place, so that JSSE will work
299        Provider provider = Security.getProvider("SunJSSE");
300        if (provider != null) {
301            Security.removeProvider("SunJSSE");
302            Security.insertProviderAt(provider, 1);
303        }
304        
305        String algorithm =  SystemPropertiesManager.get(
306                SOAP_TRUST_SECMNGR_ALGO_PROP);
307        if(algorithm == null || algorithm.length() <= 0) {
308            algorithm = "SunX509";
309        }
310        JKSKeyProvider jkskp = createKeyProvider() ;
311        KeyStore trustStore = jkskp.getKeyStore();
312        KeyManagerFactory kf = KeyManagerFactory.getInstance(algorithm);
313        kf.init(trustStore,jkskp.getPrivateKeyPass().toCharArray() );
314        
315        kms = kf.getKeyManagers();
316        defaultX509km = (X509KeyManager)kms[0];
317        
318        defineTrustManager(trustStore, algorithm);
319        
320    }
321    
322    /**
323     * Define a Trust manager
324     *
325     * @param trustStore the keystore used to store certificates
326     * @param algorithm the algorithm to user
327     * @throws Exception if an error occurs while instantiating the custom
328     * class or using the keystore
329     */
330    private static void defineTrustManager(KeyStore trustStore,
331            String algorithm)
332            throws SOAPBindingException{
333        boolean error = false ;
334        try{
335            TrustManagerFactory tf =
336                    TrustManagerFactory.getInstance(algorithm);
337            tf.init(trustStore);
338            TrustManager[] defaultTrustManagers = tf.getTrustManagers();
339            String trustManagerDefinition = SystemPropertiesManager.get(
340                    SOAP_TRUST_MNGR_PROP);
341            if(trustManagerDefinition != null
342                    && trustManagerDefinition.length() > 0) {
343                tms = new TrustManager[defaultTrustManagers.length + 1];
344                tms[0] = (TrustManager)
345                Class.forName(trustManagerDefinition).newInstance();
346                for(int i = 0; i < defaultTrustManagers.length; i++) {
347                    tms[i + 1 ] = defaultTrustManagers[i];
348                }
349            } else {                
350                tms = defaultTrustManagers;
351            }
352        }catch(ClassNotFoundException cnfe){
353            Utils.debug.error(
354                    "Client.defineTrustManager class not found: " ,cnfe);
355            error = true ;
356        }catch(InstantiationException ie){
357            Utils.debug.error(
358                    "Client.defineTrustManager cannot instantiate: " , ie);
359            error = true ;
360        }catch(NoSuchAlgorithmException nsae){
361            Utils.debug.error(
362                    "Client.defineTrustManager no algorithm: " , nsae);
363            error = true ;
364        }catch(IllegalAccessException iae){
365            Utils.debug.error(
366                    "Client.defineTrustManager illegal access: " , iae);
367            error = true ;
368        }catch(KeyStoreException kse){
369            Utils.debug.error(
370                    "Client.defineTrustManager keystore: " , kse);
371            error = true ;
372        }
373        if(error  ){
374            String msg = Utils.bundle.getString("cannotDefineTrustManager");
375            throw new SOAPBindingException(msg);
376        }
377        
378    }
379    
380    
381    
382    
383    /**
384     * Checks if  Trust Keystore properties are defined
385     * @return true if a specific trust store is to be used
386     **/
387    
388    private static boolean useSpecificTrustStore(){
389        return(
390                (SystemConfigurationUtil.getProperty(SOAP_KEYSTORE_FILE_PROP)!= null)&&
391                (SystemConfigurationUtil.getProperty(SOAP_KEYSTORE_PASS_FILE_PROP)!= null)&&
392                (SystemConfigurationUtil.getProperty(SOAP_KEYSTORE_TYPE_PROP)!= null)&&
393                (SystemConfigurationUtil.getProperty(SOAP_PRIVATE_KEY_PASS_FILE_PROP)!= null));
394    }
395    
396     /**
397     * Create a JKSKeyProvider using either default properties or specific properties 
398     * @return the JKSKeyProvider
399     **/
400       
401    private static JKSKeyProvider createKeyProvider() {
402        JKSKeyProvider jksKp  ;
403        if (useSpecificTrustStore()) {
404            jksKp = new JKSKeyProvider(
405                    SOAP_KEYSTORE_FILE_PROP,SOAP_KEYSTORE_PASS_FILE_PROP,
406                    SOAP_KEYSTORE_TYPE_PROP, SOAP_PRIVATE_KEY_PASS_FILE_PROP);
407        } else {
408            jksKp = new JKSKeyProvider();
409        }
410        return jksKp ;
411    }
412    
413    
414    
415    
416}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.