You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2016/11/10 16:29:53 UTC

svn commit: r1769149 - in /qpid/java/branches/6.1.x: ./ doc/java-broker/src/docbkx/management/managing/ doc/jms-client-0-8/src/docbkx/

Author: kwall
Date: Thu Nov 10 16:29:53 2016
New Revision: 1769149

URL: http://svn.apache.org/viewvc?rev=1769149&view=rev
Log:
QPID-7466: [Java Broker, Java Client for AMQP 0-8 to 0-10] Improve documentation for end-to-end encryption

Merged from trunk with command:
svn merge -c 1769007,1769009,1769087  ^/qpid/java/trunk

Modified:
    qpid/java/branches/6.1.x/   (props changed)
    qpid/java/branches/6.1.x/doc/java-broker/src/docbkx/management/managing/Java-Broker-Management-Managing-Truststores.xml
    qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Appendix-Exceptions.xml
    qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Message-Encryption.xml

Propchange: qpid/java/branches/6.1.x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Nov 10 16:29:53 2016
@@ -9,5 +9,5 @@
 /qpid/branches/java-broker-vhost-refactor/java:1493674-1494547
 /qpid/branches/java-network-refactor/qpid/java:805429-821809
 /qpid/branches/qpid-2935/qpid/java:1061302-1072333
-/qpid/java/trunk:1766544,1766547,1766553,1766666,1766796-1766797,1766806,1767251,1767267-1767268,1767275,1767310,1767326,1767329,1767332,1767514,1767523,1767738,1767825,1767847-1767848,1767882,1767909,1767914,1768016-1768017,1768065,1768643,1768704,1768854,1768875,1768914,1768963,1768967,1768976,1769138-1769139
+/qpid/java/trunk:1766544,1766547,1766553,1766666,1766796-1766797,1766806,1767251,1767267-1767268,1767275,1767310,1767326,1767329,1767332,1767514,1767523,1767738,1767825,1767847-1767848,1767882,1767909,1767914,1768016-1768017,1768065,1768643,1768704,1768854,1768875,1768914,1768963,1768967,1768976,1769007,1769009,1769087,1769138-1769139
 /qpid/trunk/qpid:796646-796653

Modified: qpid/java/branches/6.1.x/doc/java-broker/src/docbkx/management/managing/Java-Broker-Management-Managing-Truststores.xml
URL: http://svn.apache.org/viewvc/qpid/java/branches/6.1.x/doc/java-broker/src/docbkx/management/managing/Java-Broker-Management-Managing-Truststores.xml?rev=1769149&r1=1769148&r2=1769149&view=diff
==============================================================================
--- qpid/java/branches/6.1.x/doc/java-broker/src/docbkx/management/managing/Java-Broker-Management-Managing-Truststores.xml (original)
+++ qpid/java/branches/6.1.x/doc/java-broker/src/docbkx/management/managing/Java-Broker-Management-Managing-Truststores.xml Thu Nov 10 16:29:53 2016
@@ -22,10 +22,31 @@
 
 <section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="Java-Broker-Management-Managing-Truststores">
     <title>Truststores</title>
-    <para> A <link linkend="Java-Broker-Concepts-Truststores">Truststore</link> is required by a
-        Port in order to SSL client authentication. Some authentication provides also use a
-        truststore when connecting to authentication systems that are protected by a private issuer
-        SSL certificate.</para>
+    <para>
+        <link linkend="Java-Broker-Concepts-Truststores">Truststores</link>
+        have a number of roles within
+        the Broker.
+        <itemizedlist>
+            <listitem>
+                <para>A truststore is required by a Port in order to support SSL client authentication.</para>
+            </listitem>
+            <listitem>
+                <para>Truststores have a optional role in end to end message encryption. The Broker acts as a
+                    <link xmlns:xlink="http://www.w3.org/1999/xlink"
+                          xlink:href="https://en.wikipedia.org/wiki/Key_server_(cryptographic)">
+                        Key Server
+                    </link>
+                    so that publishing applications have convenient access to recipient's public keys.
+                </para>
+            </listitem>
+            <listitem>
+                <para>Some authentication providers also use a truststore when connecting to authentication systems that
+                    are protected by a private issuer
+                    SSL certificate.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </para>
     <section xml:id="Java-Broker-Management-Managing-Truststores-Types">
         <title>Types</title>
         <para>The following truststore types are supported. <itemizedlist>
@@ -55,6 +76,11 @@
                     <para><emphasis>Name the truststore</emphasis>. Used to identify the
                         truststore.</para>
                 </listitem>
+                <listitem>
+                    <para><emphasis>Exposed as Message Source</emphasis>. If enabled, the Broker
+                        will distribute certificates contained within the trustore to clients.
+                        Used by the end to end message encryption feature.</para>
+                </listitem>
             </itemizedlist>
         </para>
         <para>The following attributes apply to <emphasis>File Trust Stores</emphasis> only.</para>

Modified: qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Appendix-Exceptions.xml
URL: http://svn.apache.org/viewvc/qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Appendix-Exceptions.xml?rev=1769149&r1=1769148&r2=1769149&view=diff
==============================================================================
--- qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Appendix-Exceptions.xml (original)
+++ qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Appendix-Exceptions.xml Thu Nov 10 16:29:53 2016
@@ -103,6 +103,15 @@
               group) has not been permissioned within the Broker's <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${qpidJavaBrokerBook}Java-Broker-Security-ACLs.html">Access Control List
                 (ACL)</link>.</para></entry>
         </row>
+        <row xml:id="JMS-Client-0-8-Appendix-Exceptions-CertificateException">
+          <entry>CertificateException</entry>
+          <entry>Unable to find certificate for recipient '&lt;recipient&gt;'</entry>
+          <entry>
+            <para>When using end to end message encryption, this exception indicates the the message recipent's
+              principal cannot be found in the truststore. See <xref linkend="JMS-Client-Message-Encryption"/>
+            </para>
+          </entry>
+        </row>
       </tbody>
     </tgroup>
   </table>

Modified: qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Message-Encryption.xml
URL: http://svn.apache.org/viewvc/qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Message-Encryption.xml?rev=1769149&r1=1769148&r2=1769149&view=diff
==============================================================================
--- qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Message-Encryption.xml (original)
+++ qpid/java/branches/6.1.x/doc/jms-client-0-8/src/docbkx/JMS-Client-Message-Encryption.xml Thu Nov 10 16:29:53 2016
@@ -88,8 +88,16 @@
                 <link linkend="JMS-Client-0-8-Connection-URL-BrokerOptions-EncryptionRemoteTrustStore">encryption_remote_trust_store</link>
                 option. Such a connection URL might look somthing like:
             </para>
-            <programlisting>amqp://username:password@clientid/test?brokerlist='tcp://localhost:5672?encryption_remote_trust_store='$certificates%5c/certstore''</programlisting>
-
+            <programlisting>amqp://username:password@clientid/test?brokerlist='tcp://localhost:5672?encryption_remote_trust_store='$certificates%255c/certstore''</programlisting>
+            <para>
+                The <literal>$certificates/</literal> prefix is mandatory.
+                However, in order to prevent the client from interpreting this the wrong way several layers of escaping and encoding need to take place.
+                The slash character ('/') needs to be escaped by a backslash ('\') which needs to be doubly URL encoded resulting in <literal>$certificates%255c/</literal>.
+            </para>
+            <para>
+                Note that to use the broker-distributed certificates the broker must be configured to expose the trust store as a message source.
+                See the broker documentation on TrustStores for more details.
+            </para>
         </section>
         <section xml:id="JMS-Client-Message-Encryption-Sending-Enabling-Encryption">
             <title>Enabling Encryption</title>
@@ -107,7 +115,7 @@
             <para>
                 In order to encrypt all messages sent to a given Destination, the option
                 <link linkend="JMS-Client-0-8-Binding-URL-Options-SendEncrypted">sendencrypted</link> can be used.  Note
-                that enabling encryption on the address can be overridden by explicitly seting the property
+                that enabling encryption on the address can be overridden by explicitly setting the property
                 <literal>x-qpid-encrypt</literal> to false on an individual message. An example address would look like:
             </para>
             <programlisting>direct:///queue/queue?sendencrypted='true'</programlisting>
@@ -170,4 +178,216 @@
 
         </section>
     </section>
+    <section  xml:id="JMS-Client-Message-Encryption-Example">
+        <title>Message Encryption Example</title>
+        <section xml:id="JMS-Client-Message-Encryption-Example-Introduction">
+            <title>Introduction</title>
+            <para>
+                In this example we will setup the Qpid Broker for Java 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.
+            </para>
+        </section>
+        <section xml:id="JMS-Client-Message-Encryption-Example-Prerequisites">
+            <title>Prerequisites</title>
+            <para>
+                For this example it is assumed the Broker is already running and that Management is enabled on port
+                8443.
+            </para>
+            <para>
+                The example requires two (one for each client) self-signed X.509 certificates and the corresponding
+                keys. We refer to these as
+                <literal>client_1/2.cert</literal>
+                and
+                <literal>client_1/2.key</literal>
+                throughout the text below.
+            </para>
+            <para>
+                The following
+                <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://www.openssl.org">openssl</link>
+                commands can be used to generate self signed certicates suitable for this test.
+                <programlisting>
+<![CDATA[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"]]>
+                </programlisting>
+            </para>
+        </section>
+        <section xml:id="JMS-Client-Message-Encryption-Example-Broker-Config">
+            <title>Broker Configuration</title>
+            <para>
+                In this example we want the broker to distribute the client certificates.
+                Essentially, we want the broker to act as a<link xmlns:xlink="http://www.w3.org/1999/xlink"
+                                                                 xlink:href="https://en.wikipedia.org/wiki/Key_server_(cryptographic)">
+                Key Server</link>.
+                Use
+                <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${oracleKeytool}">java's keytool</link>
+                to create a trust store containing the two client certificates.
+                <programlisting>
+<![CDATA[keytool -importcert -file client_1.cert -alias client1 -keystore mytruststore.jks
+keytool -importcert -file client_2.cert -alias client2 -keystore mytruststore.jks]]>
+                </programlisting>
+                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:
+                <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;"}'
+                </programlisting>
+                The TrustStore must be configured to expose the certificates as a message source to the clients:
+                <programlisting>curl -v -u admin https://localhost:8443/api/v6.1/truststore/clientcerts -X POST -d
+                    '{"exposedAsMessageSource": true}'
+                </programlisting>
+            </para>
+        </section>
+        <section xml:id="JMS-Client-Message-Encryption-Example-Client-Config">
+            <title>Client Configuration</title>
+            <para>
+                The configuration for the clients happens in the connection URL. In this example this is provided via a
+                JNDI properties file.
+            </para>
+            <para>
+                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
+                <literal>encryption_remote_trust_store</literal>
+                element within the connection URL provides the name of the truststore.
+            </para>
+            <para>
+                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
+                <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://www.openssl.org">openssl</link>
+                and <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${oracleKeytool}">java's keytool</link>.
+                <programlisting>
+<![CDATA[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]]>
+                </programlisting>
+
+            </para>
+            <para>
+                The final JNDI properties file should look similar to this:
+                <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
+                </programlisting>
+            </para>
+        </section>
+        <section xml:id="JMS-Client-Message-Encryption-Example-Application">
+            <title>Application Code</title>
+            <para>
+                On the producing side, the application needs to enable encryption and indicate the intended recipient(s)
+                of each message. This is done via the
+                <literal>x-qpid-encrypt</literal>
+                and
+                <literal>x-qpid-encrypt-recipients</literal>
+                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
+                <link linkend="JMS-Client-0-8-Appendix-Exceptions-CertificateException">CertificateException</link>
+                will be encountered.
+            </para>
+            <para>
+                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.
+                <programlisting language="java">
+// 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;
+        }
+    }
+}
+                </programlisting>
+            </para>
+        </section>
+    </section>
 </chapter>



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org