Recent SSL upgrade also introduced SNI which breaks REST API - rest

We use ServiceNow to communicate with PushBullet via REST API. Recently we started getting the following error
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
And a 500 HTTP error.
After much research it appears SNI has been added as part of an SSL certificate upgrade https://en.wikipedia.org/wiki/Server_Name_Indication
ServiceNow state they do not support SNI in any version.
The tests that you see are from SoapUI and Postman.
The way we test for this is by using the following command:
openssl s_client -connect api.pushbullet.com:443
(this fails)
Adding the -servername parameter is used to pick the correct domain from the certificate that now contains multiple domains:
openssl s_client -connect api.pushbullet.com:443 -servername api.pushbullet.com
CONNECTED(00000003)
depth=1 /C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G2
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/CN=*.pushbullet.com
i:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G2
1 s:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G2
i:/C=US/O=GeoTrust Inc./OU=(c) 2008 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGbDCCBVSgAwIBAgIQTyKV1VxdJTcntEU7ErB0mjANBgkqhkiG9w0BAQsFADBH
<snip>
Is there any way of now accessing PushBullet APIs without SNI?
Gavin.

SNI support has been added as of the Jakarta release:
https://docs.servicenow.com/bundle/jakarta-servicenow-platform/page/integrate/outbound-web-services/concept/outbound-sni-support.html
By default, SNI support is disabled in Jakarta. If it has been enabled on your ServiceNow instance, you would have the glide.outbound.tls_sni.enabled property set to true. Change it to false and your API calls should stop using SNI.

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 to display a server's certificate when the cert is signed by an unknown CA?

I have a server that is giving me TLS problems and I would like to view the cert it is presenting in order to help diagnose the issue. Normally, I would use openssl to display the cert like so:
$ openssl s_client -connect facebook.com:443
However, I don't know what CA signed this server's cert. I thought it was our internal CA but testing suggests otherwise. Thus I cannot use openssl's -CAfile or -CApath flags to specify the CA.
One more restriction. The server is not an HTTP server (it's postgres), otherwise I would just use curl --insecure.
I tried using the -verify flag because according to OpenSSL's documentation:
The verify depth to use. This specifies the maximum length of the server certificate chain and turns on server certificate verification. Currently the verify operation continues after errors so all the problems with a certificate chain can be seen. As a side effect the connection will never fail due to a server certificate verify failure.
It claims to continue after all errors so all problems can be seen. However, all I get is:
1737:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:188:
That is the error message I get with or without -verify.
So how can I view the certificate presented by the server?

How to use an intermediate certificates with mojolicious

Mojolicious lets me specify an ssl certificate and key when starting the app:
> ./myapp.pl prefork --listen 'https://*:8485&cert=my.crt&key=my.key'
I am trying todo this with a rapidssl certificate.
Connecting to this service results in wget being rather unhappy:
$ wget https://example.com:8485/
--2016-06-22 09:50:49-- https://example.com:8485/
Resolving example.com (example.com)... 1.3.2.4
Connecting to example.com (example.com)|1.3.2.4|:8485... connected.
ERROR: cannot verify example.com's certificate, issued by `/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G3':
Unable to locally verify the issuer's authority.
To connect to example.com insecurely, use `--no-check-certificate'.
No big surprise, since when using rapidssl certs in other applications I have
to specify an intermediate certificate as well. So I tried to add this here too by concatenating the intermediate cert to the site certificate, but this has no influence on the outcome.
I also tried to put the intermediate certificate along with the root cert into a separate file and start with:
> ./myapp.pl prefork --listen 'https://*:8485&cert=my.crt&key=my.key&ca=myca.crt'
but the result was equally uninspiring:
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
please advise.
If you want the server to send not only the leaf (servers) certificate but also any other (intermediate) certificates to the client then you simply add these to the cert file in the correct order. This means your my.crt should look like this
----BEGIN CERTIFICATE-----
MII... the leaf certificate
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MII... the first intermediate certificate, i.e. the one which signed the leaf cert
-----END CERTIFICATE-----
...

ADFS server and service provider signing certificate

From ADFS and ADFS 2.0 perspective is it possible to register Service Provider metadata that is using certificate (public key) that is not issued by signing authority ? I mean on self signing certificate.
Yes - you can use a self-signed certificate for the SP and that certificate is reflected in the SP metadata.
So you can generate it with the Java keytool etc.
Also ensure that you generate the certificate for a reasonable period - at least a year otherwise you will have to co0ntinually update the metadata on the ADFS side.
It should not be as described in following document -
Certificate Requirements for Federation Servers in section Determining your CA strategy
"ADFS does not require that certificates be issued by a CA. However, the SSL certificate (the certificate that is also used by default as the service communications certificate) must be trusted by the ADFS clients. We recommend that you not use self-signed certificates for these certificate types."

WSO2 4.6.0 - unable to install CA certificate. Always falling back to default certificate

We are trying to install a third party CA cert on our WSO2 ESB server (Linux). We are unable to install CA certificate. Always falling back to default certificate.
While the WSO2 service is started, it shows that the new cert is being referred and loaded, but at client browser, it gives error and shows the default inbuilt cert.
Can someone guide us to where we need to look for errors?
We followed the instructions from
http://wso2.org/library/knowledge-base/2011/08/adding-ca-certificate-authority-signed-certificate-wso2-products
All steps are followed correctly,
Sreejith, the document you're following is correct. I've used the same to configure our servers.
you can get the certificate being used by your server by executing this from a consumer host (client):
echo -n | openssl s_client -connect esb.yourdomain.co.nz:8243 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > certificate.cert
Then you can see if the certificate being used is the one you expect.
To replace the default certificate, you'll need to replace the WSO_HOME$/repository/resources/security/wsocarbon.jks keystore and modify WSO2_HOME$/repository/conf/carbon.xml and $WSO2_HOME/repository/conf/axis2/axis2.xml with the new keystore (along with alias and passwords) whenever necessary.
According with my experience the keystore need to be replaced. You can not simply add your certificate to the existent keystore, you need to force wso2 server to use your certificate by making it the only certificate in the keystore. Also the certificate CN and alias must match your server hostname (as in the url) for this to work.
Then add the certificate to the WSO_HOME$/repository/resources/security/client-truststore.jks keystore.
And don't forget to restart the server after the changes.
Regards