certificate at provider for push notification in iOS - ios

I need to implement iOS push notification in my app. I ve been trying this for a while and have done the iOS side code changes required. I'm able to get the device token from APNS. But im stuck with the provider side implementation.
I'm using a java provider and using JAVAPNS library to implement the provider side logic. What puzzles me is the certificate installation and stuff required at the provider.
My provider runs on unix/windows machine and I am not sure how to proceed with the SSL certificate installation here.
I have obtained SSL certificate for push notification from the apple developer site. But how do I proceed with the p12 file?
I do not find many explanations on this on the net though there are a great deal of discussions and tutorial about the iOS side implementation of push notification.

The SSL certificate is used to establish the socket connection to gateway.push.apple.com from your server side code. Here is some Ruby code as an example:
APN_SSL_KEY_FILE = 'lib/SSLCert_Private_Key.pem'
APN_SSL_HOST = 'gateway.push.apple.com'
APN_SSL_PORT = 2195
APN_SSL_PASSWORD = '<password>'
def configure_apn_cert
##apn_cert = File.read(File.join(RAILS_ROOT, APN_SSL_KEY_FILE))
##apn_context = OpenSSL::SSL::SSLContext.new
##apn_context.key = OpenSSL::PKey::RSA.new(##apn_cert, APN_SSL_PASSWORD)
##apn_context.cert = OpenSSL::X509::Certificate.new(##apn_cert)
end
def create_and_configure_apn_server
configure_apn_cert if not ##apn_cert
puts "APN Service: Configuring APN SOCKET and SSL connection"
#apn_socket = TCPSocket.new(APN_SSL_HOST, APN_SSL_PORT)
#apn_ssl = OpenSSL::SSL::SSLSocket.new(#apn_socket, ##apn_context)
#apn_ssl.sync = true
#apn_ssl.connect
#apn_is_active = false; # reopen the TCP/SSL sockets for now
end
You get a .pem file with:
$ openssl pkcs12 -in myfile.p12 -out myfile.pem

If you're using JavaPNS, it's as simple as :
import javapns.Push;
public class PushTest {
public static void main(String[] args) {
Push.alert("Hello World!", "keystore.p12", "keystore_password", false, "Your token");
}
}

Related

Which npm module should be used to send push notification to Apple(iphone) in node js and why and how can we achieve this?

I want to send push-notifications to iphone by using node.js server-side language
I found npm two modules for doing that
https://www.npmjs.com/package/apns
var apns = require("apns"), options, connection, notification;
options = {
keyFile : "conf/key.pem",
certFile : "conf/cert.pem",
debug : true
};
connection = new apns.Connection(options);
I have only single pem file but they are asking about two pem files key.pem and cert.pem
https://www.npmjs.com/package/apn
var options = {
token: {
key: "path/to/key.p8",
keyId: "T0K3NK3Y1D",
teamId: "T34M1D"
},
production: false
};
var apnProvider = new apn.Provider(options);
In this, they are asking about key, keyId. teamId. But how can I get this ?
Which npm module should I used for sending Apple push notifications ?
I have pem file and bundle id of my app. Are they sufficient to send push notifications to Apple user or not ?
Also can't figure out which npm module is best suited for sending the same ?
Any suggestions are always welcome regarding this.
Thanks for the help
You have too many subquestions in that question which point to the same question of which npm module to use for sending iOS notifications from node server.
I used APN module from here and it works seamlessly-
https://www.npmjs.com/package/apn
Apple guide on how APN works-
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1
Details on how to create key and certificate files-
https://quickblox.com/developers/How_to_create_APNS_certificates
Hope that helps!

Sandbox apple pay testing handshake failure

I am having trouble validating the merchant in my apple pay sandbox environment. Taken from https://developer.apple.com/reference/applepayjs/applepaysession#2166532, once my server then calls the Start Session endpoint at the provided URL, I get a 500 error.
I've dug around and this 500 error is happening somewhere in the network layer. As listed on the apple page (https://developer.apple.com/reference/applepayjs/), I need the following requirements met:
All pages that include Apple Pay must be served over HTTPS. done, server has ssl/https sitewide
To enable merchant validation, your server must allow access over HTTPS (TCP over port 443) to the Apple Pay IP addresses provided in Listing 1 below. done, server is open to all ips on port 443
Your server must support the Transport Layer Security (TLS) 1.2 protocol and one of the cipher suites listed in Table 1. server does support tls 1.2, since I send requests on tls 1.2 to apple pay's development server (below)
I've been using Wireshark to check what's going on, and I seem to be failing once the server is in the ChangeCipherSpec phase, after the server sends back the cipher spec to the client. (Reference for ssl procedure: https://support.f5.com/csp/article/K15292). As you can see from my image, I'm communicating to the apple pay sandbox server, passing in the same supported tls protocol and cipher suite that the error would suggest -> Handshake Failure (40), so something else is going on and I don't know where to look
If you look at the ServerHello message, you can see the server found and accepted the cipher suite that matches the client, which also matches one of the required ciphers that apple pay supports
I can add other details as necessary
The issue was that our server did not have TLS 1.2 enabled by default. Enabling TLS 1.2 and disabling TLS 1.0 fixed the issue - Win 2008
edit
There are a few things that needed to happen. Our server was on .net 4.5, which does not use tls 1.2 by default (apple requires tls 1.2 to be used). So, we upgraded our solution to .net 4.6, and also forced tls 1.2 for our request. Additionally, we have to include the merchant id certificate in our request to apple (which isn't mentioned in the docs very well).
You can find the github repo of the source I used here (https://github.com/justeat/ApplePayJSSample), but here is my code that I needed to put in my solution to make things work (I also had to export my merchant certificate from my mac's keychain that gave me a .p12 file. I imported this .p12 file into my server's computer certificate store)
[System.Web.Http.HttpPost]
public async Task<ContentResult> GetApplePaySession([FromBody] string url)
{
// http://stackoverflow.com/a/36912392/1837080
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// Load the merchant certificate for two-way TLS authentication with the Apple Pay server.
var certificate = LoadMerchantCertificate();
// Get the merchant identifier from the certificate to send in the validation payload.
var merchantIdentifier = GetMerchantIdentifier(certificate);
// Create the JSON payload to POST to the Apple Pay merchant validation URL.
var payload = new ApplePayRequest()
{
merchantIdentifier = merchantIdentifier,
domainName = System.Web.HttpContext.Current.Request.Url.Host,
displayName = "[display name from apple developer portal]"
};
JObject merchantSession;
// Create an HTTP client with the merchant certificate
// for two-way TLS authentication over HTTPS.
using (var httpClient = CreateHttpClient(certificate))
{
var jsonPayload = JsonConvert.SerializeObject(payload);
using (var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"))
{
// POST the data to create a valid Apple Pay merchant session.
using (var response = await httpClient.PostAsync(url, content))
{
response.EnsureSuccessStatusCode();
// Read the opaque merchant session JSON from the response body.
var merchantSessionJson = await response.Content.ReadAsStringAsync();
merchantSession = JObject.Parse(merchantSessionJson);
}
}
}
// Return the merchant session as JSON.
return Content(merchantSession.ToString(), "application/json");
}
#region Apple Pay helper methods
private X509Certificate2 LoadMerchantCertificate()
{
X509Certificate2 certificate;
// Load the certificate from the current user's certificate store. This
// is useful if you do not want to publish the merchant certificate with
// your application, but it is also required to be able to use an X.509
// certificate with a private key if the user profile is not available,
// such as when using IIS hosting in an environment such as Microsoft Azure.
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
// when using thumbprint from mmc, look at:
// http://stackoverflow.com/a/14852713
// there is a hidden character that you must delete
var certificates = store.Certificates.Find(
X509FindType.FindByThumbprint,
"[thumbprint]",
validOnly: false);
if (certificates.Count < 1)
{
throw new InvalidOperationException(
// ReSharper disable once UseStringInterpolation
string.Format(
"Could not find Apple Pay merchant certificate with thumbprint '{0}' from store '{1}' in location '{2}'.",
"‎[thumpprint]", store.Name, store.Location));
}
certificate = certificates[0];
}
return certificate;
}
private string GetMerchantIdentifier(X509Certificate2 certificate)
{
// This OID returns the ASN.1 encoded merchant identifier
var extension = certificate.Extensions["1.2.840.113635.100.6.32"];
// Convert the raw ASN.1 data to a string containing the ID
return extension == null ? string.Empty : Encoding.ASCII.GetString(extension.RawData).Substring(2);
}
private HttpClient CreateHttpClient(X509Certificate2 certificate)
{
var handler = new WebRequestHandler();
handler.ClientCertificates.Add(certificate);
return new HttpClient(handler, disposeHandler: true);
}
#endregion
I just recently went through this. For me I had to merge the PEM and KEY files into a PFX. Then I was able to run the start session call from ubuntu 16.04 using .net core 2.1
private HttpClient CreateHttpClient()
{
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(new X509Certificate2(#"/path/yourcombinedpfx.pfx"));
return new HttpClient(handler); ;
}

Cannot send push notifications from AWS SNS to my iPad

I'm struggle since 2 days to make push notifications from amazon sns to work on my ipad. After nights spending on google and youtube i'm desperated and the amazon documentation isn't really helpful.
What i already did until now :
I got the certificate from apple and successfully uploaded the .p12 file to SNS and created the application platform and set the endpoint.
I set my cognito credentials in the viewcontroller in xcode and add some notification listeners in the appdelegate.
So When i run the app on the iPad, this one successfully display the deviceToken in the xcode promp. So i can enter it in SNS console and configure it.
So i created all required stuffs (certificate, .p12, registering the devicetoken etc.)
Unfortunately, when i'm trying to publish to the endpoint (send a notification to my device), i never received any notifications. I don't really know what's going bad. Yet, when i'm trying to do the same by sending a push to an email, its works perfectly.
I watched tons of tutorial in youtube and followed several docs on the web but it seems to definitely not working.
In Xcode i didn't forget to enter the same bundle ID than in my apple developer account (com.XXX.XXX), so i think my app is well configured.
Amazon docs are not dev friendly, most tutorial on the web are outdated,
Is someone know the perfect procedure to send push notifications from Amazon SNS to an Ipad ?
Thank you.
Last night I have successfully integrated AWS SNS.
Yes, the document on AWS are not well documented.
To integrate SNS you need to configure APNS perfectly. I would like to suggest here to first configure all the necessary credentials to get a notification using APNS.
I am expecting here that the developer knows how to create App ID, .p12, .pem and provisioning profile for APNs. Once you successfully get the notification on your device using APNs then you are ready for SNS in quick time. To test the APNs notification you can use this server "www.pushtry.com".
Once everything is successfully done with APNS then jump into your AWS account >> navigate SNS dashboard >> Create platform application.
After hitting Create Application a form will open to mention your application data and some credential entry of certificate.
Please see the below image to follow the steps.
Password entry is optional as per your .p12 certificate.
On press "Load credential from file", Certificate and Private Key entry should fill automatically.
After hitting "Create Platform Application" navigate into application left pane to see the list of created application.
Now you need to create end point.
see the attached image.
Now fill the End point entry, follow the image below.
Now copy the Endpoint ARN to use into AWS console to push the message on your device. The Endpoint is actually an address of your device like token in APNS for AWS. Be sure you copied endpoint completely.
Now again jump into the "SNS Dashboard" to publish the message.
Now paste your copied "Endpoint ARN" and your text message in this form and hit "Publish Message" as per below image.
Here you are done check your device to get the notification.
=================================================================
Above I have explained to push the notification through AWS SNS console.
Now let me add here code to create Application and Endpoint ARN.
+ (void) initAWSObjectToAccessAccount
{
// Initialize the Amazon Cognito credentials provider.
AWSStaticCredentialsProvider* credentialsProvider = [AWSStaticCredentialsProvider
credentialsWithAccessKey:kAWSAccessKey
secretKey:kAWSSecretKey];
AWSServiceConfiguration* configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSWest2
credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
}
+ (void) createApplicationAndGetAppARN: (void (^) (BOOL isCreated, NSString* appARN))completion
{
[self initAWSObjectToAccessAccount];
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
[myDictionary setObject:kCertificateSecretKey forKey:#"PlatformPrincipal"];
[myDictionary setObject:kCertificatePrivateKey forKey:#"PlatformCredential"];
/* End Amazon SNS Mobile Push self registration */
AWSSNSCreatePlatformApplicationInput* platFormAppInput = [AWSSNSCreatePlatformApplicationInput new];
platFormAppInput.name = #"nFormIOS";
platFormAppInput.platform = #"APNS"; // When Release
//platFormAppInput.platform = #"APNS_SANDBOX"; // When Development phase
platFormAppInput.attributes = myDictionary;
AWSSNS *snsManager = [AWSSNS defaultSNS];
[[[snsManager createPlatformApplication:platFormAppInput] continueWithBlock:^id(BFTask *task) {
if (task.error)
{
completion (NO, nil);
}
else
{
AWSSNSCreatePlatformApplicationResponse* appResponse = task.result;
NSString* applicationARN = appResponse.platformApplicationArn;
completion (YES, applicationARN);
}
return nil;
}] waitUntilFinished];
}
+ (void) createSNSEndPoint:(NSString*)applicationARN
completion:(void (^)(BOOL isSuccess, NSString* endpointARN))completion
{
/* This is the code to actually register the device with Amazon SNS Mobile Push based on the token received. */
AWSSNSCreatePlatformEndpointInput* platformEndpointRequest = [AWSSNSCreatePlatformEndpointInput new];
platformEndpointRequest.customUserData = kUniqueID; // It could be anything.
platformEndpointRequest.token = kDeviceTokenNo; // Device Token No for APNS
platformEndpointRequest.platformApplicationArn = applicationARN;
AWSSNS* snsManager = [AWSSNS defaultSNS];
[[[snsManager createPlatformEndpoint:platformEndpointRequest] continueWithBlock:^id(BFTask *task) {
if (task.error)
{
completion (NO, nil);
}
else
{
AWSSNSCreateEndpointResponse* endPointRes = task.result;
completion (YES, endPointRes.endpointArn);
}
return nil;
}] waitUntilFinished];
}
===========================
kAWSAccessKey
kAWSSecretKey
This you will get when you will create a credential in AWS account.
kCertificateSecretKey
kCertificatePrivateKey
To get these two follow the steps.
/* STEP 1: Retrieve the SSL certificate (.cer file)
STEP 2: use the following command to convert it into .pem
openssl x509 -in aps.cer -inform DER -out myapnsappcert.pem
STEP 3: Open the file in NotePad. Copy the portion bounded as follows:
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
STEP 4: Use it as NSString replacing the special characters (newlines) with escape sequences (\n). It would now resemble this:
-----BEGIN CERTIFICATE-----\nMIIGKTCCBRGgAwIBAgIILZH6mrSmUj8wDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV\n......\n----END CERTIFICATE-----
STEP 5: Save the .cer file as .p12
STEP 6: Use the following command to get the Private Key:
openssl pkcs12 -in CertificatesSNS.p12 -out myapnsappp
rivatekey.pem -nodes -clcerts
STEP 7: Open the file in NotePad. Copy the portion bounded as follows:
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
STEP 8: Repeat Step 4 for this too.
STEP 9: Use the Value retrieved in step4 as "PlatformPrincipal"
STEP 10: Use the Value retrieved in step5 as "PlatformCredential" */

How to implement server-side sever name indication using Security.framework under OS X/iOS

I am trying to implement server name indication on the server side of a OS X application: The server should pick a certificate based on the peer host name provided by the client.
Does anybody know whether this can be achieved using the Security.framework?
SSLSetCertificate takes only one leaf certificate and I cannot find any callback for providing a certificate based on a host name.
In openSSL for example, there is the SSL_CTX_set_tlsext_servername_callback for this purpose.
Any help is greatly appreciated.
There is a new feature in OS X 10.11 (El Capitan) which makes this possible. Sadly, there is currently zero documentation on this feature, but I nevertheless found out how it works:
You have to enable the new option kSSLSessionOptionBreakOnClientHello on your SSL context with:
SSLSetSessionOption(context, kSSLSessionOptionBreakOnClientHello, YES);
This causes the handshake to break after is has received the hostname from the client and it returns the status errSSLClientHelloReceived. You can then get the hostname, use it to look up the appropriate certificate and apply the certificate to the context. Then you can continue the handshake.
OSStatus status = SSLHandshake(context);
// ...
if(status == errSSLClientHelloReceived)
{
size_t hostnameLength;
SSLGetPeerDomainNameLength(context, &hostnameLength);
char hostname[hostnameLength];
SSLGetPeerDomainName(context, hostname, &hostnameLength);
SecIdentityRef cert = ... ; // Look up certificate using hostname
SSLSetCertificate(context, (__bridge CFArrayRef)#[(__bridge id) cert]);
// Repeat from start by calling SSLHandshake
}

APN Production certificate not being recognized by PushSharp

I've developed an iOS app, that receives Push Notifications. I'm sending them from a .NET environment using PushSharp. Everything went beautifully while developing, and the Pushs were successfully sent:
var push = new PushBroker();
var appleCert = File.ReadAllBytes(#"Utils\Cert.Development.p12");
push.RegisterAppleService(new ApplePushChannelSettings(false, appleCert, "*******"));
push.QueueNotification(new AppleNotification()
.ForDeviceToken(token)
.WithContentAvailable(1)
);
push.StopAllServices();
Now, the app has been approved, and it's at AppStore. I have generate the correct production certificate:
var push = new PushBroker();
var appleCert = File.ReadAllBytes(#"Utils\Cert.Production.p12");
push.RegisterAppleService(new ApplePushChannelSettings(true, appleCert, "*******"));
push.QueueNotification(new AppleNotification()
.ForDeviceToken(token)
.WithContentAvailable(1)
);
push.StopAllServices();
but when I try to send the push from PushSharp, it throws the following exception:
You have selected the Production server, yet your Certificate does not appear to be the Production certificate! Please check to ensure you have the correct certificate!
I'm pretty sure I've followed all the steps. I've downloaded the production certificate that was binded with the provisioning file set in the app to publish. Opened it and exported the .p12.
I'm also sure I'm not using a development one by accident, because, if I set PushSharp for development, using this last certificate, it throws the following error:
You have selected the Development/Sandbox (Not production) server, yet your Certificate does not appear to be the Development/Sandbox (Not production) certificate! Please check to ensure you have the correct certificate!
How can the certificate be neither Development, nor Production?
Is there somewhere I can validate the file? Please give me some insigth on this matter, as I have no clue where to start
Apple has changed the name. Please go to ApplePushChannelSettings.cs and change the name as below.
From
if (production && !subjectName.Contains("Apple Production IOS Push Services"))
To
if (production && !subjectName.Contains("Apple Push Services"))
I need to do this when I renewing my expired cert yesterday. Change the name, rebuild it and upload to server, then it's working again.
Apple has introduced a new universal push certificate that enables connection to both the APNs Production and Development environments.That's why the production certificate common name has been changed from Apple Production IOS Push Services to Apple Push Services.
You should change the code on the provider push server to be compatible with the new common name.
When you create production certificate (.p12) for .NET, Always export like selecting the certificate only. see the attached image
http://davidbits.blogspot.in/2016/02/error-you-have-selected-production.html
Issue was in PushSharp Library just update it to Version .3 this is becuase apple has changed Push Certificate Name From (Apple Production Push Service) to (Apple Push Service)
and pushSharp check the name of Certificate :
(production && !subjectName.Contains("Apple Push Services")).
Error: You have selected the Production server, yet your Certificate
does not appear to be the Production certificate! Please check to
ensure you have the correct certificate!
Solution: (production && !subjectName.Contains("Apple Push Services"))
Invoke Following snippet Send Device Token & Message
public void PendingNotification(string DeviceToken,string message)
{
try
{
int port = 2195;
//Developer
String hostname = "gateway.sandbox.push.apple.com";
//Production
//String hostname = "gateway.push.apple.com";
String certificatePassword = "XXXXXX";
string certificatePath = Server.MapPath("~/Cert.p12");
TcpClient client = new TcpClient(hostname, port);
X509Certificate2 clientCertificate = new X509Certificate2(System.IO.File.ReadAllBytes(certificatePath), certificatePassword);
X509Certificate2Collection certificatesCollection = new X509Certificate2Collection(clientCertificate);
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
sslStream.AuthenticateAsClient(hostname, certificatesCollection, SslProtocols.Tls, false);
//String DeviceToken = "a5062b62aacbe6a499e02351c3f233ce87004574ff01965dff5f6bb8f15cae13";
String LoginName = "Name";
int Counter = 1; //Badge Count;
String Message = message;
String UID = "your choice UID";
string payload = "{\"aps\":{\"alert\":\"" + Message + "\",\"badge\":" + Counter + ",\"sound\":\"default\"},\"UID\":\"" + UID + "\",\"LoginName\":\"" + LoginName + "\"}";
MemoryStream memoryStream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(memoryStream);
writer.Write((byte)0);
writer.Write((byte)0);
writer.Write((byte)32);
writer.Write(HexStringToByteArray(DeviceToken.ToUpper()));
writer.Write((byte)0);
writer.Write((byte)payload.Length);
byte[] b1 = System.Text.Encoding.UTF8.GetBytes(payload);
writer.Write(b1);
writer.Flush();
byte[] array = memoryStream.ToArray();
sslStream.Write(array);
}
catch (Exception ex)
{
//Response.Write(ex.Message);
}
}
public static byte[] HexStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
Follow the steps in the following link to generate Production SSL Certificate:
How To Create APNS Certificate
If that didn't work,double check on your code,make sure you're reading from the correct certificate file, and you're passing True to ApplePushChannelSettings
I'd recommend using something from the 3.0 nuget preview releases. This issue is fixed in these releases and will not be backported to 2.x.
PushSharp 2.x > Push.Apple > ApplePushChannelSettings.cs
On ApplePushChannelSettings.cs find DetectProduction() and CheckProductionCertificateMatching() and replace '.Contains("Apple Production IOS Push Services")' by '.Contains("Apple Push Services")'
It occurs because apple changed the certificate name from "Apple Production IOS Push Services" to "Apple Push Services", the PushSharp identify the type(production/development) by the certificate name.

Resources