In our MTLS handshake, we have a client certificate issued using ECC and server certificate issued using RSA. We are seeing handshake errors at algorithm check since server does not have ecdsa algorithms, should client and server certificate be of same type (RSA or ECC ) for handshake to work or is there a way to add ecdsa algorithms on server side without changing RSA server certificate ?
During negotiation, the client sends a hello message. It contains the cipher suites the client supports. After receiving the hello message, the server replies hello message which contains the chosen cipher suite. This is the negotiation phase. In your case, the negotiation will fail and cause a failure that the cipher suite algorithm used by the client is not supported by the server.
Related
I captured with wireshark a TLSv1.3 handshake established between a client and server that both support TLSv1.3. I know that the certificate is being sent in an encrypted form but i can't find any field that indicate that here we find the encrypted certificate. Any help please?
In TLS 1.3 servers send their certificates encrypted.
In TLS 1.3 client and server exchange keys at the very beginning: client sends its choice in ClientHello, and the server sends its key_share in ServerHello. Everything after ServerHello is encrypted.
It differs drastically from SSL/TLS prior to TLS 1.3, where key exchange happened AFTER authentication. In SSL and TLS 1-1.2 certificates were sent as plain text.
If you open RFC 8446 on Page 11, you will see the message exchange diagram. Please find the server-side {Certificate*} message. The notation {*} means:
asterisk (*) indicates this is an optional message,
braces {} indicate that
messages protected using keys derived from a [sender]_handshake_traffic_secret
More formally RFC 8446 section 4.4 specifies:
As discussed in Section 2, TLS generally uses a common set of
messages for authentication, key confirmation, and handshake
integrity: Certificate, CertificateVerify, and Finished. ...
These messages are encrypted under keys derived from the
[sender]_handshake_traffic_secret.
I am trying to make REST calls to server from a client.
Server Side
I am using Flask as web server. I have generated the certificate(cert.pem) and public key(key.pem) using the following command.
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
Following is the server side code.
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}
#app.route('/someroute/<arg1>,<arg2>', methods=['GET'])
def fn1(arg1,arg2):
res = fn2(arg1, arg2)
return str(res)
def fn2(un,pwd):
#Do something and return a number
return num
if __name__ == '__main__':
context = ('cert.pem', 'key.pem')
app.run(host="ip", port=port1, debug=True, ssl_context=context)
Client Side
I have a flask application from which I need to make REST calls to the above server. Following is the code I am using to do so.
import requests
# Below is the part of a function which gets called upon hitting a route in Flask.
ret = requests.get("https://<ip>:<port1>/<someroute>/arg1,arg2", verify=True)
print ret.text
This is throwing the following error.
requests.exceptions.SSLError: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
This error is expected as the certificate is not known to the client. Everything is working perfectly fine if I skip the certificate verification (verify=False).
How do I make the client trust this unknown server SSL certificate? I have seen solutions where I need to use Nginx on the client side. Can we not do some changes on flask(client side) itself to make it trust my server? If yes, what is that exact change I should be making in Flask? Also, suggest if urllib is better than requests.
UPDATE 1
I have another query here. From above, I understood that requests.get verifies the certificate every time we make a REST call (Please correct me if I am wrong). But, this looks like an extra load. Can we not have an encrypted connected established in the beginning and exchange the data by encrypting it with a private key? So, how to implement the beginning part - Send private key by encrypting it with the public key to the server and then start exchanging data by encrypting the data with the private key.
OC (original comment):
Server Side
I am using Flask as web server. I have generated the certificate(cert.pem) and public key(key.pem) using the following command.
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 36
If you read man req it says,
-out filename
This specifies the output filename to write to or standard output by default.
-keyout filename
This gives the filename to write the newly created private key to. If this option is not specified then the filename present in the configuration file is used.
Here, cert.pem could be referred to as the public certificate and key.pem really is your private key (Keep it safe and well, private). FTFY.
OC (original comment):
This error is expected as the certificate is not known to the client. Everything is working perfectly fine if I skip the certificate verification (verify=False).
How do I make the client trust this unknown server SSL certificate? I have seen solutions where I need to use Nginx on the client side. Can we not do some changes on flask(client side) itself to make it trust my server? If yes, what is that exact change I should be making in Flask? Also, suggest if urllib is better than requests.
I'll answer this backwards, but first a little background. I found these links really useful to understand - on a high level - how the SSL/TLS handshake happens and how authentication is carried out
IBM: An overview of the SSL or TLS handshake
IBM: How SSL and TLS provide authentication
Now, coming back to your question,
requests > urllib (Personal opinion, but requests does have a good support and is mature - not implying that urllib isn't)
It looks like you want to do server authentication only, for that your client needs the server's public certificate. From How SSL and TLS provide authentication:
The certificates required are as follows, where CA X issues the certificate to the SSL or TLS client, and CA Y issues the certificate to the SSL or TLS server:
For server authentication only, the SSL or TLS server needs:
The personal certificate issued to the server by CA Y
The server's private key
and the SSL or TLS client needs:
The CA certificate for CA Y
Use it in requests.get referring this from requests, which says,
You can pass verify the path to a CA_BUNDLE file or directory with certificates of trusted CAs:
requests.get('https://github.com', verify='/path/to/certfile')
Once you do that and have good certificates, it should work.
NOTE: The openssl command in the OC does not show any subject for the key/cert pair, this would still fail cert verification, refer documentation that shows how to generate self-signed certs. There's a requirement to provide SANs (SubjectAltNames) so providing just a CommonName (CN) in the subject won't future proof your process.
EDIT:
The CA talk in the IBM document can be confusing, but since this is a self-signed cert, you don't have to worry about this, just think of it as your Flask server being the SSL/TLS server, and any client of that server can do server verification using server's public key.
OC (original comment):
From above, I understood that requests.get verifies the certificate every time we make a REST call (Please correct me if I am wrong). But, this looks like an extra load.
That's true. It would perform verification each time. To avoid that you can use requests.Session() as mentioned in requests SSL Cert Verification
OC (original comment):
Can we not have an encrypted connected established in the beginning and exchange the data by encrypting it with a private key? So, how to implement the beginning part - Send private key by encrypting it with the public key to the server and then start exchanging data by encrypting the data with the private key.
This is explained really well in the IBM docs listed
I have configured a web application with TLS 1.0. My requests are going in the encrypted format through out the channel, which is secured for from the man in the middle attack. TLS is working on RSA mechanism, so the doubt is if the network admin (having the private key) will be able to decrypt the request or not.
First, system or network admin do not have access to the private key if you add an HSM and configure your web server to use it (see https://en.wikipedia.org/wiki/Hardware_security_module).
Secondly, many PFS cipher suites are available with TLS 1.0 (see https://security.stackexchange.com/questions/74270/which-forward-secrecy-cipher-suites-are-supported-for-tls1-0-protocols), so if you only accept such cipher suites on your web server, somebody that can capture your communications and that knows the RSA private key will not be able to decrypt the content of the channel: the cipher key used to protect the channel is ephemeral, it is not your RSA private key.
On Windows 7Pro, I have a secure socket client app using schannel.
Connecting to a secure server with TLSV1.0, 1.1 and 1.2, without client authentication works fine.
Connecting to a secure server with TLSV1.0, 1.1, with client authentication works fine.
A connection with client authentication with TLSV1.2 fails. Instead of replying with handshake certificate, the client replies with 3 TLS records: Client Key exchange-Cipher change spec-Client key exchange. As the server does not receive the client's certificate, it aborts the connection.
The client's certificate has an intermediate and trusted root ca. In all cases AcquireCredentialsHandle is called with
SCH_CRED_NO_DEFAULT_CREDS|SCH_CRED_MANUAL_CRED_VALIDATION|SCH_CRED_NO_SERVERNAME_CHECK,
and InitializeSecurityContext with (among others)
ISC_REQ_MANUAL_CRED_VALIDATION and ISC_REQ_USE_SUPPLIED_CREDS.
Do I need to treat client authentication differently for TLS V1.2?
I'm using SMTP transport. I would like to use TLS but my hosting has self-signed certificate.
It is possible to use TLS in such situation?
TLS itself has no concept of the certificate being self signed or not. When you initiate a TLS connection (either by connecting to a specific port or via STARTTLS) the server and client negotiate the TLS connection.
As part of the TLS negotiation it is up to the client and server to decide whether the certificate that they're presented is valid or not. If the certificate is self signed it's possible that the client (I'm assuming you're the server) may reject the certificate because it's not issued by a known CA or it might accept it.
It's therefore possible to use TLS with self signed certificates (we do it) but it's also possible that a client could reject the connection because it cannot verify the certificate. If you full have control of the clients (which you do here) you can of course aid this and ensure you always accept your hosts certificate.