可以从 Erik Wramner 的博客中找到真相http://erikwramner.wordpress.com/2013/03/27/trust-self-signed-ssl-certificates-and-skip-host-name-verification-with-jax- ws
我包含了完整的解决方案,其中使用 Apache CXF 向自签名的 SharePoint https 服务发出 SOAP Web 服务请求:
NaiveSSLHelper.java
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.transport.http.HTTPConduit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.ws.BindingProvider;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
public class NaiveSSLHelper {
public static void makeWebServiceClientTrustEveryone(
Object webServicePort) {
if (webServicePort instanceof BindingProvider) {
BindingProvider bp = (BindingProvider) webServicePort;
Map requestContext = bp.getRequestContext();
requestContext.put(JAXWS_SSL_SOCKET_FACTORY, getTrustingSSLSocketFactory());
requestContext.put(JAXWS_HOSTNAME_VERIFIER,
new NaiveHostnameVerifier());
} else {
throw new IllegalArgumentException(
"Web service port "
+ webServicePort.getClass().getName()
+ " does not implement "
+ BindingProvider.class.getName());
}
}
public static SSLSocketFactory getTrustingSSLSocketFactory() {
return SSLSocketFactoryHolder.INSTANCE;
}
private static SSLSocketFactory createSSLSocketFactory() {
TrustManager[] trustManagers = new TrustManager[] {
new NaiveTrustManager()
};
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[0], trustManagers,
new SecureRandom());
return sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
return null;
}
}
public static void makeCxfWebServiceClientTrustEveryone(HTTPConduit http) {
TrustManager[] trustManagers = new TrustManager[]{
new NaiveTrustManager()
};
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setSecureSocketProtocol("TLS");
tlsParams.setKeyManagers(new KeyManager[0]);
tlsParams.setTrustManagers(trustManagers);
tlsParams.setDisableCNCheck(true);
http.setTlsClientParameters(tlsParams);
}
private interface SSLSocketFactoryHolder {
SSLSocketFactory INSTANCE = createSSLSocketFactory();
}
private static class NaiveHostnameVerifier implements
HostnameVerifier {
@Override
public boolean verify(String hostName,
SSLSession session) {
return true;
}
}
private static class NaiveTrustManager implements
X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
private static final java.lang.String JAXWS_HOSTNAME_VERIFIER =
"com.sun.xml.internal.ws.transport.https.client.hostname.verifier";
private static final java.lang.String JAXWS_SSL_SOCKET_FACTORY =
"com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory";
}
SoapTester.java
import crawler.common.sharepoint.stubs.sitedata.ArrayOfSList;
import crawler.common.sharepoint.stubs.sitedata.GetListCollectionResponse;
import crawler.common.sharepoint.stubs.sitedata.SList;
import crawler.common.sharepoint.stubs.sitedata.SiteData;
import crawler.common.sharepoint.stubs.sitedata.SiteDataSoap;
import org.apache.cxf.configuration.security.AuthorizationPolicy;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
import org.apache.cxf.transport.http.auth.HttpAuthHeader;
import org.apache.cxf.transport.http.auth.SpnegoAuthSupplier;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.ietf.jgss.GSSName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This example will invoke a web service on SharePoint 2013+ with optional kerberos auth.
*/
public class SoapTester {
private static final Logger LOG = LoggerFactory.getLogger(SoapTester.class);
public static void main(String[] args) {
String endpointAddress = args[0];
String keytabFilePath = args.length > 2 ? args[1] : null;
String principalName = args.length > 2 ? args[2] : null;
String servicePrincipalName = args.length > 3 ? args[3] : null;
if (!endpointAddress.endsWith("/")) {
endpointAddress += "/";
}
endpointAddress += "_vti_bin/SiteData.asmx";
final String endpointAddressFinal = endpointAddress;
Service service = Service.create(SiteData.SERVICE);
SiteDataSoap soap = service.getPort(SiteDataSoap.class);
NaiveSSLHelper.makeWebServiceClientTrustEveryone(soap);
BindingProvider bindingProvider = (BindingProvider) soap;
bindingProvider.getRequestContext().put(AsyncHTTPConduit.USE_ASYNC,
Boolean.TRUE);
bindingProvider.getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);
List<Handler> chain = bindingProvider.getBinding().getHandlerChain();
chain.add(new SOAPHandler<SOAPMessageContext>() {
@Override
public boolean handleMessage(SOAPMessageContext context) {
String endpointAddress = (String) context.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
SOAPMessage msg = context.getMessage();
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
msg.writeTo(out);
String str = new String(out.toByteArray());
LOG.info("Sharepoint xml [" + endpointAddress + "]" + (outbound ? " (Outbound)" : " (Inbound)") + ": " + str);
} catch (Exception e) {
LOG.error("Cannot get soap xml from message ", e);
}
if (outbound.booleanValue()) {
try {
context.getMessage().setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
}
@Override
public Set<QName> getHeaders() {
return null;
}
});
bindingProvider.getBinding().setHandlerChain(chain);
Client client = ClientProxy.getClient(bindingProvider);
client.getEndpoint().put("org.apache.cxf.stax.maxChildElements", System.getProperty("org.apache.cxf.stax.maxChildElements") != null ? System.getProperty("org.apache.cxf.stax.maxChildElements") : "5000000");
HTTPConduit http = (HTTPConduit) client.getConduit();
NaiveSSLHelper.makeCxfWebServiceClientTrustEveryone(http);
AuthorizationPolicy authorization = new AuthorizationPolicy();
authorization.setAuthorizationType(HttpAuthHeader.AUTH_TYPE_NEGOTIATE);
http.setAuthorization(authorization);
SpnegoAuthSupplier authSupplier = new SpnegoAuthSupplier();
if (servicePrincipalName != null) {
authSupplier.setServicePrincipalName(servicePrincipalName);
authSupplier.setServiceNameType(GSSName.NT_HOSTBASED_SERVICE);
}
Map<String, String> loginConfig = new HashMap<>();
loginConfig.put("useKeyTab", "true");
loginConfig.put("storeKey", "true");
loginConfig.put("refreshKrb5Config", "true");
loginConfig.put("keyTab", keytabFilePath);
loginConfig.put("principal", principalName);
loginConfig.put("useTicketCache", "true");
loginConfig.put("debug", String.valueOf(true));
authSupplier.setLoginConfig(new Configuration() {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
return new AppConfigurationEntry[] {
new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
loginConfig)};
}
});
http.setAuthSupplier(authSupplier);
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setAllowChunking(false);
httpClientPolicy.setAutoRedirect(true);
http.setClient(httpClientPolicy);
Holder<ArrayOfSList> vLists = new Holder<>();
Holder<Long> getListCollectionResult = new Holder<>();
soap.getListCollectionAsync(getListCollectionResult, vLists, res -> {
try {
GetListCollectionResponse listCollectionResponse = res.get();
ArrayOfSList arrayOfSList = listCollectionResponse.getVLists();
LOG.info("Successfully got {} lists from {}", arrayOfSList.getSList().size(), endpointAddressFinal);
for (SList slist : arrayOfSList.getSList()) {
LOG.info("Successfully got list {}", slist.getTitle());
}
System.exit(0);
} catch (Exception e) {
LOG.error("List collection response", e);
}
});
}
}
这是关于 JDK7 和 glassfish 的另一个示例。请注意 Nikolay Smirnov 的评论。我使用 jdk 7 和 glassfish 3.1.2。在这种环境中,如果服务器处理自签名证书,则建议的解决方案非常有效。
// import com.sun.xml.ws.developer.JAXWSProperties;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.ws.BindingProvider;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
/**
*
* Usage examples (BindingProvider port):
* NaiveSSLHelper.makeWebServiceClientTrustEveryone(port); // GlassFish
* NaiveSSLHelper.makeCxfWebServiceClientTrustEveryone(port); // TomEE
*
* Based on Erik Wramner's example frome here:
* http://erikwramner.wordpress.com/2013/03/27/trust-self-signed-ssl-certificates-and-skip-host-name-verification-with-jax-ws/
*
* I have extended the functionality when Apache CXF is used.
*/
public class NaiveSSLHelper {
private static final String JAXWS_HOSTNAME_VERIFIER = "com.sun.xml.ws.transport.https.client.hostname.verifier"; // JAXWSProperties.HOSTNAME_VERIFIER;
private static final String JAXWS_SSL_SOCKET_FACTORY = "com.sun.xml.ws.transport.https.client.SSLSocketFactory"; // JAXWSProperties.SSL_SOCKET_FACTORY;
// In Glassfish (Metro) environment you can use this function (Erik Wramner's solution)
public static void makeWebServiceClientTrustEveryone(Object webServicePort) {
if (webServicePort instanceof BindingProvider) {
BindingProvider bp = (BindingProvider) webServicePort;
Map requestContext = bp.getRequestContext();
requestContext.put(JAXWS_SSL_SOCKET_FACTORY, getTrustingSSLSocketFactory());
requestContext.put(JAXWS_HOSTNAME_VERIFIER, new NaiveHostnameVerifier());
} else {
throw new IllegalArgumentException(
"Web service port "
+ webServicePort.getClass().getName()
+ " does not implement "
+ BindingProvider.class.getName());
}
}
// In TomEE (Apache CXF) environment you can use this function (my solution)
public static void makeCxfWebServiceClientTrustEveryone(Object port) {
TrustManager[] trustManagers = new TrustManager[]{
new NaiveTrustManager()
};
Client c = ClientProxy.getClient(port);
HTTPConduit httpConduit = (HTTPConduit) c.getConduit();
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setSecureSocketProtocol("SSL");
tlsParams.setKeyManagers(new KeyManager[0]);
tlsParams.setTrustManagers(trustManagers);
tlsParams.setDisableCNCheck(true);
httpConduit.setTlsClientParameters(tlsParams);
}
public static SSLSocketFactory getTrustingSSLSocketFactory() {
return SSLSocketFactoryHolder.INSTANCE;
}
private static SSLSocketFactory createSSLSocketFactory() {
TrustManager[] trustManagers = new TrustManager[]{
new NaiveTrustManager()
};
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(new KeyManager[0], trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
return null;
}
}
private static interface SSLSocketFactoryHolder {
public static final SSLSocketFactory INSTANCE = createSSLSocketFactory();
}
private static class NaiveHostnameVerifier implements
HostnameVerifier {
@Override
public boolean verify(String hostName,
SSLSession session) {
return true;
}
}
private static class NaiveTrustManager implements
X509TrustManager {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs,
String authType) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs,
String authType) throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}
}