用于加密消息的 Qpid 和 JNDI

Qpid and JNDI for encrypted messages

我目前正在做一个 JMS 项目,我已经创建了 2 个密钥和 2 个证书以及一个 TrustStorage,我通过 Qpid UI 创建的 mytruststore。 在我的 jndi.properties 文件中,我有以下代码:

//Set the InitialContextFactory class to use

java.naming.factory.initial = org.apache.qpid.jms.jndi.JmsInitialContextFactory

//Define the required ConnectionFactory instances
//connectionfactory.<JNDI-lookup-name> = <URI>

connectionfactory.myFactoryLookup = amqp://localhost:5672
connectionfactory.producerConnectionFactory = amqp://admin:admin@?brokerlist='tcp://localhost:5672?encryption_remote_trust_store='$certificates%255c/mytruststore''
connectionfactory.consumer1ConnectionFactory = amqp://admin:admin@?brokerlist='tcp://localhost:5672?encryption_key_store='C:\OpenSSL-Win64\bin\mytruststorage.jks'&encryption_key_store_password='thanos''
connectionfactory.consumer2ConnectionFactory = amqp://admin:admin@?brokerlist='tcp://localhost:5672?encryption_key_store='C:\OpenSSL-Win64\bin\mytruststorage.jks'&encryption_key_store_password='thanos''

//Configure the necessary Queue and Topic objects
//queue.<JNDI-lookup-name> = <queue-name>
//topic.<JNDI-lookup-name> = <topic-name>

queue.myQueueLookup = queue
topic.myTopicLookup = topic
queue.myTestQueue = queue

在我的 EncryptionExample.java class 中,我有以下代码:

package org.apache.qpid.jms.example;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
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 = getResourceAsStream("jndi.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 InputStream getResourceAsStream(String string) {
        // TODO Auto-generated method stub
        return null;
    }

    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;
        }
    }
}

当我尝试构建它时,出现以下异常。

Exception in thread "main" java.lang.NullPointerException

at java.util.Properties$LineReader.readLine(Unknown Source)

at java.util.Properties.load0(Unknown Source)

at java.util.Properties.load(Unknown Source)

at org.apache.qpid.jms.example.EncryptionExample.createConnection(EncryptionExample.java:106)

at org.apache.qpid.jms.example.EncryptionExample.runProducerExample(EncryptionExample.java:54)

at org.apache.qpid.jms.example.EncryptionExample.main(EncryptionExample.java:48)

我假设 jndi.properties 文件中的以下代码有问题:

    connectionfactory.myFactoryLookup = amqp://localhost:5672
    connectionfactory.producerConnectionFactory = amqp://admin:admin@?brokerlist='tcp://localhost:5672?encryption_remote_trust_store='$certificates%255c/mytruststore''
    connectionfactory.consumer1ConnectionFactory = amqp://admin:admin@?brokerlist='tcp://localhost:5672?encryption_key_store='C:\OpenSSL-Win64\bin\mytruststorage.jks'&encryption_key_store_password='thanos''
    connectionfactory.consumer2ConnectionFactory = amqp://admin:admin@?brokerlist='tcp://localhost:5672?encryption_key_store='C:\OpenSSL-Win64\bin\mytruststorage.jks'&encryption_key_store_password='thanos''

这是我的解决方案资源管理器:

您不能在 Properties 个文件中使用 // 作为注释。请改用 #!

参见:https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#load-java.io.Reader-

您遇到的第一个也是最大的问题是,您正在尝试使用来自客户端的连接 URI 和客户端功能,而不是您配置项目要使用的客户端。您似乎正在使用 Qpid JMS,这是在 Qpid 项目中开发的新 AMQP 1.0 客户端。此客户端使用与之前的 AMQP 0.x 客户端不同的 URI 语法,并且在传递这些无效的 URI 时,您将从连接工厂获得异常。

您将遇到的另一个问题(在您的 post 的评论中提到)是 AMQP 1.0 JMS 客户端中没有消息加密功能,因此一旦您正确定义了 URI。

较新的 AMQP 1.0 JMS 客户端的文档是 here