I'd like to clarify two things regarding the SAN and SNI in Hyperledger Fabric:
a) Is Common Name (CN) or Subject Alternative Name (SAN) from the client certificate verified by the server when Mutual TLS (TLS with client authentication) is enabled? (f.i. by resolving DNS name and comparing the consecutive IP address with the IP address of the client (the TCP/IP connection source))?
We have already tested this scenario and discovered that a peer as a server verifies TLS certificates hash regardless whether client authentication is enabled or not. Here is an example of peer logs supporting our conclusion:
2021-02-16 08:54:17.491 UTC [common.deliver] Handle -> DEBU 660f7 Attempting to read seek info message from 10.250.26.32:46320
2021-02-16 08:54:17.491 UTC [common.deliver] deliverBlocks -> WARN 660f8 error parsing envelope from 10.250.26.32:46320: **claimed TLS cert hash is** [235 124 10 219 135 57 10 64 94 166 44 162 123 25 91 195 202 31 164 34 51 5 12 238 77 39 197 171 135 246 12 244] **but actual TLS cert hash is** [185 32 246 230 116 37 63 240 4 9 236 246 210 42 59 45 198 22 209 194 183 117 64 137 153 68 27 56 143 39 211 118]
2021-02-16 08:54:17.492 UTC [orderer.common.server] func1 -> DEBU 660f9 Closing Deliver stream
2021-02-16 08:54:17.492 UTC [comm.grpc.server] 1 -> INFO 660fa streaming call completed grpc.service=orderer.AtomicBroadcast grpc.method=Deliver grpc.peer_address=10.250.26.32:46320 grpc.peer_subject="CN=proxy,OU=peer,O=Hyperledger,ST=North Carolina,C=US" grpc.code=OK grpc.call_duration=239.612µs
We also found a source code on github supposedly responsible for such behaviour (https://github.com/hyperledger/fabric/blob/master/internal/pkg/comm/util.go):
// mutualTLSBinding enforces the client to send its TLS cert hash in the message,
// and then compares it to the computed hash that is derived
// from the gRPC context.
// In case they don't match, or the cert hash is missing from the request or
// there is no TLS certificate to be excavated from the gRPC context,
// an error is returned.
func mutualTLSBinding(ctx context.Context, claimedTLScertHash []byte) error {
if len(claimedTLScertHash) == 0 {
return errors.Errorf("client didn't include its TLS cert hash")
}
actualTLScertHash := ExtractCertificateHashFromContext(ctx)
if len(actualTLScertHash) == 0 {
return errors.Errorf("client didn't send a TLS certificate")
}
if !bytes.Equal(actualTLScertHash, claimedTLScertHash) {
return errors.Errorf("claimed TLS cert hash is %v but actual TLS cert hash is %v", claimedTLScertHash, actualTLScertHash)
}
return nil
}
Is it possible to switch off this verification? Do you have any recommendation how can we configure HLF network with gRPCs reverse proxy between organisations (for incoming connections). In our test we assumed that proxy as a client uses its own certificate issued by CA of HLF organisation (peer’s organisation)?
b) Does fabric peers on version 1.4.1 supports SNI (Server Name Indicator)? The issue is if it's possible with proxy to host multiple https servers on a single IP?
We have already tested this using nginx proxy and this part of communication seems to work. But the question is if peer as a client will send SNI to the server (in our case nginx proxy) to inform it what DNS it is aiming? Thanks to it we can run many virtual servers on the proxy using only on IP address.
a) Of course not, the server doesn't verify the client certificate SAN or CN when the client is reaching it.
The error you are getting is invoked from within the application layer (Fabric) so it means that you actually pass the TLS handshake.
This error is returned probably because:
Mutual TLS (Client side authentication) is turned on in the peer local configuration. When mutual TLS is turned on, the Fabric client sends its TLS certificate hash in the request it sends to the peer, and the peer checks that the TLS hash matches the hash of the TLS certificate it detects from the stream. This is done to prevent a replay attack that will make a malicious peer re-use the same request to request blocks from peers while the malicious peer is not eligible of receiving these blocks.
Your peer is behind a TLS terminating proxy. In such a case, the peer receives a TLS handshake from the proxy and not from the client, and the proxy has a different TLS certificate, so it rejects it.
Do you have any recommendation how can we configure HLF network with gRPCs reverse proxy between organisations (for incoming connections).
You need your proxy to work in a "passthrough mode" (googling TLS passthrough with your favorite proxy will yield you results).
b)
Does fabric peers on version 1.4.1 supports SNI (Server Name Indicator)? The issue is if it's possible with proxy to host multiple https servers on a single IP?
You can do it if your reverse proxy supports it, and it has nothing to do with Fabric. All you need is your proxy to be able to read the SNI in the client hello of the TLS handshake, and route to the correct port/host without terminating the TLS session.
Related
I am using python3 ssl to connect via web sockets to an nginx server. According to my code below, I should be connecting via TLSv1_1.3 since I not using 1, 1.1, or 1.2. My ssl (OpenSSL 1.1.1h 22 Sep 2020) supports TLSv1.3.
sslCon=None
if self.server.startswith("wss"):
sslCon=ssl.SSLContext(ssl.PROTOCOL_TLS)
sslCon.options |= (
ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2
)
self.conn = await websockets.connect(self.server, ssl=sslCon)
My NGINX config specifies TLSv1.3, but when I observer the connection over Wireshark, I see the following. Any ideas to how to diagnose this further?
Frame 3172: 308 bytes on wire (2464 bits), 308 bytes captured (2464 bits) on interface en0, id 0
Ethernet II, Src: Apple_62:32:d8 (XX:XX:XX:XX:XX:XX), Dst: Cisco_9f:f2:8f (00:00:0c:9f:f2:8f)
Internet Protocol Version 4, Src: XXX.XXX.XXX.XXX, Dst: XXX.XXX.XXX.XXX
Transmission Control Protocol, Src Port: 62035, Dst Port: 8189, Seq: 1, Ack: 1, Len: 242
Transport Layer Security
TLSv1 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 237
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 233
Version: TLS 1.2 (0x0303)
Random: dccd34397b86bac156d3ae39483a268ed3536ef09a3557c3…
Session ID Length: 32
Session ID: ea577122c909b7c78e20dbb5f982a7be94169fac8f51886f…
Cipher Suites Length: 8
Cipher Suites (4 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 152
Extension: server_name (len=23)
Extension: ec_point_formats (len=4)
Extension: supported_groups (len=12)
Extension: session_ticket (len=0)
Extension: encrypt_then_mac (len=0)
Extension: extended_master_secret (len=0)
Extension: signature_algorithms (len=30)
Extension: supported_versions (len=3)
Extension: psk_key_exchange_modes (len=2)
Extension: key_share (len=38)
Extension: supported_versions (len=3)
You'll find the support for TLS 1.3 announced inside this extension. This is Perfectly normal, i.e. this is how TLS 1.3 works. The way with not announcing the support for TLS 1.3 directly in the ClientHello version but instead in the extension was done in order to not confuse broken TLS stacks.
How would I diagnose an ssl.WRONG_VERSION_NUMBER error in python3
First by better understanding how TLS works :)
Then by not assuming that the error message means what it says :(
Unfortunately this error message and similar strange ones often happen if one tries to connect to a server which does not speak TLS at all and when the non-TLS response of the server is just interpreted as TLS. Such situations typically happen if the wrong port is used, if the server is misconfigured or if one assumes that the protocol should start with TLS but it does not (like in case with SMTP etc which have some plain TCP initial dialog first). Thus, look at the actual data coming from the server to see if they even look like TLS.
It can also happen that due to misconfiguration or misunderstanding the server does not support the specific TLS version even if you are sure that it should. Therefore look for information in the logs the server writes, especially the error logs.
I would like to create a TLS connection to a server. Then, I want to send some encrypted data to the server. I know the hostname and port and I have the certificate. Surprisingly, I also received the private key of the server. However, I think it is not normal that I received the private key.
The first question is that, do I really need the private key to make a TLS connection?
By the way, I am using this python script
import socket
import ssl
server_addr = '**.**.**.**'
server_port = ****
server_cert = 'server.crt'
server_key = 'server.key' # I use the private key
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain(certfile=server_cert, keyfile=server_key)
bindsocket = socket.socket()
bindsocket.connect((server_addr, server_port))
I am using the private key in the above script. It works without any error. However, when I try to bind() instead of connect(), i.e.,
bindsocket.bind((server_addr, server_port))
I get the following error:
OSError: [Errno 99] Cannot assign requested address
I have read many related questions about the above error, however, I still do not understand why this happens. Since, I have the hostname, port, certificate, and the key, I expect to create a TLS connection successfully.
The second question is that how can I establish a TLS connection? Is my script correct?
I highly appreciate any comment to improve the script.
So, first of all, you should absolutely not have the private key! As the name says, it is private and not necessary to establish a connection.
You could have the public key, but even that is not necessary as long as you use standard SSL and you trust the CA that signed the servers certificate.
Are you sure, it is the private key? Does the file begin with -----BEGIN PRIVATE KEY-----? Check with openssl rsa -noout -text -in server.key.
Refer to the wikipedia article and this post for more on asymmetric cryptography.
Further along the way:
With socket.bind() you bind a socket to a port on your local machine. This is not possible, as your machine does not have the address (you provide a server address).
From your code, it looks like you are trying to open the socket as a server. You will need the private key for that, but then you will be accepting connections and not connect to other machines yourself. I have a feeling, that you are mixing up two things here.
Refer to the python documentation of socket.bind() and to this question as this seems to be closely related.
Also check out the python documentation on ssl. I took the example, that does what you are asking for from said documentation:
import socket, ssl, pprint
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# require a certificate from the server
ssl_sock = ssl.wrap_socket(s,
ca_certs="/etc/ca_certs_file",
cert_reqs=ssl.CERT_REQUIRED)
ssl_sock.connect(('www.verisign.com', 443))
pprint.pprint(ssl_sock.getpeercert())
# note that closing the SSLSocket will also close the underlying socket
ssl_sock.close()
Also have a look on the example on how to open a SSL socket in server mode.
Further thoughts:
Do you really need to do all that TLS stuff yourself? If the server, for example, uses HTTPS (SSL encrypted HTTP), you can just use the http.client library.
Feel free to ask, if you need me to clarify something. I'll update my answer accordingly.
EDIT:
As you indicated, you want to open a port in server mode, I made an example for you (it heavily leans on the python documentation example):
import socket, ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(certfile="cert.pem", keyfile="key.pem")
bindsocket = socket.socket()
bindsocket.bind(('127.0.0.1', 10023))
bindsocket.listen(5)
def deal_with_client(connstream):
data = connstream.recv(1024)
# empty data means the client is finished with us
while data:
print(data)
data = connstream.recv(1024)
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
deal_with_client(connstream)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
Running it:
% python3 ssltest.py
b'hello server!\n'
b'this is data\n'
The client side:
% openssl s_client -connect 127.0.0.1:10023
CONNECTED(00000005)
depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
verify error:num=18:self signed certificate
verify return:1
depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
verify return:1
---
Certificate chain
0 s:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
i:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
---
Server certificate
-----BEGIN CERTIFICATE-----
... certificate ...
-----END CERTIFICATE-----
subject=C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
issuer=C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2272 bytes and written 404 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
... session stuff ...
Extended master secret: yes
---
hello server!
this is data
^C
If you use some usual protocol like HTTP, you should use a library though. Here is a example with flask:
>>> from flask import Flask
>>> app = Flask(__name__)
>>>
>>> #app.route("/")
... def hello():
... return "Hello World!"
...
>>> if __name__ == "__main__":
... app.run(ssl_context=('cert.pem', 'key.pem'))
...
* Serving Flask app "__main__" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [06/Aug/2020 11:45:50] "GET / HTTP/1.1" 200 -
I am trying to implement peer certificate validation in node.js with express.
In the production i receive error: EE certificate key too weak.
How can i change it to support the weak key?
I don't want to ignore it in the code level, because if i am doing that it does not check the CA at all.
In the development server, if i remove the matching CA certificate i receive UNABLE_TO_VERIFY_LEAF_SIGNATURE, while in the production server i receive "EE certificate key too weak" - it does not check it at all.
In the development server it is working correctly, but in the production server i receive the error.
I cannot change the certificate on the client devices, so i must support the weak key.
https.createServer({
key: getFile(config.get("ssl_certificate.key")),
cert: getFile(config.get("ssl_certificate.cert")),
ca: [
getCACertFile('ca-crt.pem'), //some certificates
],
requestCert: true,
rejectUnauthorized: false
},app)
In req.socket.authorizationError, i expect to receive null.
In the development server i receive null, but in the production server i receive "EE certificate key too weak"
stderrs:
error: failed to start server: Error: error:140AB18E:SSL routines:SSL_CTX_use_certificate:ca md too weak
at Object.createSecureContext (_tls_common.js:135:17)
at Server (_tls_wrap.js:873:27)
at new Server (https.js:62:14)
at Object.createServer (https.js:85:10)
Node v10.0.0 Release News
Dependencies
V8 has been updated to 6.6. [9daebb48d6]
OpenSSL has been updated to 1.1.0h. [66cb29e646]
If you are using Node.js>=10.0.0, it will raise the exception if certs are encrypted by sha1 or md5.
Generate new certs encrypted by sha256 will fix the question on Server.
But in your case, since the certs has been used for devices to connect to server, you can simply use Node.js<10.0.0 (eg:v8.x) to start the server.
Besides, suggest to use nvm to control versions of Node.js.
nvm use v8.x.x
node server.js
Two aspects of your typical SSL cert immediately jump to one's mind: RSA key length, and the hash algorithm. The recipe to accept the cert might differ based on which one is weak.
Check the cert properties, under Siganture Algorithm. Is it sha1RSA by any chance? If so, search for enabling SHA1 support.
Check the public key. How many bits in it? Is it less than 1024? Then search for minimum RSA key length setting.
User who has authorized TLS certificate only able to connect to Open-sip server from application (Android and iOS).
What we need to change in config file for only TLS connection to Open-sip server.
You can configure the TLS certificate information in opensips.cfg file
tls_certificate="/usr/local/etc/opensips/tls/glob/glob-cert.pem"
tls_private_key="/usr/local/etc/opensips/tls/glob/glob-privkey.pem"
tls_ca_list="/usr/local/etc/opensips/tls/glob/glob-calist.pem"
## turn on the strictest and strongest authentication possible
tls_verify_client = 1
tls_require_client_certificate = 1
tls_method = TLSv1
tls_verify_client = 1 will ensure the client with authorized certificate configured in tls_ca_list file
Can you try uncommenting the line of startTLS from config file and make it true as a value?
It should work!
Also make sure that your Android and iOS clients are configured to accept TLS connections(though most of the time it's default behaviour).
I use Tyrus webSocket implementation to connect to the server from my JavaFX application. When I try to establish connection over SSL I get this error: javax.net.ssl.SSLException: SSL handshake error has occurred - more data needed for validating the certificate
I tried to use a dummy certificate and host verification as described in Disable Certificate Validation in Java SSL Connections but to no avail.
There is also not much information on Tyrus documentation.
I simply don't know what to do!
P.S. For what it's worth I managed to get around this issue by using Grizzly client
//final WebSocketContainer container = ContainerProvider.getWebSocketContainer();
final ClientManager client = ClientManager.createClient();
URI uri = URI.create(this.uri + "?" + System.currentTimeMillis());
session = client.connectToServer(this, uri);
It sounds like you need to install a certificate chain. I believe you can import the signing certificate using keytool -import. Have you setup the certificate store?