Mutual TLS Authentication : validate client cert - certificate

I am not sure if we simply validating the client cert is enough, because if someone steal the cert then they can impersonate others, how do we validating the client? Does client needs to sign some message with its private key , send together with the client cert, and server side get the public key from the cert to validate the message to confirm he is the cert holder?

When a server validates a client certificate, it makes the client prove that the certificate is theirs by performing an operation with its corresponding private key. The private key isn't part of the certificate, so stealing it doesn't allow impersonation of the user.

Related

How to make client accept the server SSL certificate

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

How does the verification server recognize which public key to use in RSA?

I am trying to implement a (simplified) RSA-like verification process in my (Java) application.
The client sends a request (data + private key signature) and the server either rejects his request or processes it - depending on the signature validity.
But I don't understand how the verification server knows which public key to use for signature decryption. Indeed, no public key - nor public key ID - seem to be sent to the verification server.
Does it actually test all authorized public keys ? Or is the public key stored from a previous communication exchange ?
The figure has several errors and some omissions, because it is probably a simplification:
the hash is digitally signed, not encrypted, and the signature is verified, not decrypted. The underlying cryptographic operation is not equivalent.
the signed data should include the certificate and the certification chain
if you use a known format like CMS, pkcs#7 or XMLDsig the hash to sign usually includes also a reference to the signing certificate and content-type to avoid tampering
To validate a signed document you verify the signature using the public key of the attached certificate but it is mandatory to check that the signing certificate is trusted verifying that the certificate itself or the issuing Certification Authority is present in the client's trustore.
The signature includes the certification chain because usually the truststore does not contain the intermediate CAs. The certificates of the truststore are exchanged previously
Additionaly the verification process should check that the certificate is not expired and not revoked
Note that the verification process is the same for all digital certificates in a public key infrastructure, not just RSA
As the figure you attached with the question suggests, the client sends its certificate along with the signature, the certificate contains the public key, the server checks for certificate validity and uses it to check the signature.

For Server validation using a trusted CA, will the ca-public key that was used to sign the server certificate be provided back to the server?

I was working on a sample TLS client/server program to perform certificate validation.
For a self signed certificate validation, these are the steps i followed.
#server side:
Generated a server key file serverkey.key
Generated a CSR certificate servercert.csr from the key file.
Digitally signed(using openssl x509 utility) the servercert.csr using a
generated rootCA.key and rootCA.cert. server certificate file servercert.cert
is generated.
Loaded the certificate file(servercert.cert) and key file(serverkey.key) using
SSL_CTX_use_certificate_file and SSL_CTX_use_PrivateKey openssl apis.
#client side:
Loaded the server ca-file --> rootCA.cert (which was manually copied to the
client) using the SSL_CTX_load_verify_locations api.
Using the SSL_get_verify_result() api validated the certificate that server
sends in the Certificate message.
The question that i have is that if i use a trusted CA(like godaddy) to sign a server CSR certificate, will the CA be providing its public key file (something similar to rootCA.cert) as well which was used for signing ?
By which i can load the same to the trusted list at client side using SSL_CTX_load_verify_locations api.
My intention is to keep the code unchanged regardless of being a self signed certificate or a valid CA provided certificate.
When (any) x509 certificate is generated, these things happen:
Private key is generated
Public key (associated with the private key mentioned above) is embedded in the new certificate (becomes an integral part of it)
The new certificate is signed using private key of the issuer (read: CA)
In order to verify the certificate integrity (to check if nobody tampered with it) - you need to verify the signature (created using issuer's private key - see 3)). To be able to do it you need to obtain (somehow) the issuer's public key. This key is embedded in the issuer's certificate (see 2)). Usually the trusted CAs' certificates are stored in so called trusted certificate store. In case of OpenSSL you specify this "store" by using SSL_CTX_load_verify_locations function (and a few other simmilar functions - consult OpenSSL documentation).
To summarize:
In your case the location pointed by SSL_CTX_load_verify_locations should contain your CA's certificate(s) - all of them - the whole certificate chain up to the self-signed root certificate. You can obtain all of the certificates in the chain from your CA (in your case GoDaddy).
I hope that helps.
If I can clarify anything more please ask.

challenge password in SCEP

What is the purpose of challenge password in simple certificate enrollment protocol (SCEP)?
My understanding is that it is used to authenticate devices.
My question is : How it is different from authentication done by using public and private key pairs?
Challenge password is(/may be) used in the enrollment process. As stated in SCEP specification (section 2.3):
PKCS#10 [RFC2986] specifies a PKCS#9 [RFC2985] challengePassword
attribute to be sent as part of the enrollment request. Inclusion of
the challengePassword by the SCEP client is OPTIONAL and allows for
unauthenticated authorization of enrollment requests. The PKCS#7
[RFC2315] envelope protects the privacy of the challenge password.
When utilizing the challengePassword, the server distributes a shared
secret to the requester which will uniquely associate the enrollment
request with the requester. The distribution of the secret must be
private: only the end entity should know this secret. The actual
binding mechanism between the requester and the secret is subject to
the server policy and implementation.
In section 2.5 draft states:
The challengePassword MAY be used to automatically authorize the
request.
SCEP draft states in section 2.8:
SCEP does not specify a method to request certificate revocation.
but when challenge password was used in the enrollment process then:
In order to revoke a certificate, the requester must contact the CA
server operator using a non-SCEP defined mechanism. Although the
PKCS#10 [RFC2986] challengePassword is used by SCEP for enrollment
authorization (see Enrollment authorization (Section 2.3)) this does
not inhibit the CA server from maintaining a record of the
challengePassword to use during subsequent revocation operations as
implied by [RFC2985].
If a certificate is compromised (the private key is stolen, etc.) the
certificate needs to be revoked as it will remain valid till the end of it's
term.
Any administrator with access to a cert can revoke the cert. If a challenge
password was specified during the certificate signing request that password
will be required before the cert can be revoked.
So, it seems the sole purpose of the challenge password is to prevent
revocation by someone without the password.
SCEP is used to issue certificates to devices (mostly in an untrusted network). The admin will generate challenge password and send it to the user via mail. The SCEP server knows about this challenge password. (We can ask SCEP Server to generate a challenge password and give it to the admin which he shares with respective person). When a device requests SCEP server for certificate with this challenge password, the SCEP server can validate the challenge password and issue certificate.
Actually the device makes first request to get CA cert of the server. It validates the CA Cert. Then the device generates private and public key locally which is what, for instance, iOS MDM agent does. Then a CSR (Certificate Signing Request) is sent to the SCEP server with challenge password. The SCEP Server validates challenge password and now signs the device's public key with its private key. The result is the certificate.
Reference:
https://www.cisco.com/c/en/us/support/docs/security-vpn/public-key-infrastructure-pki/116167-technote-scep-00.html

PKI certificate import

When I login to my bank account using https, it's only a server side SSL authentication before I enter my login info. My browser does the server authentication based on the certificate info from the server during SSL session. I did not have to do any manual import of server certificate as a trusted cert into my browser. It just happens at runtime during SSL exchange.
On the other hand, I have also seen applications where one has to manually import the certificate (using keytool for e.g.) when you look into their install guide.
Question is: If the certificate info is exchanged in the beginning of SSL session, each side has enough info to authenticate the other side. Why would some apps require manual import of certs from each other between client and server. Be it either or both side authentication.
ADDITIONAL INFO based on the responses below:
I was referring the scenario where I was installing a commercial software based on client-server model with client side SSL authentication turned ON. I installed the server on machine A and 2 clients on different machines all in my private network. During install, server generates a self-signed certificate locally. So do the 2 clients. Once installation is complete, I was asked to copy the clients' certs to server machine and manually import them as trusted certs. Also, copy the server cert to client machines and do the import into their trusted store. They provided a wrapper tool on top of java keytool to perform the cert import. Why is this manual import necessary here? The client and server will anyway exchange certificate info during SSL handshake and perform the authentication. Again, these are self-signed certs and CA involved here.
Note that a certificate is signed by a certificate authority so it depends on which certificate authorities your browser trusts. If the Web server sends a certificate signed by a certificate authority that’s trusted by the browser/application and the certificate is valid, you shouldn’t get any warnings whatsoever.
On the other hand, if the browser receives a certificate from the Web server and it doesn’t trust the certificate authority that signed that certificate, the browser will take some action — at the very least, it should warn you about this. When you import a certificate from a Web site, you’re essentially telling your browser that you have decided to trust that certificate independently of who signed it.
Edit: The same reasoning applies: The keystore keeps a list of trusted certificate authorities and their corresponding certificates. The whole concept of PKI is to have a hierarchy of trusted CAs that emit signed certificates for other parties. If a certificate is self-signed, there’s no valid trust chain — how will Java know that the certificate hasn’t been forged by an attacker?
You’re assuming that a connection between a client and a Web server is implicitly trusted just because certificates are exchanged during the SSL handshake. What if a man in the middle poses as the Web server and, instead of sending the server certificate, sends his own certificate instead? How would clients know that the certificate received by the man in the middle is not to be trusted? If the certificate is signed by a trusted CA, or if the certificate has been manually added to the keystore as a trusted certificate, the client can check whether it should trust the certificate or not.
An SSL server's certificate has to be "vouched for" by a certificate authority (CA). Your browser (or other program) contains a list of CAs it trusts. If you're using a site that is not certified by one of the standard CAs, then you'd have to import its CA in order for the verification to succeed.
No legitimate site (especially for online banking) should require you to use an "alternative" CA. Only do this for sites where you're not sending super-sensitive data.