我正在嘗試建立與基于 Java 的 MQTT 代理(Active MQ)的 TLSV1.2 連接。
我的客戶在 HSM 模塊中有私鑰,因此我無法訪問。
x509 證書可用。
CA 是自簽名的。
證書詳細資訊:
簽名演算法: ecdsa-with-SHA256
公鑰演算法: id-ecPublicKey (256bit)
曲線: prime256v1
觀察到的行為:
服務器拋出以下錯誤:
43 2022-04-01 23:22:26.772084 serverip clientip TLSv1.2 73 Alert (Level: Fatal, Description: Handshake Failure)
這發生在客戶端密鑰交換之后
41 2022-04-01 23:22:26.739193 clientip serverip TLSv1.2 303 Client Key Exchange, Certificate Verify, Change Cipher Spec, Encrypted Handshake Message
到目前為止我已經嘗試過:
我嘗試使用由同一 CA 簽名的 x509 證書通過 curl 與同一代理建立連接。這是被接受的。因此,我得出結論,這不是一個糟糕的證書問題。
注意:我在 curl 命令中使用了 --insecure ,對此我沒問題,因為客戶端身份驗證是我不關心的,因為服務器正在拋出錯誤。
觀察/假設:
握手失敗來自服務器之后:
Client Hello
Server Hello
Server Hello, Certificate, Server Key Exchange, Certificate Request, ServerHelloDone
Certificate
Client Key Exchange, Certificate Verify, Change Cipher Spec, Encrypted Handshake Message
Alert (Level: Fatal, Description: Handshake Failure) ( from server)
由此我得出結論,密碼套件/協議兼容性沒有問題。
我在 HSM 生成的 X509 證書和我為驗證生成的證書之間看到的唯一區別是通用名稱格式。
Wireshark 說:
- HSM 生成的證書 CN 是DirectoryString: teletexString (0)
- 手動生成的證書CN是utf-8
,CN是一樣的,wireshark顯示的格式不一樣。
我從未如此深入地研究過握手,并且只了解 TLS 握手的基礎知識。
The server is a managed Kubernetes deployment and hence really difficult currently to associate logs with clients.
But, based on observation , I see this message (activemq-netty-threads)","message":"AMQ222208: SSL handshake failed for client. java.io.IOException: Sequence tag error."
I think this is the corresponding broker logs.
Any way forward/ opinions appreciated.
Results of parsing x509 certs
- HSM cert
CN=A_00000066,OU=AA,O=Organisation Limited,L=Place,ST=State,C=C
- FS certs
1.2.840.113549.1.9.1=#160a726f6f74406174686572,CN=A_00000066,OU=AA,O=O,L=Place,ST=State,C=C
Adding relevant Server logs
SSL logs on the server:
"throwable" : {
java.security.SignatureException: Invalid encoding for signature
at java.base/sun.security.util.ECUtil.decodeSignature(ECUtil.java:279)
at jdk.crypto.ec/sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:477)
at java.base/java.security.Signature$Delegate.engineVerify(Signature.java:1247)
at java.base/java.security.Signature.verify(Signature.java:675)
at java.base/sun.security.ssl.CertificateVerify$T12CertificateVerifyMessage.<init>(CertificateVerify.java:651)
at java.base/sun.security.ssl.CertificateVerify$T12CertificateVerifyConsumer.consume(CertificateVerify.java:771)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:689)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008)
at io.netty.handler.ssl.SslHandler.runAllDelegatedTasks(SslHandler.java:1542)
at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1556)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1440)
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1267)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1314)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:792)
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:475)
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at org.apache.activemq.artemis.utils.ActiveMQThreadFactory$1.run(ActiveMQThreadFactory.java:118)
Caused by: java.io.IOException: Sequence tag error
at java.base/sun.security.util.DerInputStream.getSequence(DerInputStream.java:336)
at java.base/sun.security.util.ECUtil.decodeSignature(ECUtil.java:255)
... 32 more}
)
javax.net.ssl|WARNING|32|Thread-4 (activemq-netty-threads)|2022-04-06 10:01:32.559 UTC|SSLEngineOutputRecord.java:168|outbound has closed, ignore outbound application data
Update 2:
The signing is handed over to the rustls library. There is a custom function that uses the HSM private key to sign the digest as required.
example signature: 0349003046022100838bde8a902f9ebb18cdd9bc5af263dc978a670d95770c11e2e8d29e3c7b2c28022100d345fa7245fb34c8cf710958da80a638c598c44e2cbd724571dfd9e9ade95008
Actual Client encrypted handshake message after which failure happens:
Consuming ECDHE ClientKeyExchange handshake message (
"ECDH ClientKeyExchange": {
"ecdh public": {
0000: 04 EB B8 76 96 C5 E0 C6 20 73 F0 4C AB 93 F1 A6 ...v.... s.L....
0010: E9 6C 64 B0 BB 72 64 A4 74 75 26 4B E2 79 C0 26 .ld..rd.tu&K.y.&
0020: 42 C8 C8 8F D4 C5 CA EC 22 DA B5 3B 03 E8 E8 19 B......."..;....
0030: 28 28 EF C6 9D EE 80 3A CD A1 60 2B 62 83 52 8F ((.....:..` b.R.
0040: 23 B4 5B 46 1F 76 86 00 0D DF F3 1E 6B 86 01 A4 #.[F.v......k...
0050: 64 09 C9 80 0A 03 C6 EE A4 AA 36 05 F4 45 7A 91 d.........6..Ez.
0060: A5 .
},
}
uj5u.com熱心網友回復:
簽名被移交給 rustls 庫。有一個自定義函式可以根據需要使用 HSM 私鑰對摘要進行簽名。
示例簽名:0349003046022100838bde8a902f9ebb18cdd9bc5af263dc978a670d95770c11e2e8d29e3c7b2c28022100d345fa7245fb34c8cf710958da80a638c5087c44e2dcade99d
假設您正在為實際上是二進制的資料顯示十六進制,那幾乎是正確的。Dehexed 它實際上是一個有效的(或至少有效格式化的)Ecdsa-Sig ,用于嵌入在 (ASN.1) BITSTRING 中的 256 位曲線(例如 P-256 ) 。具體來說 03 49 00 是包含 72 個位元組且沒有未使用/額外位的 BITSTRING 的標記和長度。這 72 個位元組由 30 46 02 21 (33bytes) 02 21 (33bytes) 組成,這是正確編碼:一個構造的 SEQUENCE(標簽 0x10 加上 0x20),值長度為 70 個位元組,包含兩個原始 INTEGER 項(標簽 0x02),每個值長度 33 個位元組。
無論構建 CertificateVerify 訊息的代碼都應該洗掉前 3 個位元組并使用其余位元組。它需要處理不同的長度;如本例所示, DER 簽名最多為 72 位元組,但通常會改為 71 或 70 位元組,偶爾但很少甚至更少。比較openssl rust crate:ECDSA 簽名大小不是 64 位元組?和(鏈接在那里)如何為 java.security.Signature 簽名方法指定簽名長度(我的)Java 的 BouncyCastle 并不總是驗證 OpenSSL ECDSA 簽名。
(PS:ClientKeyExchange 訊息在這里無關。客戶端簽名始終在 CertVerify 訊息中。對于TLS1.3 以下的服務器,簽名確實在 ServerKeyExchange 中,但這與您的情況無關。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/457761.html
標籤:java ssl tls1.2 wireshark activemq-artemis