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