shr*_*uma 5 python ssl activemq-classic stomp stomp.py
谁能解释一下如何将 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()
Run Code Online (Sandbox Code Playgroud)
我创建了http://activemq.apache.org/how-do-i-use-ssl.html文档中给出的密钥库和信任库,并将它们添加到SSL_OPTS代理中的环境变量中,但我找不到如何使用密钥库和信任库初始化 Python STOMP 客户端。我是否应该使用stomp.Connection()方法中给出的 SSL 参数,如果是,如何使用?
谁能解释一下是否还有其他方法可以通过 STOMP 添加 SSL?
所述的Python STOMP客户端(作为4.1.20版)使用SSLContext来处理其密钥对/证书,所以没有理由以产生Java密钥对客户端。
考虑到这一点,让我们完成设置 ApacheMQ 以支持 SSL 包装的 STOMP 连接的整个过程。以下流程已在 ApacheMQ 5.15.4 上测试过。我们通过在代理和客户端之间手动移动自签名证书来明确建立双向信任;使用证书颁发机构也是可能的,但如何这样做是一个不同的问题。
如上所述,在 Python 方面,KeyStore 几乎没有用,并且由于SSLContext需要 PEM 编码的证书,我们不妨手动创建密钥对和证书(即使用openssl)。首先,在客户端机器上,让我们创建一个 4096 位的 RSA 密钥:
openssl genrsa -out client.key 4096
Run Code Online (Sandbox Code Playgroud)
使用它,将公钥部分转换为证书并使用密钥本身对其进行签名;由于我们将手动将证书移动到代理,因此自签名证书不是问题:
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
Run Code Online (Sandbox Code Playgroud)
STOMP 客户端将需要签名证书client.pem和私钥client.key,而代理只需要证书。
在代理上,我们可以按照 Apache 指南的第一部分并使用 Javakeytool创建一个带有服务器密钥的 KeyStore:
keytool -genkeypair -alias broker -keyalg RSA -keysize 4096 -keystore broker.ks
Run Code Online (Sandbox Code Playgroud)
当提示输入“名字和姓氏”时,提供服务器的主机名,在我们的示例中,我们将简单地将其视为localhost; 如果代理和客户端在不同的服务器上运行,请确保将其设置为 Python 客户端最终用于识别代理的任何内容:
What is your first and last name?
[Unknown]: localhost
Run Code Online (Sandbox Code Playgroud)
所有其他输入值可以保留为“未知”。
归根结底,我们只希望允许使用我们知道的证书的客户端连接到代理,因此此时将client.pem上面生成的内容复制到代理并通过以下方式将其添加到信任存储中
keytool -import -alias client -keystore broker.ts -file client.pem
Run Code Online (Sandbox Code Playgroud)
如果代理允许来自任何客户端的连接,则可以跳过这最后一步。
默认情况下,通过 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>
Run Code Online (Sandbox Code Playgroud)
确保删除任何现有的纯文本连接器,例如默认的 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
Run Code Online (Sandbox Code Playgroud)
或(在 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
Run Code Online (Sandbox Code Playgroud)
这里,两个密码是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()
Run Code Online (Sandbox Code Playgroud)
为了确保ApacheMQ确实验证客户端证书,我们可以重复步骤1,创建一个新的对,client2.key/client2.pem说,并使用它。这样做应该会导致 ApacheMQ 打印以下无意义的错误消息:
ERROR | Could not accept connection from null : {}
java.io.IOException: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
Run Code Online (Sandbox Code Playgroud)
现在,细心的读者会注意到我们实际上从未将代理证书移动到客户端,但无论如何事情似乎都可以工作。事实证明, 的默认行为stomp.py根本不执行证书验证,从而允许(主动)攻击者对连接进行中间人攻击。
由于我们正在滚动自签名证书,因此我们需要做的就是向 Python 客户端提供实际的代理证书来解决这种情况。在代理上,通过导出证书
keytool -exportcert -rfc -alias broker -keystore broker.ks -file broker.pem
Run Code Online (Sandbox Code Playgroud)
并移至broker.pemPython 客户端。现在,在上面的测试脚本中,通过将 SSL 配置替换为包含证书
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')
Run Code Online (Sandbox Code Playgroud)
如上所述,我们可以通过重复代理证书生成过程来创建一个broker2.pem,在测试脚本中使用它来测试这确实执行了正确的验证,并注意它会失败并显示
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7782 次 |
| 最近记录: |