I have simple client/server SSL code which worked fine on Python 3.2. However, I decided to switch over to 2.7 (due to abundance of third party modules), and now the code is failing. The code is as follows:
Client:
def connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(('localhost', 4430))
self.ssl_sock = ssl.wrap_socket(self.sock, cert_reqs = ssl.CERT_NONE, ssl_version = ssl.PROTOCOL_TLSv1)
Server:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.hostname, 4430))
self.sock.listen(5)
while True:
self.newsocket, self.fromaddr = self.sock.accept()
self.ssl_sock = ssl.wrap_socket(
self.newsocket,
server_side = True,
certfile = "cert.pem",
ssl_version=ssl.PROTOCOL_TLSv1
)
self._handle_client(self.ssl_sock)
The cert.pem (located in same directory as server .py file):
-----BEGIN RSA PRIVATE KEY-----
(812 "random" characters here)
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
(1260 "random" characters here)
-----END CERTIFICATE-----
The failure is as follows:
1. server waits on self.sock.accept()
2. client connects with self.sock.connect()
3. server proceeds to wrap_socket, which fails with the following error:
Traceback (most recent call last):
File "C:\Program Files\Python27\lib\multiprocessing\process.py", line 258, in _bootstrap
self.run()
File "C:\workspace\projectc\server\server\clientlistener.py", line 49, in run
ssl_version=ssl.PROTOCOL_TLSv1
File "C:\Program Files\Python27\lib\ssl.py", line 381, in wrap_socket
ciphers=ciphers)
File "C:\Program Files\Python27\lib\ssl.py", line 141, in __init__
ciphers)
SSLError: [Errno 336265218] _ssl.c:351: error:140B0002:SSL routines:SSL_CTX_use_PrivateKey_file:system lib
P.S.
1) I have suspicions about the wrap_socket call because even when I use a non-existent file for certfile, the same error results.
2) I tried the alternative wrapping on the python documentation on ssl (i.e. with ssl.SSLContext) and it's odd that 'module' object has no attribute 'SSLContext', when it is part of the ssl module. This wasn't an issue when I tried it with Python 3.2.
Update:
I have found that the problem only occurs when I'm doing "run" from within Eclipse, not so when I run both files from separate command prompts. Still investigating the issue...
Update 2:
I tried a very simple client/server script with the SAME code and it works. Now, with the server code sitting inside a multiprocessing subprocess (launched by the main server process), it seems not to work. Related?
Problem solved. I believe Eclipse was holding some residual information, though I never figured out what information it was holding. It might have to do with the migration from python 3.2 to 2.7, though I previously already changed that in the run config. Resetting the Eclipse environment worked (simply rebooting the computer wasn't enough).
my solution was that when creating my .pem file i set a blank password and assumed it meant no pasword. so the server was still expecting to use a password. i had to manually remove the password.
here is a little how to guide if it helps anyone (also, please note i was coding python for a server side to an iOS app)
NOTE: need to follow directions from apple’s developer website to create certificate first
then export the .p12 file,
by exporting the embedded private key that is created (in ‘keychain access’),
NOT the actual certificate
————————————————————————————————————
————————————————————————————————————
FOR DEVELOPMENT CERT:
After getting the p12 file, it needs to be converted to the PEM format by executing this command from the terminal:
$ openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns_dev.p12
$ openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns_dev.p12
If you wish to remove the passphrase execute the following:
(NOTE: using a ‘blank’ password when exporting/converting, is still indeed setting a password,
hence you should still execute the following if you intend to have no password)
$ openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem
Finally, you need to combine the key and cert files into a apns-dev.pem file we will use when connecting to APNS:
$ cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem
————————————————————————————————————
FOR PRODUCTION CERT:
After getting the p12 file, it needs to be converted to the PEM format by executing this command from the terminal:
$ openssl pkcs12 -clcerts -nokeys -out apns-prod-cert.pem -in apns_prod.p12
$ openssl pkcs12 -nocerts -out apns-prod-key.pem -in apns_prod.p12
If you wish to remove the passphrase execute the following:
(NOTE: using a ‘blank’ password when exporting/converting, is still indeed setting a password,
hence you should still execute the following if you intend to have no password)
$ openssl rsa -in apns-prod-key.pem -out apns-prod-key-noenc.pem
Finally, you need to combine the key and cert files into a apns-dev.pem file we will use when connecting to APNS:
$ cat apns-prod-cert.pem apns-prod-key-noenc.pem > apns-prod.pem
Related
I'm currently reading Foundations of Python Network Programming and came across the following example, demonstrating how to use Python's ssl module:
Listing 6-3: Securing a Socket with TLS for Both Client and Server in Python 3.4 or Newer
#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter06/safe_tls.py
# Simple TLS client and server using safe configuration defaults
import argparse, socket, ssl
def client(host, port, cafile=None):
purpose = ssl.Purpose.SERVER_AUTH
context = ssl.create_default_context(purpose, cafile=cafile)
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
raw_sock.connect((host, port))
print('Connected to host {!r} and port {}'.format(host, port))
ssl_sock = context.wrap_socket(raw_sock, server_hostname=host)
while True:
data = ssl_sock.recv(1024)
if not data:
break
print(repr(data))
def server(host, port, certfile, cafile=None):
purpose = ssl.Purpose.CLIENT_AUTH
context = ssl.create_default_context(purpose, cafile=cafile)
context.load_cert_chain(certfile)
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind((host, port))
listener.listen(1)
print('Listening at interface {!r} and port {}'.format(host, port))
raw_sock, address = listener.accept()
print('Connection from host {!r} and port {}'.format(*address))
ssl_sock = context.wrap_socket(raw_sock, server_side=True)
ssl_sock.sendall('Simple is better than complex.'.encode('ascii'))
ssl_sock.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Safe TLS client and server')
parser.add_argument('host', help='hostname or IP address')
parser.add_argument('port', type=int, help='TCP port number')
parser.add_argument('-a', metavar='cafile', default=None,
help='authority: path to CA certificate PEM file')
parser.add_argument('-s', metavar='certfile', default=None,
help='run as server: path to server PEM file')
args = parser.parse_args()
if args.s:
server(args.host, args.port, args.s, args.a)
else:
client(args.host, args.port, args.a)
The book says to download the following two files, which are a self-signed CA and a second CA (signed by the self-signed CA) for the hostname, "localhost":
ca.crt
localhost.pem
The code is, then, supposed to be run as follows:
Server
python safe_tls.py -s localhost.pem '' 1060
Client
python safe_tls.py -a ca.crt localhost 1060
However, whenever I run the server and client together, I get the following error for each:
Server
Listening at interface '' and port 1060
Connection from host '127.0.0.1' and port 35148
Traceback (most recent call last):
File "safe_tls.py", line 50, in <module>
server(args.host, args.port, args.s, args.a)
File "safe_tls.py", line 35, in server
ssl_sock = context.wrap_socket(raw_sock, server_side=True)
File "/usr/lib/python3.6/ssl.py", line 407, in wrap_socket
_context=self, _session=session)
File "/usr/lib/python3.6/ssl.py", line 814, in __init__
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1068, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:833)
Client
Connected to host 'localhost' and port 1060
Traceback (most recent call last):
File "safe_tls.py", line 52, in <module>
client(args.host, args.port, args.a)
File "safe_tls.py", line 15, in client
ssl_sock = context.wrap_socket(raw_sock, server_hostname=host)
File "/usr/lib/python3.6/ssl.py", line 407, in wrap_socket
_context=self, _session=session)
File "/usr/lib/python3.6/ssl.py", line 814, in __init__
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1068, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
I already tried generating my own self-signed CA and secondary CA (signed by the self-signed CA) for the hostname, "localhost", but I still get the same errors when running the client and server.
Could anyone please tell me what I am doing wrong?
Thank you.
P.S. I am running Python 3.6.5 on Ubuntu 18.04 LTS in case it matters...
UPDATE
I was able to get the code above to work without the secondary certificate, but rather with a single self-signed certificate which I generated as follows:
$ openssl req -x509 -nodes -newkey rsa:4096 -keyout localhost.key -new -out localhost.crt
$ cat localhost.crt localhost.key > localhost.pem
Then, I ran the safe_tls.py program exactly as described above, except with localhost.crt instead of ca.crt.
Still, I would like to be able to sign one CA with another, and then use the secondary CA as I was trying to originally. Any help figuring out how to do this would be greatly appreciated!
Alright, so I ended up finding the answer to my own question, so I'll post it here.
I don't know what is wrong with the ca.crt and localhost.pem files provided at the public repo of Foundations of Python Network Programming; however, I managed to generate and sign my own CAs, getting it to work as I had originally wanted.
Here are the commands I had to type:
$ # I don't know what these files are, but OpenSSL complains if they do not exist...
$ mkdir demoCA
$ touch demoCA/index.txt demoCA/index.txt.attr
$ echo '01' > demoCA/serial
$ # Create a self-signed certificate and private key.
$ openssl req -x509 -nodes -newkey rsa:4096 -keyout ca.key -new -out ca.crt
$ # Create a certificate signing request for server, "localhost", and a private key.
$ openssl req -nodes -newkey rsa:4096 -keyout localhost.key -new -out localhost.csr
$ # Sign the certificate request.
$ openssl ca -keyfile ca.key -cert ca.crt -in localhost.csr -outdir . -out localhost.crt
$ cat localhost.crt localhost.key > localhost.pem
NOTE:
For each of the openssl req ... commands, executed above, I had to fill in some information at the command-line (since I did not create a configuration file). All the information, except for the host name and email must be the same across the two certificates, otherwise the last command, where we sign the certificate request, will fail.
The server and client can now be successfully run as originally shown:
Server
python safe_tls.py -s localhost.pem '' 1060
Client
python safe_tls.py -a ca.crt localhost 1060
I have some Ansible playbooks I want to run against some Windows hosts. I've followed the various Ansible guides for setting up WinRM and they have worked fine, but the default setups are very insecure and I want something more production ready. However, the instructions for how to do this are incredibly sparse. So far I've done the following:
On my Windows box:
Enabled WinRM using the supplied ConfigureRemotingForAnsible.ps1 script
Configured target machine to to use a valid cert on HTTPS/5986 rather than the self-signed one generated by the above script
Enabled both Kerberos and CredSSP auth methods on the target machine in WinRM. Some of my role steps require CredSSP to work reliably.
So far so good, the Windows side seems to work fine. However, getting Ansible to connect is proving a nightmare. I can't figure out how to get Ansible to trust the HTTPS cert on the target despite adding it. On my Centos 7 'push box' I've done the following:
Install Ansible and pip with the pywinrm, requests_kerberos and requests_credssp modules
Added my CA certificate to both /etc/pki/tls/certs and /etc/pki/ca-cert
Set my inventory to the following:
ansible_user=ADMINISTRATOR#DOMAIN.COM
ansible_password=Password1
ansible_port=5986
ansible_connection=winrm
ansible_winrm_scheme=https
/#ansible_winrm_server_cert_validation=ignore
ansible_winrm_transport=credssp
With certification validation turned on the connection fails with the following error:
fatal: [host.domain.com]:
UNREACHABLE! => {
"changed": false,
"msg": "credssp: HTTPSConnectionPool(host='host.domain.com', port=5986): Max retries exceeded with url: /wsman (Caused by SSLError(SSLError(\"bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)\",),))",
"unreachable": true}
With cert validation turned off it works fine.
So my question: How do I get Ansible to trust my CA cert?
This isn't simple. The problem is there are 3 different groups that don't really want to play:
Ansible: doesn't want to be responsible for OpenSSL or Windows details
OpenSSL: doesn't want to be used as a certficate authority
Microsoft: doesn't want you to use anything but Windows tools
First, you will need 3 sets of OpenSSL certs/keys: the self-signed Certificate Authority (CA), the server winRM HTTPS, and the target user.
There is nothing magic about the CA cert/key; it can be generated with the following:
openssl req -new -out caroot.req -keyout caroot.key -days 730 ...
openssl x509 -req -in caroot.req -extensions v3_ca -signkey caroot.key -out cacert.pem -days 730
There are a myriad of other options to these OpenSSL commands which may (or not) work in your specific environment. This is one of those instances of "don't want to be responsible for OpenSSL".
Second, you will need the WinRM HTTPS cert/key. First, you have to generate the server cert. The key detail of generating the cert is that the 'common name' attribute on the cert MUST match the target hostname, as with any HTTPS cert.
openssl req -new -out server.req -keyout server.key -days 730 -nodes ...
Now, here comes the tricky part. You can't just sign the key as-is; the signing process must add a couple of additional attributes to the cert or it won't work. Create a text file I'll call 'attributes.txt' with the following two lines:
subjectAltName=DNS:hostname.mycompany.com,DNS:hostname
extendedKeyUsage=serverAuth
The first line, the 'subjectAltName', is required or the Python/OpenSSL client will reject the key. You need to substitute the proper values for the target hostname. The second line is required by Windows, or Windows will not use this as for an HTTPS server.
Now sign the key with the CA generated earlier:
openssl x509 -req -in server.req -out server.pem -days 730 -CA caroot.pem -CAkey caroot.key -extfile attributes.txt
Another little wrinkle: Windows will not import a server key unless you combine the key and cert into a PKCS#12 file. This can be done with the following command, which creates a PKCS#12 file with no password:
openssl pkcs12 -export -password pass: -inkey server.key -in server.pem -out windows.pfx
This will generate a file 'windows.pfx' which we'll need later.
Bored yet?
Next we need another key/cert for the user login. This is assuming that we're going to be using a local user on the system, not a domain user. (You're on your own for that.) Generating the key is almost the same as for the server key, except that the 'commom name' attribute (CN) MUST match the target username. (In later usage, I'll call our user 'ansible').
openssl req -new -out winlogin.req -keyout winlogin.key -days 730
This private key is going to be the login key, analogous to an SSH private key. Next, we must sign the req. As with the server key, we need to add some extended attributes, or it won't work. Our 'attributes.txt' file must contain the following lines:
subjectAltName=otherName:1.3.6.1.4.1.311.20.2.3;UTF8:ansible#localhost
extendedKeyUsage=clientAuth
The oddball identifier in the 'altNames' is some proprietary Microsoft thingy, which must contain the 'username#localhost'. As I mentioned before, I'm using a local user 'ansible'. The extendedKeyUsage attribute is required or Windows will not allow the key to be used for user authentication. Finally, we sign the cert:
openssl x509 -req -in winlogin.req -out winlogin.pem -days 730 -CA caroot.pem -CAkey caroot.key -extfile attributes.txt
Just so you know, I'm starting to get on my own nerves here. We're almost there. Finally, copy the CA cert (caroot.pem), the server PKCS#12 cert/key (windows.pfx), and the user cert (winlogin.pem) to the target windows system somewhere. Run the following PowerShell script from the same directory. It will create the local ansible user and import all of the SSL artifacts into their proper target locations. Sorry if this isn't commercial quality code. This also has a hardcoded password for the ansible user, but that is irrelevant and can be freely changed. If this actually runs successfully, and you survive the shock, the file artifacts (the PFX and PEM files) can be removed from the Windows server.
Also note that this script adds a firewall rule at the end. This, of course, will have to be appropriately modified for your environment.
# master script to enable winRM via HTTPS and allow certificate-based
# authentication
# add the local 'ansible' user
$username = "ansible"
$password = ConvertTo-SecureString -string "Ans!b123" -AsPlainText -Force
New-LocalUser -Name $username -AccountNeverExpires -Description "Ansible Remote Management" -Password $password
Add-LocalGroupMember -Member "ansible" -Group "Administrators"
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
# import the Ansible root CA; certlm should show this in the
# 'Trusted Root Certification Authorities' folder
$caroot = Import-Certificate -FilePath ".\caroot.pem" -CertStoreLocation "Cert:\LocalMachine\Root"
# import the user cert; certlm should show this in the 'Trusted People' folder
$userkey = Import-Certificate -FilePath ".\winlogin.pem" -CertStoreLocation "Cert:\LocalMachine\TrustedPeople"
New-Item -Path WSMan:\localhost\ClientCertificate -subject "ansible#localhost" -URI "*" -Issuer $caroot.Thumbprint -Credential $credential -Force
# import the server certs - should appear in 'Personal' folder. The PFX file
# must contain both the cert and private key
$srvcert = Get-ChildItem -Path ".\windows.pfx" | Import-PFXCertificate -CertStoreLocation "Cert:\LocalMachine\MY" -Exportable
# Now create the winRM instance
$selector_set = #{Address= "*" Transport = "HTTPS" }
$value_set = #{CertificateThumbprint = $srvcert.Thumbprint}
New-WSManInstance -ResourceURI "winrm/config/Listener" -SelectorSet $selector_set -ValueSet $value_set
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -value $true
# add a firewall rule
New-NetFirewallRule -DisplayName "Ansible WinRM" -Direction Inbound -Protocol "TCP" -LocalPort "5986" -Action Allow -RemoteAddress #("192.16.2.3")
Okay, now that Windows is done, we should be ready for Ansible. Within your inventory, you will need the following variables to apply to the Windows systems:
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_ca_trust_path: /path/to/caroot.pem
ansible_winrm_transport: certficate
ansible_winrm_cert_pem: /path/to/winlogin.pem
ansible_winrm_cert_key_pem: /path/to/winlogin.key
At this point, Ansible should connect to the Windows server using HTTPS and certificate authentication. I hope I hit all of the steps, it was a while ago I set this up.
Run this on the ansible control host:
python -c "import ssl; print(ssl.get_default_verify_paths())"
this way you will see where python expects to find the certificate
Ensure that the CA trust certificate is pem-encoded
regards
This worked for me..
https://dodgydudes.se/validate-ca-certificate-in-ansible-connecting-with-winrm/
ansible_winrm_ca_trust_path: /etc/ssl/certs #TLS 1.2
ansible_winrm_transport: ntlm
I was getting SSL error in python while connecting to a secured url. As a quick work around I passed verify=false and it worked. Later I got the .cer file now that file path is given to verify. Now I get
SSLError: unknown error (_ssl.c:2825)
What is the problem?
Is it because I gave .cer instead of .pem?
Can I convert .cer to .pem file ?
How to fix this problem?
Essentially, yes - this "unknown error" is a result of having a .cer file instead of a .pem file. I just ran into this, with a different line number (_ssl.c:4025 or so) but otherwise identical symptoms, and using a .pem certificate file instead fixed it.
Courtesy of HUB and Marcel Friedmann on Server Fault, here's how to convert a certificate between those formats:
Open the certificate file with Notepad++ or similar. If it starts with -----BEGIN CERTIFICATE-----, it's already in the right format - just rename it to .pem.
Otherwise, If you have OpenSSL installed, it's a fairly easy matter:
openssl x509 -inform der -in certificate.cer -out certificate.pem
If you don't have OpenSSL installed, but do have Java's keytool, you can use that, but it's a little convoluted.
First, find a keystore that won't mind some use. If you don't have one, create one:
# create a dummy certificate in the file test.keystore, forcing the keystore to be created
keytool -genkey -alias test -keystore test.keystore
# and now delete the cert
keytool -delete -alias test -keystore test.keystore
# the (empty) keystore will still exist
Then, import the .cer-format cert:
keytool -import -trustcacerts -alias test -file certificate.cer -keystore test.keystore
Finally, export it as .pem (making sure it's actually in the right format):
keytool -exportcert -alias test -file certificate.pem -rfc -keystore test.keystore
If you don't have OpenSSL or Java keytool, you'll need to install one of them, or find another way.
At the moment I am trying to figure out how to do an MQTT publish using the .crt and .pem files for our application.
below is some pseudo code for what I am trying to do:
connect to an existing topic over tls
publish a message
import paho.mqtt.publish as mqtt
import paho.mqtt.client as mqttclient
topic = "Some/Topic/Goes/Here"
my_ca_cert = open("<path_to_cert>\ca.crt", 'rb').read()
my_pri_cert = open("<path_to_cert>\private.pem", 'rb').read()
my_key_cert = open("<path_to_cert>\certificate.pem", 'rb').read()
mqttc = mqttclient.Client("Python_Ex_Pub")
mqttc.tls_set(my_ca_cert, certfile=my_pri_cert, keyfile=my_key_cert)
mqttc.connect("<gateway_address>", 8883)
mqttc.publish(topic_name, "This is a test pub from Python.")
mqttc.loop(timeout=2.0, max_packets=1)
When I run the script the following error is thrown:
Traceback (most recent call last):
File "mqtt_pub_test.py", line 9, in <module>
mqttc.tls_set(my_ca_cert, certfile=my_pri_cert, keyfile=my_key_cert)
File "C:\Python27\lib\site-packages\paho\mqtt\client.py", line 557, in tls_set
raise IOError(ca_certs+": "+err.strerror)
IOError: -----BEGIN CERTIFICATE-----
<cert_info_here>
-----END CERTIFICATE-----: No such file or directory
I read through the TLS example on the paho doc page, but just do not understand how I should be passing the crt / pem files in my code.
At one point I was only pointing to the folder containing the files, and even went as far as to chmod 777 the folder but at that point I was getting access denied from python at run time.
Any help is appreshiated
Do you want to do TLS client authentication (that is, your Python script needs to authenticate to the server / MQTT broker)? Or do you want your Python script to behave like a web browser, and just validate the server certificate?
If you only want the latter, I have had success using the tls_set() method in the Paho Python client when I point it to a PEM file containing the server's certificate. And that's the only argument you need to pass on tls_set() to have the Paho client validate the server certificate, and connect to the broker using TLS. For example:
mqttc.tls_set("/home/bob/certificates/mqttbrokercertificate.pem")
How do you obtain the mqtt broker's certificate in PEM format? The easiest way is with openssl:
openssl s_client -host mqtt.broker.hostname.com -port 8883 -showcerts
Redirect the output to a file, and delete every line in the file except what is between the "BEGIN CERTIFICATE" and "END CERTIFICATE" lines (inclusive -- be sure to include these lines as well). This is a good article here on StackOverflow about how to save a server's SSL certificate using openssl:
How to save server SSL certificate to a file
Lastly, you need to be sure of what version of TLS your broker supports, and make sure your Python client also supports it. For example, the IBM Watson IoT Platform requires TLS 1.2. The ssl module in Python 2.7 (which is built on openssl) does not support TLS 1.2. Generally, you need Python 3.X, and openssl of at least 1.0.1. Here is how you can set the TLS version on the Paho client (don't forget to import ssl):
mqttc.tls_set("/home/bob/certificates/mqttbrokercertificate.pem", tls_version=ssl.PROTOCOL_TLSv1_2)
If you want TLS client authentication, that's probably better handled in an entirely separate article. But I hope this helps with TLS server authentication using the Paho Python client.
Looking at the error it would suggest that the tls_set function is expecting paths to the files not the contents of the file to be passed in.
I am trying to implement APNS for my iOS push app from this tutorial raywenderlich tutorial.
As per the description I have done everything.
After running this command
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem
I got a big message showing that everything is connected as expected.
I have two files now PushChatCert.pem and PushChatKey.pem
and token_hex = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
So for sending push notification from server I following PyAPNs.
After doing all these in my python shell it is throwing IOError: [Errno 19] Operation not supported by device
import time
from apns import APNs, Frame, Payload
apns = APNs(use_sandbox=True, cert_file='PushChatCert.pem', key_file='PushChatKey.pem')
# Send a notification
token_hex = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
payload = Payload(alert="Hello World!", sound="default", badge=1)
apns.gateway_server.send_notification(token_hex, payload)
Error
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
apns.gateway_server.send_notification(token_hex, payload)
File "/Library/Python/2.7/site-packages/apns.py", line 544, in send_notification
self.write(self._get_notification(token_hex, payload))
File "/Library/Python/2.7/site-packages/apns.py", line 273, in write
return self._connection().write(string)
File "/Library/Python/2.7/site-packages/apns.py", line 254, in _connection
self._connect()
File "/Library/Python/2.7/site-packages/apns.py", line 230, in _connect
self._ssl = wrap_socket(self._socket, self.key_file, self.cert_file)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ssl.py", line 891, in wrap_socket
ciphers=ciphers)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ssl.py", line 509, in __init__
self._context.load_cert_chain(certfile, keyfile)
IOError: [Errno 19] Operation not supported by device
Edit :
After running this comment in my terminal
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem I was getting an entrust issue as follows
depth=1 /C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
verify error:num=20:unable to get local issuer certificate
So I downloaded the entrust_2048_ca.cer file and kept in the same folder then from terminal I am running as
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -CAfile entrust_2048_ca.cer -cert PushChatCert.pem -key PushChatKey.pem
Then that problem has been solved but how to execute that in PyAPNs?
Problem has been solved. It was some SSL security issue for that python was not able to access the files from that file dir.
I was following this tutorial apple-push-notification-services.
As described in this tutorial after creating the aps_development.cer file I was doing these to get the .pem files
$ openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem
Convert the private key’s .p12 file into a .pem file:
$ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12
Enter Import Password:
MAC verified OK
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
After doing all I had PushChatKey.pem and PushChatCert.pem files and using those files I was not able to send push notifications to my device PyAPNs
apns = APNs(use_sandbox=True, cert_file='PushChatKey.pem', key_file='PushChatCert.pem')
How did I solve?
Finally I tried those certificate creation from the beginning but this time following some other tutorial
Create your APNS certificates.
After creating the SSL Certificate that you download as named aps_developer_identity.cer. Double-click on it to install it in the Keychain Access application. The SSL certificate will be used by your provider application so that it can contact the APNs to send push notifications to your applications.
Launch Keychain Assistant from your local Mac and from the 'login' keychain, filter by the 'Certificates' category. You will see an expandable option called “Apple Development iOS Push Services”:
Expand this option then right click on “Apple Development iOS Push Services” -> Export “Apple Development iOS Push Services ...″. Save this as apns-dev-cert.p12 file somewhere you can access it.
Now from apns-dev-cert.p12 you make .pem using these command from your terminal
openssl pkcs12 -in apns-dev-cert.p12 -out apns.crt.pem -clcerts -nokeys
openssl pkcs12 -in apns-dev-cert.p12 -out apns.key.pem -nocerts -nodes
If you want to create single .pem
openssl pkcs12 -in apns-dev-cert.p12 -out apns_development.pem -nodes -clcerts
Now use these apns.crt.pem and apns.key.pem files with PyAPNs and it works like magic.
Create your provision profile carefully.
Thanks.