APNS Provider API and Postman - ios

I'm trying to use Postman to create and test Apple Notifications in my iOS app.
For a reason I don't get, requests I send from Postman to either https://api.development.push.apple.com/3/device/<token-device> or https://api.push.apple.com/3/device/<token-device> fail without getting any HTTP response.
What I've done so far:
1 - created and exported a APNS certificate from my developer.apple.com account (as explained there).
2 - converted this .p12 certificate with a recent version of openssl (1.0.2l) to a .pem cert and key like this:
openssl pkcs12 -in apns.p12 -out apns.crt.pem -clcerts -nokeys
openssl pkcs12 -in apns.p12 -out apns.key.pem -nocerts -nodes
3 - configured Postman (Preferences > certificates) to use these .pem files for both Provider API urls (dev and prod)
4 - setup a POST request to those Provider API endpoint for my device token with a apns-topic header that has my bundle id for value, and a HTTP body like this:
{
"aps" : {
"alert" : {
"title" : "Game Request",
"body" : "Bob wants to play poker"
}
}
}
It fails with "Could not get any response, blablabla".
If I check what happens it the Postman console, I see:
POST
https://api.development.push.apple.com/3/device/12340e529f85a145b567736e7fd84c9d7e42a43a3c343ec378c2ff071011e4
06:18:08.236
Error: Parse Error
Client Certificate:
keyPath:"/Users/thomas/wksp_myproj/apns.key.pem"
pemPath:"/Users/thomas/wksp_myproj/apns.crt.pem"
Request Headers:
cache-control:"no-cache"
Postman-Token:"99615df2-b2aa-4a51-8d15-8ce27f4e8ca9"
Content-Type:"application/json"
apns-topic:"myproj.app"
Also, when I test with openssl as explained there:
openssl s_client -connect api.development.push.apple.com:443 -cert apns.pem -debug -showcerts -CAfile GeoTrust_Global_CA.pem
I get a lot of outputs with at the end:
HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 0aread from 0x7f9445c215c0 [0x7f9446015c03] (5 bytes => 0 (0x0))
read:errno=0
What's going wrong?

OK, forget about Postman, I've ended up using curl with http2 and an updated version of openssl (1.0.2l) and it's working just fine. For those who would like to see an curl command example, it's here.

Related

Ruby - Connect with SSL and authenticate via client certificate - sslv3 alert bad certificate

I'm trying to connect a webcrawler that accesses a certain site via SSL and queries my data on that site. The authentication of this site is via a self-signed Digital Certificate. At the moment I want to access the site, I upload this certificate in .pfx format to my api, convert it to .pem, and when I try to access the site with this certificate, the response comes with status 403 (forbidden ).
However, when I try to access the site through a browser with the certificate in .pfx format I usually get it.
I already tried using Mechanize, and it worked for a while (until a few months ago it worked), but then it started to give the error:
SSL_connect returned = 1 errno = 0 state = SSLv3 read finished A: sslv3 alert bad certificate
The site is old, it does not receive updates frequently.
After that I already tried to use the net / http lib and the error persisted, I tried to use the httprb gem and lastly I tried with Faraday. All attempts ended either in that error quoted above or with the response status == 403.
What can I do to be able to connect? Is there something wrong with my script? Is it missing any information I need to get through?
Code:
# Faraday customs method:
class FaradayHttp
def with_openssl
system "openssl pkcs12 -in my-certificate-path -out certificate-output-path -nodes -password pass:certificate-password"
def cert_object
OpenSSL::X509::Certificate.new File.read("certificate-output-path")
end
# create PKey
def key_object
OpenSSL::PKey.read File.read("certificate-output-path")
end
faraday = Faraday::Connection.new 'https://example-site.com',
:ssl => {
certificate: cert_object,
private_key: key_object,
version: :SSLv3,
verify: false
}
faraday
end
end
# Controller that try to connect with the ssl server:
agent = FaradayHttp.new.with_openssl
page = agent.get '/login_path'
# mypki will prompt you for certificates
require 'mypki'
# faraday will use certificates from mypki
require 'faraday'
faraday = Faraday::Connection.new 'https://example-site.com'
faraday.get '/login_path'

APNS_CERTIFICATE - Push Notification does not send in production

I've had this issue for about 2 weeks, when I suddenly stopped sending notifications in production. I am using the django-push-notifications library and by django admin I can send a test message, but it does not send messages through the system.
On my local computer, everything works flawlessly. I discovered a command to test the certificate:
openssl s_client -connect gateway.push.apple.com:2195 -cert apns-cert.pem
With this one I had the return: Timeout: 7200 (sec) Verify return
code: 20 (unable to get local issuer certificate) Extended master
secret: yes
So with a lot of research, I discovered that I needed to put the path of "CA":
openssl s_client -CApath /etc/ssl/certs/ -connect gateway.push.apple.com:2195 -cert apns-cert.pem
Who was taking me to: Verify return code: 0 (ok)
However, for use in the library, I needed to put the full path of a .pem file. Then I found this command:
ls /etc/ssl/certs/Entrust*
I tested all the .pem files that were there, until I reached what appeared to have worked perfectly:
openssl s_client -CAfile /etc/ssl/certs/Entrust.net_Premium_2048_Secure_Server_CA.pem -connect gateway.push.apple.com:2195 -cert apns-cert.pem
Soon, I formatted my PUSH_NOTIFICATIONS_SETTINGS:
PUSH_NOTIFICATIONS_SETTINGS = {
"GCM_API_KEY": "xxxx",
"APNS_CERTIFICATE": os.path.join(BASE_DIR, "apns-cert.pem"),
"APNS_CA_CERTIFICATES": "/etc/ssl/certs/Entrust.net_Premium_2048_Secure_Server_CA.pem",
"APNS_ERROR_TIMEOUT": 3,
}
IOS_VERIFY_RECEIPT_API = 'https://buy.itunes.apple.com/verifyReceipt'
ANDROID_VERIFY_RECEIPT_API = 'https://www.googleapis.com/androidpublisher/v2/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}'
Unfortunately it still does not send PUSH, and no error because I have configured it to pop errors to send by email.
PS: Remembering that by sending a test text via django admin: OK. Sending via sandbox (debug): OK.
In fact it was not an SSL issue, it was a bulk upload error by the library.
The tokens registered in the system were expired and the library does not know how to work with it and canceled the action, causing no other token to be attempted. I corrected the problem by looping and ignoring the individual error by sending a test to my email:
def send_push(self):
errors = []
# IOS
queryset_ios = APNSDevice.objects.filter(user=self.authentication)
for device in queryset_ios:
try:
device.send_message(self.subject, badge=1, sound=self.kind.sound)
except APNSServerError as e:
errors.append(APNS_ERROR_MESSAGES[e.status])
except Exception:
pass
# ANDROID
queryset_android = GCMDevice.objects.filter(user=self.authentication)
extra = {'notification': self.pk, 'kind': self.kind.kind, 'sound': self.kind.sound}
for device in queryset_android:
try:
queryset_android.send_message(self.subject, badge=1, extra=extra)
except GCMError as e:
errors.append(str(e))
except Exception:
pass
if errors:
send_mail("Push Error",
"Push: %s \n User: %s \n\n Errors: %s" % (self.subject, self.authentication.full_name, errors),
settings.DEFAULT_FROM_EMAIL, ["my#mail.com"])

Handoff and Universal Links failed on iOS 9

I am setting up my apple-app-site-association file follow this tutorial:
Handoff works on iOS 8 but handoff does not work on iOS 9.
I setup a free hosting site and upload the apple-app-site-association file onto the root of website: universallink.net46.net
1. I created a JSON file and named it handoff.json:
{
"activitycontinuation":
{
"apps": ["XXXXXXXXXX.com.home.handoff"]
},
"applinks":
{
"apps":[],
"details":
{
"XXXXXXXXXX.com.home.handoff":
{
"paths":["*"]
}
}
}
}
The XXXXXXXXXX here is the team id of the Distribution provisioning profile
I used Keychain Access app to export a iPhone Distribution certificate to a Certificates.p12 key.
I signed the JSON file with these commands:
Create a certificate in the openssl command.
openssl pkcs12 -in Certificates.p12 -clcerts -nokeys -out output_crt.pem
Create a secret key.
openssl pkcs12 -in Certificates.p12 -nocerts -nodes -out output_key.pem
Create an intermediate certificate.
openssl pkcs12 -in Certificates.p12 -cacerts -nokeys -out sample.ca-bundle
Sign the handoff.json file with the following command.
cat handoff.json | openssl smime -sign -inkey output_key.pem -signer output_crt.pem -certfile sample.ca-bundle -noattr -nodetach -outform DER> apple-app-site-association
I uploaded the signed file "apple-app-site-association" onto the root of website universallink
I configured the entitlement:
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>activitycontinuation:www.universallink.net46.net</string>
<string>activitycontinuation:universallink.net46.net</string>
<string>applinks:www.universallink.net46.net</string>
<string>applinks:universallink.net46.net</string>
</array>
</dict>
I implement the function application:continueActivity.... and return YES.
I installed the app on the iOS 9 beta 4 device and also installed the Certificates.p12 at step 3 into the device.
I sent a message to myself the link of universallink website
I expected my app would be launched but actually, it was Safari.
I don't know if I did something wrong.
You are signing it wrong. You need a
certificate and key for an identity issued by a certificate authority trusted by iOS
See apples official documentation here: https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/Handoff/AdoptingHandoff/AdoptingHandoff.html#//apple_ref/doc/uid/TP40014338-CH2-SW10
I Think This:
"details":
{
"XXXXXXXXXX.com.home.handoff":
{
"paths":["*"]
}
}
Should be this:
"details": [{
"appID": "XXXXXXXXXX.com.home.handoff",
"paths": ["*"]
}]
I was trying to make it work by using a local server (an OTA https python server) with a self created certificate using SSL and it didnĀ“t work. I could track the communications and listen to the channel but in the different tests I tried, the json file was never asked for, so there is the problem, the SSL certificate.
Go to : https://support.apple.com/en-gb/HT205205 as "not all the root certificates" are supported by apple (most of them are though).

Getting an error while Connecting to APNS server

source: www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1
Terminal Command:
Initiums-iMac:Desktop initium$ openssl s_client -connect
gateway.sandbox.push.apple.com:2195
-cert PushChatCert.pem -key PushChatKey.pem
Terminal Response:
CONNECTED(00000003) 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
verify return:0
3122:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake
failure:/SourceCache/OpenSSL098/OpenSSL098-47.2/src/ssl/s3_pkt.c:1106:SSL
alert number 40
3122:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake
failure:/SourceCache/OpenSSL098/OpenSSL098-47.2/src/ssl/s23_lib.c:182:
Following the tutorial www.raywenderlich.com/32960, I think I faced the same issue... almost.
Anyways, I think it was because I had goofed up with the .p12 file specifically.
Did you export the correct private key from inside "Keychain Access > Keys"?
This part comes under "Generating the Certificate Signing Request (CSR)" where you have to export the private key as a p12 file.
Delete all the unnecessary certificates and keys, start from scratch and closely follow the tutorial.

How can I use a PEM certificate with password?

I'm having plenty of trouble trying to use a certificate that has a password on ruby. I'm using rest-client, but that's not a requirement.
This is the cURL-equivalent of the call I need to make:
curl -E certificate.pem:PASSWORD -d ident=language -d data="test" "https://theurl"
I tried many things, but I can't get the password part working. Here's what I have so far:
cert = OpenSSL::X509::Certificate.new(File.read("#{RAILS_ROOT}/certificate.pem"))
reply = RestClient.post("https://theurl", {:ident => 'language', :data => 'test'}, {:ssl_client_cert => cert})
I've tried putting the password everywhere, as :password and :ssl_client_key, I've looked through all documentation I could find but there's nowhere that will accept this password.
This is the error I always get:
SSL_connect returned=1 errno=0 state=SSLv3 read finished A: sslv3 alert handshake failure
What am I missing?
The way you uses curl with option -E, you are specifying a private key with a certificate.
(from the cURL man page)
-E/--cert
(SSL) Tells curl to use the specified
client certificate file when getting a
file with HTTPS, FTPS or another
SSL-based protocol. The certificate
must be in PEM format. If the optional
password isn't specified, it will be
queried for on the terminal. Note that
this option assumes a "certificate"
file that is the private key and the
private certificate concatenated! See
--cert and --key to specify them independently.
So in order to do the samething with RestClient, you can try using the ssl_client_key option. Like:
:ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),

Resources