What I have:
WAS traditional 9.0 with EJB web service;
webservice client - java application;
SSL configured for only 9449 port as described here (one way http://www.ibm.com/developerworks/webservices/tutorials/ws-radsecurity3/ws-radsecurity3.html)
I need SSL mutual authentication, so I go to Quality of protection (QoP) settings, and set Client authentication = Required.
Up to this point all works fine.
Problem is that my EJB application needs client certificate's common name to obtain a user ID, which it will use in business logic. And here I failed.
Code snippet (web service side):
MessageContext context = wsContext.getMessageContext();
HttpServletRequest req = (HttpServletRequest)context.get(MessageContext.SERVLET_REQUEST) ;
System.out.println("!! isSecure " + req.isSecure());
X509Certificate[] certificates = (X509Certificate[]) req.getAttribute("java.servlet.request.X509Certificate");
if (null != certificates && certificates.length > 0) {
...
} else {
System.out.println("!! Empty certificates");
}
isSecure returnd true, but I get "Empty certificates" message.
My guess is maybe the reason is in following. When I output the SSL configuration used on 9449 port, the first line is "com.ibm.ssl.clientAuthenticationSupported = false" while through Admin Console it is set as Required.
com.ibm.websphere.ssl.JSSEHelper jsseHelper = com.ibm.websphere.ssl.JSSEHelper.getInstance();
java.util.Properties props = jsseHelper.getProperties("WebServiceConfigure");
System.out.println("!!! WebServiceConfigure = " + props.toString());
You might want to try the "direct connect" certificate properties. This was created to address intermediate (SSL-terminating) proxies (like a web server with plug-in) that issued a certificate different than the ultimate client. This property is
com.ibm.websphere.ssl.direct_connection_peer_certificates
You can determine whether you're getting the certificate from direct connect peer or proxied peer via com.ibm.websphere.webcontainer.is_direct_connection.
See also: WAS 9 doc page.
Related
I have some need to access client certificates before calling the endpoint, so how can I access client certificates ? It looks like I cannot access HttpServletRequest object if using NIO transport/connector.
So I tried changing WSO2 API manager to use blocking http/https by changing following files
1) axis.xml - uncomment the transport senders to receivers to use
normal http/https transport instead of PTT.
2) catalina-server.xml - change Connector protocol to default
(HTTP 1.1).
3) apimanager.xml - change all the references of http.nio.port
to mgt.transport.https.port
After this, the endpoints are redirecting to carbon homepage. The https port 9443 seems does not look for API endpoints.
Is there any other config to be added ?
Please point some direction. Thanks in advance.
1) If you're trying to achieve Mutual SSL, it's supported by APIM OOTB.
https://docs.wso2.com/display/AM220/Mutual+SSL+Support+for+API+Gateway
2) If you want to read the client certificate for some other reason, you can do it like this.
org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) messageContext).getAxis2MessageContext();
// try to retrieve the certificate
Object sslCertObject = axis2MessageContext.getProperty("ssl.client.auth.cert.X509");
if(sslCertObject != null) {
// if the certificate is available in the MessageContext, it means that mutual SSL validation has been done
// and succeed in the transport level.
// So, you can return tru here
//return true;
// Following are some additional steps
// retrieve certificate
javax.security.cert.X509Certificate[] certs = (javax.security.cert.X509Certificate[]) sslCertObject;
javax.security.cert.X509Certificate x509Certificate = certs[0];
// log the DN name of it
String dn = x509Certificate.getSubjectDN().getName();
log.info("Application is authenticate with certificate : " + dn);
// add certificate in to transport headers to send it to backend
setCertificateAsHeader(axis2MessageContext, x509Certificate);
return true;
} else {
// if certificate is not available in the MessageContext, it means that mutual SSL validation has been failed
// in the transport level.
// send 401 to client
handleAuthFailure(messageContext);
}
Ref: http://xacmlinfo.org/2015/06/02/securing-apis-using-mutual-ssl-with-wso2-api-manager/
Note: As per the blog post, you still needs to configure mutual ssl for this.
This is my first time creating a WCF service. I need to use HTTPS as I will be using MembershipBinding. The steps I have taken up to this point are:
Created a certificate authority using the makecert.exe application - from this I have created a server certificate and a client certificate.
Added the certificate authority to the Trusted Root Certification Authorities within Microsoft Management Console.
Added the client and server certificates to my personal certificates within Microsoft Management Console.
Created a https binding for the service in IIS using the server certificate.
Set the appropriate permissions for the app pool on the server certificate.
Defined the service certificate within the serviceBehaviours node in the web.config.
I am now testing the service using the WCF Test Client but I am getting the message:
Error: Cannot obtain Metadata from https://localhost:444/Service.svc If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address. For help enabling metadata publishing, please refer to the MSDN documentation at http://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata Exchange Error URI: https://localhost:444/Service.svc Metadata contains a reference that cannot be resolved: 'https://localhost:444/Service.svc'. Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost:444'. The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. The remote certificate is invalid according to the validation procedure.HTTP GET Error URI: https://localhost:444/Service.svc There was an error downloading 'https://localhost:444/Service.svc'. The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. The remote certificate is invalid according to the validation procedure.
The error suggests that there is an issue trusting the certificate but I have trusted the certificate authority used to create it so I don't know how to resolve it. The service worked fine when I was using http.
Thanks in advance.
As your certificate is self-signed, you need to add a hack to your client call :
using (MyWCFServiceClient client = new MyWCFServiceClient())
{
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback = TrustAllCertificatesCallback;
#endif
client.MyCall();
}
And the definition for TrustAllCertificatesCallback :
internal static bool TrustAllCertificatesCallback(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
{
bool isValid = true;
// TODO logic to check your self-signed certifiacte
return isValid;
}
The TrustAllCertificatesCallback callback should be deactivated on your production environement.
I have WCF web service that need to be secured using SSL/TLS protocol. In the other hand I have C++ client that consume WCF web service using gSOAP library. Already only server needs to have certificate. Now I have tasked to enforce client to have certificate. My earlier implementation for client is like this:
soap_ssl_init();
int soapResult = soap_ssl_client_context(soapPtr, SOAP_SSL_NO_AUTHENTICATION, "client.pem", NULL,
NULL, "cacert.pem", NULL);
if (soapResult)
{
soap_print_fault(soapPtr, stderr);
throw new ClientLogException("Can not use ssl for comminucations!");
}
else
{
}
struct soap mySoap = *soapPtr;
WSHttpBinding_USCOREILogServicesProxy proxy(mySoap);
input.request = &request;
int callCode = proxy.CallWebService(WEB_SERVICE_ADDRESS, NULL, &input, response);
if (callCode != 0)
{
cout << "Web service call code: " + callCode << endl;
throw new ClientLogException("Error in calling web service with call code: " + callCode);
}
which I does it from gSOAP documents. It works fine with only server required to have certificate. I viewed communication using WireShark and connection was completely encrypted.
Now for enforcing client to use certificate, I am going to use Nine simple steps to enable X.509 certificates on WCF article. But the article uses a C# WCF client. I must implement client configuration in my gSOAP C++ client. I can add client certificate in above code when calling soap_ssl_client_context and in third parameter.
I have 2 problem here:
1- I don't know is it possible calling web service that both client and server have certificates and communication be secured when server uses WCF and client uses gSOAP.
2- In the CodeProject article it seems that web service call is using http and I am wonder there is no encryption in communication.
In the end if anyone has better solution, or recommend other tools will be welcome.
HTTPS works out of the box with gsoap if you compile with -DWITH_OPENSSL and link against the OpenSSL libs. The out-of-the-box default settings will encrypt messages with https://, but this does not enforce authentication because you need to register the server certificates first with soap_ssl_client_context() as you point out.
To authenticate both server and client, the gsoap manual suggests the following:
int soapResult = soap_ssl_client_context(soapPtr,
SOAP_SSL_DEFAULT, // requires server to authenticate
"client.pem", // client cert (+public key) to authenticate to server
"password", // you need this when client.pem is encrypted
NULL, // capath to certs, when used
"cacert.pem", // should contain the server cert
NULL);
Also, you may need to convert PEM to CER (or the other way) for windows.
I have common web service interface on each endpoint applications (server and client).
How can I create port on client side without connecting to server for "the same" wsdl?
I search something what present this pseudocode:
MagicProxyFactory proxy = MagicProxyFactory.newInstance(MyServiceInterface.class);
/* then bind service address like that
((BindingProvider)proxy).getRequestContext()
.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http address");
*/
MyServiceInterface port = (MyServiceInterface) proxy.getPort();
Steps include:
Create the Service instance using the Service.create method
for which you need to know wsdl location, service name, and name space URL of the service.
e.g.
URL wsdlLocation = new URL("http://example.org/my.wsdl");
QName serviceName = new QName("http://example.org/sample", "MyService");
Service s = Service.create(wsdlLocation, serviceName);
Get the service proxy (service port for connecting) using Service.getPort() method.
For this you need to know endpoint implemenattion class name.
e.g. MyService port = s.getPort(MyService.class);
you can now call methods through proxy.
Using Metro? You can't. You need the WSDL to create the proxy. Either bundle the WSDL with your client (preferred) or use a URL from where the WSDL can be downloaded during proxy creation.
I have a client program that consumes a web service. It works quite well in a number of installations. Now I have a situation where a new customer connects to the internet via a proxy server, and my program's attempt to access the web service gets the "HTTP status 407: Proxy authentication required" error.
I thought that all the configuring of internet access, including proxy server address, port number and authentication would be done in the Control Panel Internet Options, and that I wouldn't have to worry about that in the code, or even in the app.config, of the Web Service client.
Have I got it all wrong?
What I have done in the mean time is give the user the chance to configure the proxy user name and password, and then in my code I do the following:
webServiceClient.ClientCredentials.UserName.UserName = configuredUsername;
webServiceClient.ClientCredentials.UserName.Password = configuredPassword;
But I don't know that this is the right thing. Because it seems to me that the above ClientCredentials would refer to the web service binding/security, not to the internet proxy server.
I suppose I can try it at the customer, but I'd rather be sure of what I'm doing first.
I found out how to do this thing, with the help of a contributor to another forum which in the flurry of trying all sorts of things I've forgotten. So thank you to that now forgotten person.
Here's the code that worked in the end (suitably disguised, but gives the right idea):
BasicHttpBinding binding = new BasicHttpBinding("APISoap"); /* APISoap is the name of the binding element in the app.config */
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
binding.UseDefaultWebProxy = false;
binding.ProxyAddress = new Uri(string.Format("http://{0}:{1}", proxyIpAddress, proxyPort));
EndpointAddress endpoint = new EndpointAddress("http://www.examplewebservice/api.asmx");
WebServiceClient client = new WebServiceClient(binding, endpoint);
client.ClientCredentials.UserName.UserName = proxyUserName;
client.ClientCredentials.UserName.Password = proxyPassword;