任何人都可以解释我如何将SSL添加到我正在使用的Python STOMP客户端。 我在ActiveMQ配置文件中添加了stomp + ssl传输连接器,我的基本Python STOMP客户端如下:
import time
import sys
import stomp
class MyListener(stomp.ConnectionListener):
def on_error(self, headers, message):
print('received an error "%s"' % message)
def on_message(self, headers, message):
print('received a message "%s"' % message)
conn = stomp.Connection()
conn.set_listener('', MyListener())
conn.start()
conn.connect('admin', 'password', wait=True)
conn.subscribe(destination='/queue/test', id=1, ack='auto')
conn.send(body=' '.join(sys.argv[1:]), destination='/queue/test')
time.sleep(2)
conn.disconnect()
我创建了http://activemq.apache.org/how-do-i-use-ssl.html文档中给出的密钥库和信任库,并将它们添加到代理中的SSL_OPTS
环境变量中,但是我无法找到如何初始化Python STOMP客户端密钥库和信任商店。我应该使用stomp.Connection()
方法中给出的SSL参数,如果是,如何使用?
有人可以解释是否有其他方法可以通过STOMP添加SSL?
答案 0 :(得分:3)
Python STOMP client(从版本4.1.20开始)使用SSLContext
来处理其密钥对/证书,因此没有理由为客户端生成Java KeyStore。
考虑到这一点,让我们完成设置ApacheMQ以支持SSL包装的STOMP连接的整个过程。以下过程已在ApacheMQ 5.15.4上进行了测试。我们通过在代理和客户端之间手动移动自签名证书来明确建立双向信任;使用证书颁发机构也是可能的,但如何这样做是一个不同的问题。
如上所述,在Python方面,KeyStore几乎没有用,而且由于SSLContext
需要PEM编码证书,我们也可以手工创建密钥对和证书(即使用openssl
)。首先,在客户端计算机上,让我们创建一个4096位的RSA密钥:
openssl genrsa -out client.key 4096
使用此功能,将公钥部分转换为证书并使用密钥本身进行签名;由于我们将手动将证书移至代理,因此自签名证书不是问题:
openssl req -new -out client.csr -key client.key
openssl x509 -req -days 365 -in client.csr -signkey client.key -out client.pem
rm client.csr
STOMP客户端将需要签名证书client.pem
和私钥client.key
,而代理只需要证书。
在代理上,我们可以按照Apache指南的第一部分使用Java keytool
创建一个带有服务器密钥的KeyStore:
keytool -genkeypair -alias broker -keyalg RSA -keysize 4096 -keystore broker.ks
当提示输入“名字和姓氏”时,请提供服务器的主机名,在我们的示例中,我们将只需localhost
;如果代理和客户端在不同的服务器上运行,请确保将其设置为Python客户端最终用于识别代理的任何内容:
What is your first and last name?
[Unknown]: localhost
所有其他输入值可以保留为“未知”。
在一天结束时,我们只希望允许来自客户端的代理与我们知道的证书连接,因此此时将上面生成的client.pem
复制到代理并将其添加到信任通过
keytool -import -alias client -keystore broker.ts -file client.pem
如果代理允许来自任何客户端的连接,则可以跳过此最后一步。
默认情况下,通过STOMP(实际上所有连接)的所有连接都是纯文本连接,并且为了通过SSL启用STOMP连接,请将以下<transportConnector />
添加到conf/apachemq.xml
:
<transportConnectors>
<transportConnector name="stomp+ssl" uri="stomp+nio+ssl://0.0.0.0:61613?transport.enabledProtocols=TLSv1.2&needClientAuth=true" />
</transportConnectors>
确保删除任何现有的纯文本连接器,例如默认的STOMP连接器,否则客户端将能够简单地使用这些连接器并绕过SSL要求。另请注意,needClientAuth=true
强制客户端证书验证;如果没有这个,客户端就可以在不提供可信证书的情况下进行连接。
要配置ApacheMQ以使用上面定义的密钥和信任存储,请定义环境变量ACTIVEMQ_SSL_OPTS
到(在Unix上)
export ACTIVEMQ_SSL_OPTS = -Djavax.net.ssl.keyStore=/path/to/broker.ks -Djavax.net.ssl.trustStore=/path/to/broker.ts -Djavax.net.ssl.keyStorePassword=passwordForBrokerKs -Djavax.net.ssl.trustStorePassword=passwordForBrokerTs
或(在Windows上)
set ACTIVEMQ_SSL_OPTS=-Djavax.net.ssl.keyStore=C:\path\to\broker.ks -Djavax.net.ssl.trustStore=C:\path\to\broker.ts -Djavax.net.ssl.keyStorePassword=passwordForBrokerKs -Djavax.net.ssl.trustStorePassword=passwordForBrokerTs
此处,这两个密码是在上一步中运行keytool
后选择的密码。如果不需要客户端证书验证,只需省略trustStore
和trustStorePassword
的配置。
有了这个,ActiveMQ可以像往常一样通过bin/activemq start
启动。要确保SSL配置符合预期,请注意启动服务器时打印输出的JVM args
部分。
在正确设置代理的情况下,我们也可以配置客户端。在这里,我们为stomp.Connection.set_ssl
提供了对第一步中创建的密钥和证书的引用。假设ActiveMQ服务器在localhost:61613上运行,则您的测试脚本将变为
import time
import sys
import stomp
class MyListener(stomp.ConnectionListener):
def on_error(self, headers, message):
print('received an error "%s"' % message)
def on_message(self, headers, message):
print('received a message "%s"' % message)
host = 'localhost'
port = 61613
conn = stomp.Connection([(host, port)])
conn.set_ssl(for_hosts=[(host, port)], key_file='/path/to/client.key', cert_file='/path/to/client.pem')
conn.set_listener('', MyListener())
conn.start()
conn.connect('admin', 'password', wait=True)
conn.subscribe(destination='/queue/test', id=1, ack='auto')
conn.send(body='test message', destination='/queue/test')
time.sleep(2)
conn.disconnect()
为了确保ApacheMQ确实验证了客户端证书,我们可以重复步骤1并创建一对新的client2.key
/ client2.pem
,然后使用它。这样做会导致ApacheMQ打印以下非敏感错误消息:
ERROR | Could not accept connection from null : {}
java.io.IOException: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
现在,细心的读者会注意到我们从未真正将代理证书移动到客户端,但事情似乎无论如何都有效。事实证明,stomp.py
的默认行为是执行根本没有证书验证,允许(活动)攻击者MITM连接。
在我们推出自签名证书时,我们需要做的就是为Python客户端提供实际的代理证书。在代理上,通过
导出证书keytool -exportcert -rfc -alias broker -keystore broker.ks -file broker.pem
并将broker.pem
移至Python客户端。现在,在上面的测试脚本中,通过用
conn.set_ssl(for_hosts=[(host, port)], key_file='/path/to/client.key', cert_file='/path/to/client.pem', ca_certs='/path/to/broker.pem')
如上所述,我们可以通过重复代理证书生成过程来创建broker2.pem
来测试这确实正在执行正确的验证,在测试脚本中使用它,并注意它将失败并且
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
答案 1 :(得分:0)
Try this.
conn = stomp.Connection([(host, port)])
conn.set_listener('', MyListener())
conn.set_ssl(for_hosts=[(host, port)], ssl_version=ssl.PROTOCOL_TLS)
conn.start()
conn.connect(login, password, wait=True)
conn.send(body=message, destination=queue)
conn.disconnect()
or
conn.set_ssl(for_hosts=[(host, port)], ssl_version=_ssl.PROTOCOL_TLS)
答案 2 :(得分:0)
关键是要了解密钥/证书文件必须在其中-否则即使其存储库中的stomp.py测试也无法进行。如果未正确指定文件名,则TLS在stomp.py中将不起作用。
在此处查看代码和密钥生成说明:https://gist.github.com/borislitvak/6ccea503abf1b2f9c89e87309d6dab88