I've been looking into making an iOS push notification service for one of my apps lately. It has a Python 2.7 backend so I wanted to do it in Python rather than PHP (or anything else).
I've got code that sends a notification and the device receives it, however every time I run the code it asks me to manually enter a 'pass phrase' for the PEM file.
This is not ideal, as I want this to be all automated on the server, when it needs to send a notification, it should just send it. I can't find anything in the docs for Python 2.7 that allow me to automatically set the pass phrase from a variable when connecting.
If anyone knows how to do this in Python 2.7 or any other ideas I would be really grateful.
Here's a snippet of code:
certfile = 'devPEM.pem'
apns_address = ('gateway.sandbox.push.apple.com', 2195)
s = socket.socket()
sock = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv3, certfile=certfile)
sock.connect(apns_address)
Thanks in advance.
So the answer as BorrajaX suggested was to not set a password for the key when prompted. However this is not possible as (at least on my Mac) wants the password to be a minimum 4 characters.
The steps to fix this are:
Create the certificate in the developer portal.
Download and open the certificate locally in Keychain Access
Export the certificate’s private key as a .p12 file from Keychain Access (I named it aps_key.p12).
Run the following on the .p12 key:
openssl pkcs12 -nocerts -out aps_key.pem -in aps_key.p12
Enter a password (which we will strip in a minute).
Run the following to strip the password:
openssl rsa -in aps_key.pem -out new_aps_key.pem
Convert the .cer downloaded from the Developer Center to a .pem file:
openssl x509 -in aps.cer -inform der -out aps.pem
Merge the key and certificate .pem files with the following:
cat aps.pem new_aps_key.pem > final_aps.pem
You can now delete all other files, except for final_aps.pem.
The final_aps.pem file then works with the code above without getting prompted for a password/pass phrase.
This is a useful website where I found the code for removing the password from the .pem file: http://www.sslshopper.com/article-most-common-openssl-commands.html
Edit: If you don't need the certificate and the key in the same file, you can just ignore step 8 and use the aps.pem and new_aps_key.pem files.
Related
I'm trying the use urllib.request.urlopen on a website starting with "https". Error output is:
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
There are many great threads which cover this error. Including this one which mentions SSL Labs rating. I am able to use urllib.request.urlopen on every other "https" site I have tested.
SSL Labs shows the following output:
Key RSA 2048 bits (e 65537)
Issuer Let's Encrypt Authority X3
AIA: http://cert.int-x3.letsencrypt.org/
Signature algorithm SHA256withRSA
Extended Validation No
Certificate Transparency No
OCSP Must Staple No
Revocation information OCSP
Revocation status Good (not revoked)
DNS CAA No (more info)
Trusted Yes
To clarify, my question is: is there a solution for completing the handshake, that doesn't include bypassing the certificate verification? And if there is a solution, can it be solved entirely inside a python script on linux, macOS and Windows?
I cannot answer this question for urllib, but I was able to overcome this problem using python requests instead. Note this will only work if there is a trusted certificate chain for the website in question but the server is perhaps missing a root or intermediate certificate.
Using SSL labs server test (linked here), run the test and scroll down to certification paths. IF it IS the case that there are trusted cert paths, but the server is for some reason not providing the complete chain, you can download the full trusted path here as text. Copy the full certificate chain and save it as a .pem file and pass the path of this file to the requests function:
r = requests.get(url, verify = "path/to/chain.pem")
The requests module can throw all sorts of SSL related certification failures, many of which will be server side problems, and you really want to avoid disabling the SSL verification. This solution is only for the somewhat rare case where a full certificate exists but the issuer or server in question has sloppily omitted either a root or intermediate cert.
You can work around this by adding your missing intermediate certificate to the active X509Store:
cert_text = '''
-----BEGIN CERTIFICATE-----
...put your actual certificate text here...
-----END CERTIFICATE-----
'''
# fill this out depending on which specific intermediate cert you're missing
missing_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_text)
context = ssl.create_default_context() # load default trusted certificates
store = context.get_cert_store() # get the X509Store for that context
store.add_cert(missing_cert) # add your missing cert to it
urllib.request.urlopen(site, context=context)
Note that if you only need to talk to the one server for which you're doing this, you could just pass an appropriate cafile or capath argument to create_default_context().
I was able to solve this issue (for debian-based system, I'm running Debian 9). I still need to test solutions on macOS and Windows.
On the SSL Labs report, under the "Certification Paths" header, it showed:
1 Sent by server www.exampleSITE.com
BLAH BLAH BLAH
2 Extra download Let's Encrypt Authority X3
BLAH BLAH BLAH
3 In trust store DST Root CA X3 Self-signed
BLAH BLAH BLAH
I navigated to /etc/ssl/certs/ and noticed there was no Let's Encrypt certificates present. I then downloaded the .pem and rehashed.
cd /etc/ssl/certs
sudo wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
sudo c_rehash
Then I tested the python line that was giving me an error earlier
page = urllib.request.urlopen('https://www.exampleSITE.com').read()
and it successfully retrieved the page.
I'm following a Python twisted tutorial in which an echo server is being set up in order to talk to a client over a ssl transport layer. The end goal is to obtain an encrypted connection towards our end product. From GUI to a remote embedded client.
The application can be found here: Twisted matrix SSL example
I've created a set of keys like this:
$ openssl req -x509 -newkey rsa:4096 -keyout keys/server.key -out keys/server.crt -sha256
This works except for the fact that the server in this case keeps on asking for the PEM passphrase at every connection from the client.
Enter PEM pass phrase:
My question: How can I make this setup work without inserting my password every time?
I'm afraid to make some hacker friendly mistake in this matter so I would also appreciate any additional safety tips.
TY
I've found a solution ( or workaround ) for this particular problem. I just route the key back into openssl to create a new one.
The first time it would not let me create a key without a pass phrase. This time it only needs a passphrase for accessing the first key. I can't help thinking this is just a trick.
openssl rsa -in server.key -out server_bis.key
Now it works without asking for password every time the client connects.
Zero points for security :-)
I have created a api in flask/python.When someone call my api then it gives error
CURLE_PEER_FAILED_VERIFICATION (60)
The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK. This error code has been unified with CURLE_SSL_CACERT since 7.62.0. Its previous value was 51
When i gone through this error then i found i need add a pem file for Encryption Everywhere DV TLS CA - G1. I downloaded pem file from below url.
https://www.digicert.com/kb/digicert-root-certificates.htm
But i didn't know how to add it.I am not sure i am going on right way.Can someone me guide what i should to need for resolve this issue.
You must update python version.
import OpenSSL
key = ...
signature = ...
data = ...
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, key)
OpenSSL.crypto.verify(x509, signature, data, 'sha1')
So far, I am able to do all of this without any problems. However, it doesn't feel like this is enough security, since the key itself is given to me via an URL (that I am supposed to trust*), and the method to build the signature is publicly available.
So, say the key is said to be verified by "VeriSign Class 3 Code Signing 2010 CA", can anyone tell me how I can go about checking that this is a valid claim?
I'm guessing I need to have the VeriSign certificate locally on my machine. Assuming that I do, where do I go from there?
Thanks!
*the URL is given to me as a parameter in a JSON request. Sure, the URL will be HTTPS and I can check the domain name and all that. But it seems like I should be doing checks on the certificate itself
You are right that you should check the certificate itself. And yes, you need the VeriSign root certificate(s) (and any other intermediate certificates to have the complete chain of trust) which signed the certificate to be checked.
Current Symantec (VeriSign) root certificates can be found here in zipfile.
Download and unzip the zip file and find all certificates you wish to trust and put them together (in pem format) into one certificate bundle file.
Now you need to do the actual verification. Unfortunately, the OpenSSL call you need is X509_verify_certificate. I looked at the source for both pyopenssl and M2Crypto and neither expose that call, so there's no direct Python code you can call to verify the certificate with either of those packages.
However, since you are using pyopenssl you obviously have the openssl library available. Thus you probably already have or can easily install the openssl command-line tool set. If so, you can call the openssl verify command through a pipe by doing something like this:
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, key)
# the command like likes pem format
cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
# the bundle that you created from the zip extraction
certificate_bundle = 'verisign-root-bundle.pem'
# Pipe the cert to the openssl verify command and check the return code
# a return code of 0 is successful verify
import subprocess
p = subprocess.Popen(['openssl', 'verify', '-CAfile', certificate_bundle],
stdin=subprocess.PIPE)
p.communicate(input=cert_pem)
p.wait()
if (p.returncode == 0):
print('Certificate Verified.')
else:
print('Problem with certificate')
The above pipe runs the command
openssl verify -CAfile ca.bundle certificate.pem
Finally, if you're not familiar with openssl, the command to show certificates is
openssl x509 -inform PEM -text -in certificate.pem
Hope this helps!
Maybe I only partly address your question. It seems that your largest worry is the security of the channel via which you obtain the key. You do not show any code of how you obtain that key, but you said that you retrieve it via HTTPS and now you want to verify the authenticity of this connection by certificate verification.
You can comfortably do so using the well-established third-party web client framework requests.
Quote from the docs:
Requests can verify SSL certificates for HTTPS requests, just like a
web browser. To check a host’s SSL certificate, you can use the verify
argument:
requests.get(url, verify=True)
Also:
You can pass verify the path to a CA_BUNDLE file with certificates of
trusted CAs.
The latter could look like
requests.get(url, verify='/path/to/cert.pem')
In case you really want to take control (and reduce complexity), then load the right file from http://www.symantec.com/page.jsp?id=roots and take the verify='/path/to/cert.pem' approach. I guess you need http://www.symantec.com/content/en/us/enterprise/verisign/roots/Class-3-Public-Primary-Certification-Authority-G2.pem
I have a problem.I'm testing some tips about Apple passbook with python. I'm using M2Crypto to obtain the signature.
The code is:
def passwordCallback(*args, **kwds):
return password
smime = SMIME.SMIME()
smime.load_key(key, certificate, callback=passwordCallback)
pk7 = smime.sign(SMIME.BIO.MemoryBuffer(manifest), flags=SMIME.PKCS7_DETACHED | SMIME.PKCS7_BINARY)
pem = SMIME.BIO.MemoryBuffer()
pk7.write(pem)
der = ''.join(l.strip() for l in pem.read().split('-----')[2].splitlines()).decode('base64')
The code is supposed to work well and generate the signature content, the problem is with the "key" and the "certificate".
This two variable are the name of certificate.pem and key.pem, but I have donwloaded only the pass.cert file from the Apple Developer portal.
How is possible to obtain this two files, with openssl or something similar?
SOLVED:
I have solved with this link
http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12
You need to either obtain a certificate from a third-party certification authority (CA) or create a self-signed certificate using something like the process described in openSSL. If you are just testing some code a self-signed cert will work, but a CA-issued cert provides other users some indication that you are who you the cert says you are. You could create a self-signed cert claiming to be Tim_Cook#apple.com, but no reputable CA would issue you such a cert.