blob: 616cbe8915a5de8e2442634b1a08338590c17ef0 [file] [log] [blame]
<!DOCTYPE html>
<!--
-
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
-
-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>9.4.&#160;Message Encryption Example - Apache Qpid&#8482;</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="/site.css" type="text/css" async="async"/>
<link rel="stylesheet" href="/deferred.css" type="text/css" defer="defer"/>
<script type="text/javascript">var _deferredFunctions = [];</script>
<script type="text/javascript" src="/deferred.js" defer="defer"></script>
<!--[if lte IE 8]>
<link rel="stylesheet" href="/ie.css" type="text/css"/>
<script type="text/javascript" src="/html5shiv.js"></script>
<![endif]-->
<!-- Redirects for `go get` and godoc.org -->
<meta name="go-import"
content="qpid.apache.org git https://git-wip-us.apache.org/repos/asf/qpid-proton.git"/>
<meta name="go-source"
content="qpid.apache.org
https://github.com/apache/qpid-proton/blob/go1/README.md
https://github.com/apache/qpid-proton/tree/go1{/dir}
https://github.com/apache/qpid-proton/blob/go1{/dir}/{file}#L{line}"/>
</head>
<body>
<div id="-content">
<div id="-top" class="panel">
<a id="-menu-link"><img width="16" height="16" src="" alt="Menu"/></a>
<a id="-search-link"><img width="22" height="16" src="" alt="Search"/></a>
<ul id="-global-navigation">
<li><a id="-logotype" href="/index.html">Apache Qpid<sup>&#8482;</sup></a></li>
<li><a href="/documentation.html">Documentation</a></li>
<li><a href="/download.html">Download</a></li>
<li><a href="/discussion.html">Discussion</a></li>
</ul>
</div>
<div id="-menu" class="panel" style="display: none;">
<div class="flex">
<section>
<h3>Project</h3>
<ul>
<li><a href="/overview.html">Overview</a></li>
<li><a href="/components/index.html">Components</a></li>
<li><a href="/releases/index.html">Releases</a></li>
</ul>
</section>
<section>
<h3>Messaging APIs</h3>
<ul>
<li><a href="/proton/index.html">Qpid Proton</a></li>
<li><a href="/components/jms/index.html">Qpid JMS</a></li>
<li><a href="/components/messaging-api/index.html">Qpid Messaging API</a></li>
</ul>
</section>
<section>
<h3>Servers and tools</h3>
<ul>
<li><a href="/components/broker-j/index.html">Broker-J</a></li>
<li><a href="/components/cpp-broker/index.html">C++ broker</a></li>
<li><a href="/components/dispatch-router/index.html">Dispatch router</a></li>
</ul>
</section>
<section>
<h3>Resources</h3>
<ul>
<li><a href="/dashboard.html">Dashboard</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/qpid/Index">Wiki</a></li>
<li><a href="/resources.html">More resources</a></li>
</ul>
</section>
</div>
</div>
<div id="-search" class="panel" style="display: none;">
<form action="http://www.google.com/search" method="get">
<input type="hidden" name="sitesearch" value="qpid.apache.org"/>
<input type="text" name="q" maxlength="255" autofocus="autofocus" tabindex="1"/>
<button type="submit">Search</button>
<a href="/search.html">More ways to search</a>
</form>
</div>
<div id="-middle" class="panel">
<ul id="-path-navigation"><li><a href="/index.html">Home</a></li><li><a href="/releases/index.html">Releases</a></li><li><a href="/releases/qpid-jms-amqp-0-x-6.3.1/index.html">Qpid JMS AMQP 0-x 6.3.1</a></li><li><a href="/releases/qpid-jms-amqp-0-x-6.3.1/jms-amqp-0-8-book/index.html">Apache Qpid JMS AMQP 0-8/0-9/0-9-1</a></li><li>9.4.&#160;Message Encryption Example</li></ul>
<div id="-middle-content">
<div class="docbook"><div class="navheader"><table summary="Navigation header" width="100%"><tr><th align="center" colspan="3">9.4.&#160;Message Encryption Example</th></tr><tr><td align="left" width="20%"><a accesskey="p" href="JMS-Client-Message-Encryption-Receiving.html">Prev</a>&#160;</td><th align="center" width="60%">Chapter&#160;9.&#160;Message Encryption</th><td align="right" width="20%">&#160;<a accesskey="n" href="JMS-Client-0-8-System-Properties.html">Next</a></td></tr></table><hr /></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="JMS-Client-Message-Encryption-Example"></a>9.4.&#160;Message Encryption Example</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="JMS-Client-Message-Encryption-Example-Introduction"></a>9.4.1.&#160;Introduction</h3></div></div></div><p>
In this example we will setup the Qpid Broker-J and two clients who will send each other encrypted messages.
The clients will use self signed certificates and the certificates will be distributed by the Broker.
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="JMS-Client-Message-Encryption-Example-Prerequisites"></a>9.4.2.&#160;Prerequisites</h3></div></div></div><p>
For this example it is assumed the Broker is already running and that Management is enabled on port
8443.
</p><p>
The example requires two (one for each client) self-signed X.509 certificates and the corresponding
keys. We refer to these as
<code class="literal">client_1/2.cert</code>
and
<code class="literal">client_1/2.key</code>
throughout the text below.
</p><p>
The following
<a class="link" href="https://www.openssl.org" target="_top">openssl</a>
commands can be used to generate self signed certicates suitable for this test.
</p><pre class="programlisting">
openssl req -x509 -newkey rsa:4096 -keyout client_1.key -out client_1.cert -days 365 -nodes -subj "/C=US/O=Apache/OU=Qpid/CN=client1"
openssl req -x509 -newkey rsa:4096 -keyout client_2.key -out client_2.cert -days 365 -nodes -subj "/C=US/O=Apache/OU=Qpid/CN=client2"
</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="JMS-Client-Message-Encryption-Example-Broker-Config"></a>9.4.3.&#160;Broker Configuration</h3></div></div></div><p>
In this example we want the broker to distribute the client certificates.
Essentially, we want the broker to act as a<a class="link" href="https://en.wikipedia.org/wiki/Key_server_(cryptographic)" target="_top">
Key Server</a>.
Use
<a class="link" href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html" target="_top">java's keytool</a>
to create a trust store containing the two client certificates.
</p><pre class="programlisting">
keytool -importcert -file client_1.cert -alias client1 -keystore mytruststore.jks
keytool -importcert -file client_2.cert -alias client2 -keystore mytruststore.jks
</pre><p>
Now a FileTrustStore can be created on the broker pointing to the java trust store that was just
created.
This can be done via the Web Management Console or the REST API:
</p><pre class="programlisting">curl -v -u admin https://localhost:8443/api/v6.1/truststore/clientcerts -X PUT -d
'{"type": "FileTrustStore", "stroeUrl": "&lt;path_to_truststore&gt;", "password": "&lt;your_truststore_password&gt;"}'
</pre><p>
The TrustStore must be configured to expose the certificates as a message source to the clients:
</p><pre class="programlisting">curl -v -u admin https://localhost:8443/api/v6.1/truststore/clientcerts -X POST -d
'{"exposedAsMessageSource": true}'
</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="JMS-Client-Message-Encryption-Example-Client-Config"></a>9.4.4.&#160;Client Configuration</h3></div></div></div><p>
The configuration for the clients happens in the connection URL. In this example this is provided via a
JNDI properties file.
</p><p>
On the producing side, in order to encrypt a message for a recipient, the Qpid client needs the
recipient's public certificate which is distributed by the Broker following our above broker setup. The
<code class="literal">encryption_remote_trust_store</code>
element within the connection URL provides the name of the truststore.
</p><p>
On the receiving side, in order to decrypt a message it needs a JKS keystore with the private key
matching the public certificate.
For this example, the keystores can again be created with a two-step process involving
<a class="link" href="https://www.openssl.org" target="_top">openssl</a>
and <a class="link" href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html" target="_top">java's keytool</a>.
</p><pre class="programlisting">
openssl pkcs12 -export -in client_1.cert -inkey client_1.key -out client_1.pkcs12 -name "client1"
openssl pkcs12 -export -in client_2.cert -inkey client_2.key -out client_2.pkcs12 -name "client2"
keytool -importkeystore -destkeystore client_1.jks -srckeystore client_1.pkcs12 -srcstoretype PKCS12
keytool -importkeystore -destkeystore client_2.jks -srckeystore client_2.pkcs12 -srcstoretype PKCS12
</pre><p>
</p><p>
The final JNDI properties file should look similar to this:
</p><pre class="programlisting">
java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory
# connection factories. This is where end-to-end encryption is configured on the client.
# connectionfactory.[jndiname] = [ConnectionURL]
connectionfactory.producerConnectionFactory = amqp://&lt;username&gt;:&lt;password&gt;@/?brokerlist='tcp://localhost:5672?encryption_remote_trust_store='$certificates%255c/clientcerts''
connectionfactory.consumer1ConnectionFactory = amqp://&lt;username&gt;:&lt;password&gt;@/?brokerlist='tcp://localhost:5672?encryption_key_store='path/to/client_1.jks'&amp;encryption_key_store_password='&lt;keystore_password&gt;''
connectionfactory.consumer2ConnectionFactory = amqp://&lt;username&gt;:&lt;password&gt;@/?brokerlist='tcp://localhost:5672?encryption_key_store='path/to/client_2.jks'&amp;encryption_key_store_password='&lt;keystore_password&gt;''
# Rest of JNDI configuration. For example
# destination.[jniName] = [Address Format]
queue.myTestQueue = testQueue
</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="JMS-Client-Message-Encryption-Example-Application"></a>9.4.5.&#160;Application Code</h3></div></div></div><p>
On the producing side, the application needs to enable encryption and indicate the intended recipient(s)
of each message. This is done via the
<code class="literal">x-qpid-encrypt</code>
and
<code class="literal">x-qpid-encrypt-recipients</code>
message properties. Note that the order of the relative distinguished name (RDN) entries within the
recipent's distinguished name (DNs) is significant. If the order does not match that recorded in
truststore, a
<a class="link" href="JMS-Client-0-8-Appendix-Exceptions.html#JMS-Client-0-8-Appendix-Exceptions-CertificateException">CertificateException</a>
will be encountered.
</p><p>
On the receiving side, there is nothing to do. The application code does not have to add decryption code as this is handled transparently by the Qpid client library.
However, the receiving application should gracefully handle failures in decryption in which case the encrypted message will be delivered as a BytesMessage.
</p><pre class="programlisting">
// imports omitted for brevity
public class EncryptionExample {
public EncryptionExample() {
}
public static void main(String[] args) throws Exception {
EncryptionExample encryptionExampleApp = new EncryptionExample();
encryptionExampleApp.runProducerExample();
encryptionExampleApp.runReceiverExample();
}
private void runProducerExample() throws Exception
{
Connection connection = createConnection("producerConnectionFactory");
try {
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
Destination destination = createDesination("myTestQueue");
MessageProducer messageProducer = session.createProducer(destination);
TextMessage message = session.createTextMessage("Hello world!");
// ============== Enable encryption for this message ==============
message.setBooleanProperty("x-qpid-encrypt", true);
// ============== Configure recipients for encryption ==============
message.setStringProperty("x-qpid-encrypt-recipients", "CN=client1, OU=Qpid, O=Apache, C=US");
messageProducer.send(message);
session.commit();
}
finally {
connection.close();
}
}
private void runReceiverExample() throws Exception
{
Connection connection = createConnection("consumer1ConnectionFactory");
try {
connection.start();
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
Destination destination = createDesination("myTestQueue");
MessageConsumer messageConsumer = session.createConsumer(destination);
Message message = messageConsumer.receive();
if (message instanceof TextMessage) {
// application logic
System.out.println(((TextMessage) message).getText());
} else if (message instanceof BytesMessage) {
// handle potential decryption failure
System.out.println("Potential decryption problem. Application not in list of intended recipients?");
}
session.commit();
}
finally {
connection.close();
}
}
///////////////////////////////////////
// The following is boilerplate code //
///////////////////////////////////////
private Connection createConnection(final String connectionFactoryName) throws JMSException, IOException, NamingException
{
try (InputStream resourceAsStream = this.getClass().getResourceAsStream("example.properties")) {
Properties properties = new Properties();
properties.load(resourceAsStream);
Context context = new InitialContext(properties);
ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup(connectionFactoryName);
final Connection connection = connectionFactory.createConnection();
context.close();
return connection;
}
}
private Destination createDesination(String desinationJndiName) throws IOException, NamingException
{
try (InputStream resourceAsStream = this.getClass().getResourceAsStream("example.properties")) {
Properties properties = new Properties();
properties.load(resourceAsStream);
Context context = new InitialContext(properties);
Destination destination = (Destination) context.lookup(desinationJndiName);
context.close();
return destination;
}
}
}
</pre><p>
</p></div></div><div class="navfooter"><hr /><table summary="Navigation footer" width="100%"><tr><td align="left" width="40%"><a accesskey="p" href="JMS-Client-Message-Encryption-Receiving.html">Prev</a>&#160;</td><td align="center" width="20%"><a accesskey="u" href="JMS-Client-Message-Encryption.html">Up</a></td><td align="right" width="40%">&#160;<a accesskey="n" href="JMS-Client-0-8-System-Properties.html">Next</a></td></tr><tr><td align="left" valign="top" width="40%">9.3.&#160;Receiving an Encrypted Message&#160;</td><td align="center" width="20%"><a accesskey="h" href="JMS-Client-Book.html">Home</a></td><td align="right" valign="top" width="40%">&#160;Chapter&#160;10.&#160;System Properties</td></tr></table></div></div>
<hr/>
<ul id="-apache-navigation">
<li><a href="http://www.apache.org/">Apache</a></li>
<li><a href="http://www.apache.org/licenses/">License</a></li>
<li><a href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
<li><a href="http://www.apache.org/foundation/thanks.html">Thanks!</a></li>
<li><a href="/security.html">Security</a></li>
<li><a href="http://www.apache.org/"><img id="-apache-feather" width="48" height="14" src="" alt="Apache"/></a></li>
</ul>
<p id="-legal">
Apache Qpid, Messaging built on AMQP; Copyright &#169; 2015
The Apache Software Foundation; Licensed under
the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache
License, Version 2.0</a>; Apache Qpid, Qpid, Qpid Proton,
Proton, Apache, the Apache feather logo, and the Apache Qpid
project logo are trademarks of The Apache Software
Foundation; All other marks mentioned may be trademarks or
registered trademarks of their respective owners
</p>
</div>
</div>
</div>
</body>
</html>