I am trying to find example code on how to get this to work. Without having to specify any certificates on the client side. Does anyone have an example of how to do this?
All examples I have found online use SSL/TLS for authentication as well as transport security.
Yes.
You need to add an auth_mechanisms = PLAIN or AMQPPLAIN in rabbitmq.conf
Configure certificates in the server and client.
It isn't needed to use rabbitmq_auth_mechanism_ssl plugin
Using Java you need to create a KeyStore, KeyManagerFactory and TrustManagerFactory, and send your certificate.
Using Python you'll need to use ssl package and create a context and load a certificate chain and send to the server your certificate.
After configuring ssl as in the documentation, using Java create a factory, and set a user and a password. Using Python create a PlainCredentials.
In Python will be something like this:
import pika
import sys
import ssl
context = ssl.create_default_context(cafile="/home/userx/rabbitmq/certs/testca/ca.crt")
context.load_cert_chain("/home/userx/rabbitmq/certs/client/client_certificate.pem", "/home/userx/rabbitmq/certs/client/private_key.pem")
ssl_options = pika.SSLOptions(context, server_hostname="xrabbit")
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations('/home/userx/rabbitmq/certs/testca/ca.crt')
credentials = pika.PlainCredentials('user1', 'password%!xyz$328')
parameters = pika.ConnectionParameters('xrabbit',5671,'/',credentials, ssl_options=ssl_options)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
In Java something like this:
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("/home/userx/rabbitmq/certs/client_keystore.pkcs12"), keyPassphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, keyPassphrase);
char[] trustPassphrase = "rabbit".toCharArray();
KeyStore tks = KeyStore.getInstance("PKCS12");
tks.load(new FileInputStream("/home/userx/rabbitmq/certs/trust_store"), trustPassphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");tmf.init(tks);
SSLContext c = SSLContext.getInstance("TLSv1.3");
c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("xrabbit");
factory.setPort(5671);
factory.setUsername("user1");
factory.setPassword("password$321xk!!44");
factory.setSaslConfig(DefaultSaslConfig.PLAIN);
factory.useSslProtocol(c);
factory.enableHostnameVerification();
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
Finally, use Wireshark to check the connection, only TLS packets should appear, if your client connection is using only plain text, will appear TLS packets from the server, but your client will send AMQP packets, if your server and client are using TLS you'll see only TLS packets.
Follow the official website documentation:
https://www.rabbitmq.com/ssl.html
You can find the configuration with and without client side configuration.
Related
I've implemented a SOAP client in python with the zeep library. Some of the endpoints require client-side certificate authentication, thus the need to attach the certificate to the python requests's session.
After googling around, I've come up with:
from zeep import Client
from zeep.transports import Transport
from django.utils import timezone
import requests
......
session = requests.Session()
session.verify = False
session.cert= ('pat_to_cert.pem','path_to_privKey.pem')
transport = Transport(session=session)
....
client = Client(wsdl=wsdl, transport=transport)
send = getattr(service, service_name)
result = send(**data)
Debbugging the TLS handshake, the server issues a Certificate Request, but the client replies with an empty certificate. I've already checked the .pem files with openssl with no errors.
Is it possible that python's requests is not attaching the certificate because it does not recognize the server name? How can I enforce to use this certificate for every request?
In your code, you are setting session.verify = False that's why TLS verification is disabled.
what you need to do is to set:
session.verify = 'path/to/my/certificate.pem'
Also, Alternatively, instead of using session.verify you can use session.cert if you just want to use an TLS client certificate:
session.verify = True
session.cert = ('my_cert', 'my_key')
please check the documentation for more details: https://docs.python-zeep.org/en/master/transport.html
hope this helps
I am trying to understand certificate based authentication using the msdn sample https://msdn.microsoft.com/en-us/library/ms731074(v=vs.90).aspx
This is the server code:
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
// Create the URI for the endpoint.
Uri httpUri = new Uri("https://localhost/Calculator");
// Create the service and add an endpoint.
ServiceHost myServiceHost = new ServiceHost(typeof(ServiceModel.Calculator), httpUri);
myServiceHost.AddServiceEndpoint(typeof(ServiceModel.ICalculator), binding, "");
// Open the service.
myServiceHost.Open();
Console.WriteLine("Listening...");
Console.ReadLine();
// Close the service.
myServiceHost.Close();
This is the client code I wrote:
ChannelFactory<ICalculator> factory = null;
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
EndpointAddress address = new EndpointAddress("https://localhost/Calculator");
factory = new ChannelFactory<ICalculator>(binding, address);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
factory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "sroger");
ICalculator channel = factory.CreateChannel();
int y = channel.add(9, 8);
I am getting the following exception:
An unhandled exception of type 'System.ServiceModel.CommunicationException' occurred in mscorlib.dll
Additional information: An error occurred while making the HTTP request to https://localhost/Calculator. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.
I am running both client and server from the same machine. And "sroger" is the certificate in my current user\ personal\certificates which corresponds to my machine name..
Not sure what to do from here..Any thoughts?
In the server code what certificate server uses?
Thanks
Gulumal.
https://msdn.microsoft.com/en-us/library/ms731074(v=vs.90).aspx example you used is incomplete.
Consuming https wcf service requires a valid server certificate to work, in your case both client and server certificates are required.
This is because both client and server need to trust each other in a HTTPS connection.
To get started, read https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-mutual-certificates which is a more complete example that includes specifying certificate to authenticate the service.
For a hosted WCF library via https to work you need to do the following in order:
Configure the port with an X.509 certificate (which has been
answered in
webHttpBinding with certificate)
From your server, create certificate request for common name of your
server fully qualified domain name, or at-least including a DNS subjectAltName of your server fully qualified domain name.
(there are different ways to do this, you may already know this
though)
Issue certificate and install certificate on your server
Grab application id from assembly file of your App that hosts WCF
library (i.e [assembly:
Guid("5870aeed-caca-4734-8b09-5c0615402bcf")]) Grab the certificate
thumbprint by viewing certificate properties.
As administrator, open
CMD and run this command to bind X.509 certificate to the port used
by your app on server
netsh http add sslcert ipport=0.0.0.0:443 certhash= appid={} certstorename=MY
netsh http add iplisten ipaddress=0.0.0.0:443
Add this to your server code:
myServiceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySerialNumber, "<certificate thumbprint>");
In your client code, reference your server address by fully qualified domain name that certificate that is specified as certificate Common Name or subject Alt Name
I have WCF service where I have implemented message security using certificate. But when I try to connect WCF service from my client application, I am getting following error :
The caller was not authenticated by the service.
My configuration settings are as below :
Service Settings :
ServiceHost host = new ServiceHost(typeof(HostService));
NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.Message);
tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
host.AddServiceEndpoint(typeof(IHostService), tcpBinding, "net.tcp://192.168.39.28:8000/HostService");
host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "server_cert");
Client Settings :
NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.Message);
tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
DuplexChannelFactory<IHostService> serviceFactory = new DuplexChannelFactory<IHostService>(new InstanceContext(MainWindow), tcpBinding, "net.tcp://192.168.39.28:8000/HostService");
serviceFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "client_cert");
serviceFactory.CreateChannel();
where I have created server_cert and client_cert certificates using makecert command. Can you please guide me what I missed ?
debugging certificate related issue is a big pain, I highly recommend to use wireshark. in your case, it's possible you client side didn't even send out the certificate. if the client cert is signed by another cert(s), make sure put it(them) into the trusted root on both client and server.
I have WCF service, using both BasicHttpBinding and NetTcpBinding at different endpoints within one ServiceHost. NetTcp is using a self signed certificate, which is loaded from file, all were well untill I try to actually make use of the BasicHttpBinding, so I do:
On server:
var ServiceHost host = new ServiceHost(blah blah);
host.Credentials.ServiceCertificate.Certificate = GetCertificate(); //load a certificate from file
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
var httpBinding = new BasicHttpBinding();
httpBinding.Security.Mode = BasicHttpSecurityMode.Transport;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
On Client:
ChannelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
var cer = GetCertificate();
ChannelFactory.Credentials.ClientCertificate.Certificate = cer;
var httpBinding = new BasicHttpBinding();
httpBinding.Security.Mode = BasicHttpSecurityMode.Transport;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
//accept any cert
System.Net.ServicePointManager.ServerCertificateValidationCallback =
((sender, certificate, chain, sslPolicyErrors) => true);
However when connects, I got this error
Exception - An error occurred while
making the HTTP request to
https://localhost/MyService. This
could be due to the fact that the
server certificate is not configured
properly with HTTP.SYS in the HTTPS
case. This could also be caused by a
mismatch of the security binding
between the client and the server.
certificate is not installed, and it worked fine with net tcp binding, I guess I must missed something small?
One thing I notice is net.tcp is duplex channel while basic http is simplex, I am sure there is a difference to setup? For example, I needed to load certificate at both end for net.tcp, what happens to basic http then?
Thanks in advance
Certificate for HTTPS is not configured in WCF configuration. You must configure certificate for http.sys. To do that use netsh.exe from command line with elevated privileges. If you are hosting your service in IIS/WAS you don't have to use netsh and you can configure HTTPS directly in IIS.
I'd like to connect to a Websphere 6.0 MQ via Java. I have already working code for a "normal" queue, but now I need to access a new queue which is SSL encrypted (keystore). I have been sent a file called something.jks, which I assume is a certificate I need to store somewhere. I have been searching the net, but I can't find the right information.
This is the code I use for the "normal" queue. I assume I need to set some property, but not sure which one.
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setChannel(channel_);
connectionFactory.setHostName(hostname_);
connectionFactory.setPort(port_);
connectionFactory.setQueueManager(queueManager_);
connectionFactory.setTransportType(1);
connectionFactory.setSSsetSSLCertStores(arg0)
Connection connection = connectionFactory.createConnection();
connection.setExceptionListener(this);
session_ = connection.createSession(DEFAULT_TRANSACTED, DEFAULT_ACKMODE);
connection.start();
javax.jms.Queue fQueue = session_.createQueue(queue_);
consumer = session_.createConsumer(fQueue);
Alex Fehners tutorial in developerWorks is a bit old (2005) but has code samples that should work for you.
SSL configuration of the Websphere MQ Java/JMS client
Your Java app will authenticate the QMgr based on its certificate. That means the jks file you were provided must have either the QMgr's self-signed certificate or it will have the root certificate of a Certificate Authority that signed the QMgr's certificate. In either case you point to the file using the -Djavax.net.ssl.trustStore=<location of trustStore> as noted in the article linked above. If the jks has a password, you will need to specify -Djavax.net.ssl.trustStorePassword=<password> as well. Authenticating the QMgr with a truststore is always required. The next part may or may not be required.
The other piece of the puzzle is that the QMgr may require your app to present a certificate. In other words, the QMgr cert is always authenticated, whether the app is required to authenticate is optional. If it is then you have what is known as "mutual authentication". If the channel that you connect to has been configured with SSLCAUTH(REQUIRED) then mutual auth has been enabled and the QMgr must have your application's self-signed cert or a CA root cert that signed your app's cert in its keystore. Hopefully whoever set up your jks file will have arranged for this already.
Assuming mutual auth is required, then your jks will have, in addition to the QMgr's trusted cert, a private cert representing your application. To get the app to fetch the cert and present it to the QMgr, you use the -Djavax.net.ssl.keyStore=<location of keyStore> and -Djavax.net.ssl.keyStorePassword=<password> parameters. Note these say key store whereas the previous parms said trust store.
My recommendation is to work with the WMQ administrator to set up and test the SSL connection. The first phase should be to test the channel with SSLCAUTH(OPTIONAL). This verifies that the application can resolve and authenticate the QMgr's certificate. Only when you get this working would the WMQ admin then change the channel to SSLCAUTH(REQUIRED) which tests authentication in the reverse direction.
I would highly recommend that you use the WMQ v7 client for a new application. This is for two reasons: 1) v6 is end-of-life as of Sept 2011; 2) the v7 code has a lot more diagnostic capability built in. The v7 client code is completely compatible with a v6 QMgr and works like the v6 client. You just don't get the v7 functionality. Download the WMQ client code free here:
IBM - MQC7: WebSphere MQ V7.0 Clients
I'm running the WMQ Hands-On Security Lab at IMPACT this year and will be posting the scripts and lab guide over the weekend at http://t-rob.net so check back for that.
Using SSL from the Oracle JVM (JSSE)
See also "What TLS cipherspecs/ciphersuites are supported when connecting from Oracle Java (non-IBM JRE) to MQ queue manager?"
In MQ Client version 8.0.0.2 there is a patch is included to use the TLS with Oracle JVM, this works with lanes answer above
The get this to work you will need the latest MQ Client that contains
IV66840: WMQ V7 JAVA/JMS: ADD SUPPORT FOR SELECTED TLS CIPHERSPECS WHEN
RUNNING IN NON-IBM JAVA RUNTIME ENVIRONMENT
http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840
(download)
Depending on your location you may also need to install
Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8 (download)
To use this you have to configured by using the JVM argument:
-Dcom.ibm.mq.cfg.useIBMCipherMappings=false
Note that the default security implementation behaviour differs between Oracle and IBM JVMs :
The Oracle JSSE Reference guide says:
If the KeyManager[] parameter is null, then an empty KeyManager will
be defined for this context.
The IBM JSSE Reference guide says:
If the KeyManager[] paramater is null, the installed security
providers will be searched for the highest-priority implementation of
the KeyManagerFactory, from which an appropriate KeyManager will be
obtained.
Which means that you have to setup your own ssl context
SSLContext sslcontext = SSLContext.getInstance("TLS");
String keyStore = System.getProperty("javax.net.ssl.keyStore");
String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword","");
KeyManager[] kms = null;
if (keyStore != null)
{
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(keyStoreType);
if (keyStore != null && !keyStore.equals("NONE")) {
fs = new FileInputStream(keyStore);
ks.load(fs, keyStorePassword.toCharArray());
if (fs != null)
fs.close();
char[] password = null;
if (keyStorePassword.length() > 0)
password = keyStorePassword.toCharArray();
kmf.init(ks,password);
kms = kmf.getKeyManagers();
}
sslcontext.init(kms,null,null);
And then supply that to the MQ JMS client:
JmsConnectionFactory cf = ...
MQConnectionFactory mqcf = (MQConnectionFactory) cf;
mqcf.setSSLSocketFactory(sslcontext.getSocketFactory());
If using a application server this might be handled by your application server.
Try this code along with T.Robs explanations about the certificate:
import com.ibm.mq.jms.*;
import java.io.FileInputStream;
import java.io.Console;
import java.security.*;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import com.ibm.mq.jms.MQQueueConnectionFactory;
public class SSLTest {
public static void main(String[] args) {
System.out.println(System.getProperty("java.home"));
String HOSTNAME = "myhost";
String QMGRNAME = "MyQMGR";
String CHANNEL = "MY.SVRCONN";
String SSLCIPHERSUITE = "TLS_RSA_WITH_AES_256_CBC_SHA";
try {
Class.forName("com.sun.net.ssl.internal.ssl.Provider");
System.out.println("JSSE is installed correctly!");
Console console = System.console();
char[] KSPW = console.readPassword("Enter keystore password: ");
// instantiate a KeyStore with type JKS
KeyStore ks = KeyStore.getInstance("JKS");
// load the contents of the KeyStore
ks.load(new FileInputStream("/home/hudo/hugo.jks"), KSPW);
System.out.println("Number of keys on JKS: "
+ Integer.toString(ks.size()));
// Create a keystore object for the truststore
KeyStore trustStore = KeyStore.getInstance("JKS");
// Open our file and read the truststore (no password)
trustStore.load(new FileInputStream("/home/xwgztu2/xwgztu2.jks"), null);
// Create a default trust and key manager
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
// Initialise the managers
trustManagerFactory.init(trustStore);
keyManagerFactory.init(ks,KSPW);
// Get an SSL context.
// Note: not all providers support all CipherSuites. But the
// "SSL_RSA_WITH_3DES_EDE_CBC_SHA" CipherSuite is supported on both SunJSSE
// and IBMJSSE2 providers
// Accessing available algorithm/protocol in the SunJSSE provider
// see http://java.sun.com/javase/6/docs/technotes/guides/security/SunProviders.html
SSLContext sslContext = SSLContext.getInstance("SSLv3");
// Acessing available algorithm/protocol in the IBMJSSE2 provider
// see http://www.ibm.com/developerworks/java/jdk/security/142/secguides/jsse2docs/JSSE2RefGuide.html
// SSLContext sslContext = SSLContext.getInstance("SSL_TLS");
System.out.println("SSLContext provider: " +
sslContext.getProvider().toString());
// Initialise our SSL context from the key/trust managers
sslContext.init(keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(), null);
// Get an SSLSocketFactory to pass to WMQ
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// Create default MQ connection factory
MQQueueConnectionFactory factory = new MQQueueConnectionFactory();
// Customize the factory
factory.setSSLSocketFactory(sslSocketFactory);
// Use javac SSLTest.java -Xlint:deprecation
factory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
factory.setQueueManager(QMGRNAME);
factory.setHostName(HOSTNAME);
factory.setChannel(CHANNEL);
factory.setPort(1414);
factory.setSSLFipsRequired(false);
factory.setSSLCipherSuite(SSLCIPHERSUITE);
QueueConnection connection = null;
connection = factory.createQueueConnection("",""); //empty user, pass to avoid MQJMS2013 messages
connection.start();
System.out.println("JMS SSL client connection started!");
connection.close();
} catch (JMSException ex) {
ex.printStackTrace();
} catch (Exception ex){
ex.printStackTrace();
}
}
}
Be aware of which JRE you are using. We have had big trouble in using Sun JDK, because of a certain cryptation (TLS_RSA_WITH_AES_128_CBC_SHA) on the SSL channel to the IBM MQ. We used a X509 certeficate. In order to get it working we are using IBM JRE because it has much bigger support for certain cipher suites!