I have a SOAP service which I am trying to request with suds, but when I send the request the API response that I need to check the SSL connection. This is because I need to add the digital certificate to the request and here is the problem because I only have access to the public key of the certificate, for security I cannot export the private key and I cannot get the pass of this certificate.
All the examples I found for the creation of the request using a certificate, is necessaries to add the cert in many format, like .cert or .pem. But I cannot do that because I don't have the private key, only the public. And when I trying to do that, the SSL verification fail.
I would like create the SSL connection but with the cert installed in windows. I found this code which I can get the cert, but I don't know how create the SSL connection with this information:
import wincertstore
import base64
import ssl
import os
# Certificate Name & Thumbprint to look for
certName = 'certName'
thumbPrint = 'thumbPrint'
def hex_string_readable(bytes):
return ["{:02X}".format(x) for x in bytes]
if os.name == 'nt':
storename = "MY"
with wincertstore.CertSystemStore(storename) as store:
for cert in store.itercerts(usage=None):
if cert.get_name() == certName:
print(cert.get_name())
print(cert)
pem = cert.get_pem()
encodedDer = ''.join(pem.split("\n")[1:-2])
cert_bytes = base64.b64decode(encodedDer)
cert_pem = ssl.DER_cert_to_PEM_cert(cert_bytes)
else:
print("This only works on a Windows System.")
Related
I'm setting up SSL client verification in my python app. At the moment my proof-of-concept code is falling over just establishing a secure connection.
It looks like the certificates I've generated either have a certificate usage without the necessary permissions (more likely IMO) or they have permissions that the server cannot understand or accept (a little less likely IMO).
This should be relatively trivial, but I can't find the right documentation.
I've generated the server and client certificate through OpenSSL. I've done this in the past for other apps without any problem. But I'm much less familiar with creating client certificates. OpenSSL reports that the client certificate I'm using has extensions:
X509v3 extensions:
X509v3 Subject Key Identifier:
AF:AB:9D:AA:88:96:F4:0C:F5:56:9A:2C:DB:B6:BA:D9:DD:11:69:45
X509v3 Subject Alternative Name:
email:a#example.com
X509v3 Basic Constraints:
CA:FALSE
Netscape Cert Type:
SSL Client
X509v3 Authority Key Identifier:
keyid:E1:35:7C:39:7F:39:A4:43:D2:F8:00:59:38:91:71:AF:B9:38:AD:3F
X509v3 Key Usage:
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication
The trivial server test code is:
import ssl
import socket
import logging
_log = logging.getLogger(__name__)
def main():
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain("1B.pem", "key2.pem")
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations("my_ca.crt")
raw_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
try:
# domain replaced for SO question
raw_server_socket.bind(('neptune.example.com', 8812))
raw_server_socket.listen(5)
server_socket = context.wrap_socket(raw_server_socket, server_side=True)
except Exception:
raw_server_socket.close()
raise
with server_socket:
while True:
try:
connection_to_client, address = server_socket.accept()
with connection_to_client:
connection_to_client.write(b'Hello')
except Exception as ex:
print(ex)
if __name__ == "__main__":
main()
This gives the error:
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unsupported certificate purpose (_ssl.c:1076)
... When the client connected with this:
import socket
import ssl
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.load_cert_chain("1C.pem", "key.pem")
raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Domain changed for SO question
conn = context.wrap_socket(raw_socket, server_side=False, server_hostname="neptune.example.com")
conn.connect(("neptune.example.com", 8812))
conn.close()
As hinted at by Steffen Ullrich, it looks like the problem was not with the certificate itself but the CA certificate I used to sign it.
CA certificates are limited in the rights they can authorize. These are represented in the same extensions as the certificates they sign. So for a CA to sign a certificate as a client SSL certificate both the client certificate and the CA certificate must have:
Key usage with "digital signature".
the 1.3.6.1.5.5.7.3.2 extension AKA TLS Web Client Authentication
That is, openssl should report the following for both the CA certificate and the signed client cetificate:
X509v3 Key Usage:
Digital Signature
X509v3 Extended Key Usage:
TLS Web Client Authentication
In my case, the CA didn't have the extended use 1.3.6.1.5.5.7.3.2. It was fine for server certificates but couldn't sign client certificates.
I'm trying to start using new messenger platform from FB.
So i have server with name (i.e.) www.mysite.com
I got a valid SSL certificate for that domain and apache is setup correctly - all good.
I have a python code on my server which runs flask and i point it to these certificates I got for the server:
from flask import Flask, request
from pymessenger.bot import Bot
import requests
import ipdb
from OpenSSL import SSL
app = Flask(__name__)
TOKEN = "<access_token>"
bot = Bot(TOKEN)
#app.route("/webhook", methods = ['GET', 'POST'])
def hello():
if request.method == 'GET':
if (request.args.get("hub.verify_token") == "my_awesome_token"):
return request.args.get("hub.challenge")
if request.method == 'POST':
output = request.json
event = output['entry'][0]['messaging']
for x in event:
if (x.get('message') and x['message'].get('text')):
message = x['message']['text']
recipient_id = x['sender']['id']
bot.send_text_message(recipient_id, message)
else:
pass
return "success"
if __name__ == "__main__":
# tried this - no luck
#context = SSL.Context(SSL.SSLv23_METHOD)
#context.use_privatekey_file('/home/mysite.com.key')
#context.use_certificate_file('/home/intermediate.crt')
# tried this - also same result
context = ('/mysite.com.crt', '/mysite.com.key')
app.run(host='www.mysite.com',port=5002, debug=True, ssl_context=context)
It starts without error and if I navigate with my browser - i see it gets connections.
port 5002 is open inf my firewall.
But when I go to FB and try to subscribe to that URL - it keeps failing with this error:
The URL couldn't be validated.
Callback verification failed with the following errors: curl_errno = 60;
curl_error = SSL certificate problem: unable to get local issuer certificate;
HTTP Status Code = 200;
HTTP Message = Connection established
I've read half the internet on the possible reasons and saw many different suggestions, but I can't make any of those work - i end up with the same result 95% of the time. 5% - some other error due to Flask bugging out with some "crazy" settings I try.
What can be wrong? I'm using certificate files which I got from COMODO.
Also tried in the call back url to use /webhook - no luck either. same error.
As mentioned in the answer above, the issue is that you do not have the issuer certificate which means that a secure connection can not be established. These certificates can be downloaded from your certificate provider, in your case Comodo. Once you have them you need to serve both to the user so a SSL connection can be established. There are multiple ways to do depending on how you are hosting but the easiest way to do it is to concat the certificates together. The order they are concated in IS important and will cause a Key Mismatch error if done in the incorrect order. Your certificate should be first, followed by the issuer certificate and the root certificate, like this:
-----BEGIN CERTIFICATE-----
(Your Primary SSL certificate: your_domain_name.crt)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Your Intermediate certificate: Intermediate.crt)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Your Root certificate: TrustedRoot.crt)
-----END CERTIFICATE-----
This can easily be done from the CLI like this:
cat your_domain_name.crt Intermediate.crt TrustedRoot.crt > combined.crt
This command combines the 3 files in the proper order and stores the output in a new file called combined.crt.
See here for more details.
Add the issuer certificate also. Comodo will issue their own certificate. YOu need to include that while starting the server.
I use certbot and Let's Encrypt.
Follow Installing Client software.
Then run command => sudo ./certbot-auto --apache -d YOUR_DOMAIN_NAME.COM (i tried apache, nginx and flask alone, all works, no need to put https at front)
cd /etc/letsencrypt/live/YOUR_DOMAIN_NAME.COM/
context = ('/etc/letsencrypt/live/YOUR_DOMAIN_NAME.COM/fullchain.pem', '/etc/letsencrypt/live/YOUR_DOMAIN_NAME.COM/privkey.pem')
i used cert.pem instead of fullchain.pem at first and got the above error, succeed after changed cert.pem to fullchain.pem
I want to connect to a SOAP API that does not have WSDL in Python. To connect I need to a add a SSL certificate and authenticate afterwards.
from pysimplesoap.client import SoapClient, SimpleXMLElement
cacert = open(path, 'rb').read() # read the certificate
header = SimpleXMLElement('<Header/>')
credentials = header.add_child('Credentials')
credentials.marshall('Password', 'password')
credentials.marshall('Username', 'username')
client = SoapClient(
location="https://mytest.com/Services/",
cacert=cacert)
client['Header'] = header
client.action = "https://mytest.com/Services/Action1"
client.Action1() # gives SSL error
The result I receive is a SSL error:
SSLHandshakeError: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
Can anyone, please, tell me how to solve this issue? Or can you advise any other library I can use. Most SOAP libraries I found offer connection only to WSDL.
Usually in a pfx file there is the client certificate with key, not the CA file. The libraries seems to expect the client certificate as PEM. You should extract the certificate and the key as show in https://stackoverflow.com/a/9516936/3929826.
Then hand it in to the SoapClient() initiation as cert and key_file argument:
client = SoapClient(location="https://mytest.com/Services/",
cert='mycert.pem',
key_file='mycert.key)
It should also be possible to put both into the cert file.
If that still does not work your have to add the CA certificate as the cacert parameter after you retrieved it as described in https://stackoverflow.com/a/7886248/3929826 .
For further reference see the source code of simplesoap: https://code.google.com/p/pysimplesoap/source/browse/pysimplesoap/client.py#75 .
I am trying to connect to the Visa Direct API, but i am not passing the basic SSL certificate authetification, here is my code:
import requests
headers = { 'Content-Type' : 'Application/json' }
url = 'https://sandbox.visa.com/rsrv_vpp/v1/acnl'
payload = {"SystemsTraceAuditNumber":565690,
"RetrievalReferenceNumber":"505012455690",
"AcquiringBin":409999,
"AcquirerCountryCode":"840",
"PrimaryAccountNumber":"4895070000008881"}
r = requests.post(url, data=json.dumps(payload),
cert =('/etc/ssl/certs/sandbox_cert.pem'), headers=headers,
auth=('370df57a-a8aa-4446-a23e-44a0ef06ea09',
'6023e518-c36c-47a8-b16e-c8a5b3a941ef'))
Ass you can see i am using request and passing the cert argument along with the API user and password info but i keep getting the error:
requests.exceptions.SSLError: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
I get a SSL error when I try to open https://sandbox.visa.com/rsrv_vpp/v1/acnl in Google Chrome.
The Visa Docs say
SSL Server Authentication
The SSL server certificate installed on sandbox.visa.com servers is a
Visa issued self-signed certificate. Client applications need to add
the sandbox.visa.com SSL certificate to their local trust store to
prevent SSL Handshake errors at runtime.
Ensure that your application that connects to the Visa Direct API is
configured (or built) to use the trusted certificate store as a trust
store, and not a key store.
Verify that the application is configured to use the right password
associated with the trust store file.
It looks like you need to do do some SSL Authentication before you can connect to Visa.
I have configured my server to serve only https creating a self-signed certificate. I have a client that I has to validate the server's certificate and after that will download a file from the server.
How do I implement the validation in client? Is there any code example?
My question is similar with this one: How can the SSL client validate the server's certificate?
but although the fine explanation, I didn't find any help.
So far, in my code I create a directory and then I download the file with urllib2:
[...] #imports
def dir_creation(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
def file_download(url):
ver_file = urllib2.urlopen(url)
data = ver_file.read()
with open(local_filename, "wb") as code:
code.write(data)
dir_creation(path)
file_download(url)
Rather than configuring your server to present a self-signed certificate, you should use a self-signed certificate as a certificate authority to sign the server certificate. (How to do this is beyond the scope of your question, but I'm sure you can find help on Stack Overflow or elsewhere.)
Now you must configure your client to trust your certificate authority. In python (2.7.9 or later), you can do this using the ssl module:
import ssl
... # create socket
ctx = ssl.create_default_context(cafile=path_to_ca_certificate)
sslsock = ctx.wrap_socket(sock)
You can then transmit and read data on the secure socket. See the ssl module documentation for more explanation.
The urllib2 API is simpler:
import urllib2
resp = urllib2.urlopen(url, cafile=path_to_ca_certificate)
resp_body = resp.read()
If you wish to use Requests, according to the documentation you can supply a path to the CA certificate as the argument to the verify parameter:
resp = requests.get(url, verify=path_to_ca_certificate)