Generating CSR from SecKey using OpenSSL - ios

I have created a public/private key pair (Elliptic Curve) with SecKeyGeneratePair.
How can I use the SecKey instances to generate a CSR using OpenSSL in Swift?

As far as I can tell Apple's own Security framework does not have an API currently exposed f or generating CSR's. It is technically OpenSSL wrapped; so in case you do have OpenSSL ( I personally prefer LibreSSL, libtls makes life easy ).
As an alternative you can also use Commoncrypto, which my answer won't cover but there are many many examples out there
So lets go through the steps of finding out how to do this. I found that the easiest way to work with OpenSSL is to ignore the documentation completely and go straight to reading source.
To achieve what we need, we need to 'replicate' the following 2 commands:
openssl ecparam -out server.key -name prime256v1 -genkey
openssl req -new -key server.key -out server.csr
Important Note: Be sure to pick a secure curve. Use openssl ecparam -list_curves to get a list of supported curves by your version of openssl. Read more
Step 1 Generating the key
ecparam tells us we have to look in sslsource/apps/openssl/ecparam.c first.
The source gives us 3 steps:
Set up BIO for writing, setting up group parameters and finally generating the key.
Step 2 Generating the CSR
Following the same principles but this time looking in req.c and some code in apps.c which contains some boilerplate code used in req.c
I used this method to create a crude but working proof of concept that runs in swift, you can check it out on github here: CertificateTool
The code will run on any system you can compile the swift4+ toolchain on and some version of openSSL.
Edit:
Generating the EC Secret key:
//
// ECKey.swift
// CertificateToolPackageDescription
//
// Created by Antwan van Houdt on 10/01/2018.
//
import CLibreSSL
public class ECKey {
internal let secretKey: OpaquePointer
private let group: OpaquePointer
public init() {
group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)
EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE)
EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_COMPRESSED)
secretKey = EC_KEY_new()
EC_KEY_set_group(secretKey, group)
EC_KEY_generate_key(secretKey)
}
deinit {
EC_KEY_free(secretKey)
EC_GROUP_free(group)
}
}
Creating the signing request:
//
// CertificateRequest.swift
// CertificateToolPackageDescription
//
// Created by Antwan van Houdt on 10/01/2018.
//
import CLibreSSL
public enum NIDType: String {
case email = "emailAddress"
case hostName = "CN"
case organizationalUnit = "OU"
case organization = "O"
case city = "L"
case state = "ST"
case countryCode = "C"
}
public class CertificateSigningRequest {
private let request: UnsafeMutablePointer<X509_REQ>
private let key: ECKey
private let name: UnsafeMutablePointer<X509_NAME>
public init(key: ECKey, email: String, hostName: String, organizationalUnit: String, organization: String, countryCode: String, state: String, city: String) {
request = X509_REQ_new()
self.key = key
name = X509_NAME_new()
X509_REQ_set_version(request, 2)
self.add(name: email, type: .email)
self.add(name: hostName, type: .hostName)
self.add(name: organizationalUnit, type: .organizationalUnit)
self.add(name: organization, type: .organization)
self.add(name: countryCode, type: .countryCode)
self.add(name: city, type: .city)
self.add(name: state, type: .state)
X509_REQ_set_subject_name(request, name)
self.setPublicKey()
}
deinit {
X509_REQ_free(request)
X509_NAME_free(name)
}
private func add(name: String, type: NIDType) {
var buff = Array(name.utf8)
X509_NAME_add_entry_by_NID(self.name, OBJ_txt2nid(type.rawValue), MBSTRING_UTF8, &buff, Int32(buff.count), 0, 0)
}
private func setPublicKey() {
let certKey = EVP_PKEY_new()
EVP_PKEY_set1_EC_KEY(certKey, key.secretKey)
X509_REQ_set_pubkey(request, certKey)
X509_REQ_sign(request, certKey, EVP_sha256())
EVP_PKEY_free(certKey)
}
}
Note This code does not contain any BIO related functions ( yet ) to write out the PEM data to a file or to a data buffer, but they are really easy to add.
Disclaimer: I am not a professional cryptographer nor do I know all the ins and outs of the OpenSSL API. I cannot guarantee that the code I provided is a 100% correct implementation. Always be wary of code, especially crypto code, downloaded from the web.

Though it's not a straight OpenSSL implementation, I encountered outfoxx's Shield library which makes it super easy to create CSRs via Swift.

Related

Connection to AWS IoT with M2MQTT from .net core

I managed to manually create the AWS IoT config, downloaded the certs and create a console app that could subscribe to a topic. Im now trying to automate the thing creation, which results in the certificate keys being provided by AWS as strings. Im not sure how to use these. I have the root ca downloaded already, which I assume I use for all things.
My file based cert subscriber looks like this:
Console.WriteLine("AWS IOT Dotnet core message listener starting");
string iotendpoint = "blahblah-ats.iot.ap-southeast-2.amazonaws.com";
int BrokerPort = 8883;
string Topic = "topic_1/";
var CaCert = X509Certificate.CreateFromCertFile(#"root-CA.crt");
var ClientCert = new X509Certificate2(#"device.pfx", "password");
var IotClient = new MqttClient(iotendpoint, BrokerPort, true, CaCert, ClientCert, MqttSslProtocols.TLSv1_2);
try
{
IotClient.Connect(Guid.NewGuid().ToString());
Console.WriteLine("Connected to AWS IOT");
IotClient.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
IotClient.MqttMsgSubscribed += Client_MqttMsgSubscribed;
IotClient.Subscribe(new string[] { Topic }, new byte[] { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE });
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
To load the cert from file, I tried this:
var keyText = File.ReadAllText("keys.json");
var keys = JsonConvert.DeserializeObject<Keys>(keyText);
var bytes = Encoding.ASCII.GetBytes(keys.PrivateKey.ToCharArray());
var ClientCert = new X509Certificate2(bytes);
with:
class Keys {
public string PublicKey {get;set;}
public string PrivateKey {get;set;}
}
and the keys from AWS in a json file :
{
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA4mh2PQ581XN9BmoCvDjlaktm/6gQgqGBItZThcQVMTjveU8H\npjOU2E/9lq7vmdO+96NuuMr9MKtFD+ZWtVExLjMq9hH0MvIvosVt9+6Ggcwz7Kdr\nigprfBMVORV0rgcK+nsd2DmBNrs339fqbTn5UAIFFBpqkNReW7LMl9h6g8hu4aYQ\nJTohDwSmgmNJKlzMJGtVfPggqt+bBi3lUf9NEOEz...
-----END RSA PRIVATE KEY-----\n",
"PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4mh2PQ581XN9BmoCvDjl\naktm/6gQgqGBItZThcQVMTjveU8HpjOU2E/9lq7vmdO+96NuuMr9MKtFD+ZWtVEx\nLjMq9hH0MvIvosVt9+6Ggcwz7K...
-----END PUBLIC KEY-----\n"
}
Im getting an error loading the cert:
An unhandled exception of type 'Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException' occurred in System.Security.Cryptography.X509Certificates.dll: 'Cannot find the requested object.'
Can anyone see anything obviously wrong here? I dont understand certificates...
Update:
Using the PEM text produced by the AWS SDK is more correct, but I still get an error connecting - M2MQTT says there is a cert problem, it has no private key. Does it need it?
var pemText = File.ReadAllText("thing.crt");
var bytes = Encoding.ASCII.GetBytes(pemText);
var ClientCert = new X509Certificate2(bytes);
Final hacked together solution looks like this:
var keyText = File.ReadAllText("keys.json"); // saved from AWS SDK when creating IoT Cert.
var keys = JsonConvert.DeserializeObject<Keys>(keyText);
var rsa = RsaHelper.PrivateKeyFromPem(keys.PrivateKey);
var pemText = File.ReadAllText("thing.crt");
var bytes = Encoding.ASCII.GetBytes(pemText);
var ClientCert = new X509Certificate2(bytes);
ClientCert = ClientCert.CopyWithPrivateKey(rsa);
ClientCert = new X509Certificate2(ClientCert.Export(X509ContentType.Pfx,"12345678"), "12345678");
RSAHelper from https://github.com/dejanstojanovic/dotnetcore-token-authentication/blob/asymmetric_rsahelper/Sample.Core.Common/Helpers/RsaHelper.cs
Last trick to Export and Import the PFX from https://github.com/aspnet/KestrelHttpServer/issues/2960 to solve error: "No credentials are available in the security package"
Sidebar - why do we (as an industry) always take something conceptually simple and make it so fricken complicated? :)

iOS - swift - Generating key pair for secp224k1 curve (ECDH)

I have been trying to generate public and private keys for the secp224k1 curve in iOS. We are using ECDH method to do api handshake between mobile and backend. In Java it is done using the below code.
public static KeyPair getECKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp224k1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", "SC");
kpg.initialize(ecSpec);
return kpg.generateKeyPair();
}
Is there a way to generate keys with the specific curve(secp224k1) type in swift? I tried using the apple provided EC algorithm to do the handshake by using the below code.
//Generates public and private key with EC algorithm
public static func getKey() -> [String: SecKey]? {
let attributes: [String: Any] =
[kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: false]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
let err = error!.takeRetainedValue() as Error
print(err.localizedDescription)
return nil
}
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
print("Error occured while creating public key")
return nil
}
return ["publicKey": publicKey, "privateKey": privateKey]
}
When I send the public key generated via above method I get an error from server with a message:
"error":"java.security.InvalidKeyException: ECDH key agreement requires ECPublicKey for doPhase","exception":"InvalidAuthException"
I tried VirgilCrypto for swift which came close to solving the problem. But it doesn't have the specific curve type in the library which I need. It has support for secp256r1 only. Also, answers from the below posts I tried and didn't work out.
Elliptic Curve Diffie Hellman in ios/swift
Any suggestions or help would be great, Thanks.
The Koblitz 224-bit curve is not supported by iOS. One solution might be to use a different curve type or a third-party library with secp224k1 support.
From your comments it can be concluded that the secp224k1 curve type is a requirement.
A third-party library that might be used is Virgil Crypto, which is available through github https://github.com/VirgilSecurity/virgil-crypto. It is a C++ library. (Virgil Security is also offering a Swift wrapper lib called virgil-crypto-x, but that one no longer supports secp224k1 in the current version).
C++ libs can be used in Swift indirectly by creating a Objective-C++ wrapper with a defined interface.
Build VSCCrypto.framework
On the command line enter:
git clone https://github.com/VirgilSecurity/virgil-crypto
cd virgil-crypto
utils/build.sh --target=ios
This builds the framework for iOS.
Add VSCCrypto.framework to Xcode Project
select root node in project navigator
in Xcode create 'New Group'
name it 'Frameworks'
drag & drop in Finder the VSCCrypto.framework in the folder virgil-crypto/build/ios/lib to the 'Frameworks' group
in XCode on the right under 'Embedded Binaries' tap plus
select the VSCCrypto.framework
Objective-C++ Wrapper
create a ECDHCrypto Objective-C file
when asked for 'Would you like to configure an Objective-C bridging header' tap on 'Create Bridging Header'
in the project navigator change the file suffix from .m to .mm (to allow C++ code there)
add #import "ECDHCrypto.h" to the bridging header
ECDHCrypto.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface ECDHCrypto : NSObject
#property(nonatomic, strong) NSString *ownPrivateKey;
#property(nonatomic, strong) NSString *ownPublicKey;
- (void)generateKeyPair;
- (NSString *)shared:(NSString *)otherPublicKey;
#end
NS_ASSUME_NONNULL_END
ECDHCrypto.mm
#import "ECDHCrypto.h"
#import <VSCCrypto/VirgilCrypto.h>
using virgil::crypto::VirgilKeyPair;
using virgil::crypto::VirgilByteArray;
using virgil::crypto::VirgilCipherBase;
using virgil::crypto::str2bytes;
using virgil::crypto::bytes2str;
using virgil::crypto::bytes2hex;
#implementation ECDHCrypto
- (void)generateKeyPair {
VirgilKeyPair keyPair = VirgilKeyPair::generate(VirgilKeyPair::Type::EC_SECP224K1);
VirgilByteArray ownPublicKeyBates = keyPair.publicKey();
self.ownPublicKey = [NSString stringWithCString:bytes2str(ownPublicKeyBates).c_str()
encoding:[NSString defaultCStringEncoding]];
VirgilByteArray ownPrivateKeyBytes = keyPair.privateKey();
self.ownPrivateKey = [NSString stringWithCString:bytes2str(ownPrivateKeyBytes).c_str()
encoding:[NSString defaultCStringEncoding]];
}
- (NSString *)shared:(NSString *)otherPublicKey {
NSAssert(self.ownPrivateKey, #"private key must be set, e.g. use generateKeyPair");
std::string otherPKString([otherPublicKey cStringUsingEncoding:NSASCIIStringEncoding]);
VirgilByteArray pubKey = str2bytes(otherPKString);
std::string ownPrivateKeyString([self.ownPrivateKey cStringUsingEncoding:NSASCIIStringEncoding]);
VirgilByteArray ownPrivateKeyBytes = str2bytes(ownPrivateKeyString);
VirgilByteArray shared_ba = VirgilCipherBase::computeShared(pubKey, ownPrivateKeyBytes);
std::string hex = bytes2hex(shared_ba);
NSString *shared = [NSString stringWithCString:hex.c_str()
encoding:[NSString defaultCStringEncoding]];
return shared;
}
#end
Usage in Swift
let otherPK = """
-----BEGIN PUBLIC KEY-----
ME4wEAYHKoZIzj0CAQYFK4EEACADOgAEgeW/foqxCDOd1y6lnXONkRThS6xhjLHP
SEXs7jHSpoaPQH4vArcGmIb1cAZcepEh7WDQxCyfQXg=
-----END PUBLIC KEY-----
"""
let ecdhCrypto = ECDHCrypto()
ecdhCrypto.generateKeyPair();
print("ecdhCrypto.ownPublicKey: \n" + ecdhCrypto.ownPublicKey);
print("shared secret: " + ecdhCrypto.shared(otherPK));
Test With Java Counterpart
To test whether a key exchange is successful, one can perform the following test:
In Java, a secp224k1 key pair is generated and the public key is output to the console.
The public key is copied into the Swift code of an iOS application using Copy/Paste. The app then generates a key pair and writes its own public key to the console, as well as the calculated shared secret. The iOS public key is then inserted into the Java program as an input (displayed in green).
Finally, one can compare the shared secret of the iOS app and the Java program. Here it is the same, so the key exchange was successful.
In the upper area you see Xcode with the iOS source code and in the lower area the output of the Java program:

"Export" x509 certificate in Ruby

I'm communicating with an API that has the following directions:
Install the issued x509 certificate onto the client server.
Export the x509 certificate using the supplied password and default Machine Key Set into memory.
Base64 encode the exported bytes of the x509 certificate.
Add ‘X509Certificate’ as an HTTP header and set its value to the result of step 3.
Step 1 and 4 are easy, but I have no idea on 2 or or the 'export' portion of 3. I have tried Googling for some time and I'm not sure exactly where to even really start.
Would someone point me in the right direction on how to "export" a certificate with "machine key set"?
Here is what I have so far
raw_data = File.read('cert.pfx')
pkcs = OpenSSL::PKCS12.new(raw_data, 'password')
cert = OpenSSL::X509::Certificate.new(pkcs.certificate.to_pem)
Here is equivalent .NET code:
public string GetBase64Cert(string certificateThumbprint)
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var foundCertificates = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, false);
if (foundCertificates.Count != 1)
{
return null;
}
var certByteArray = foundCertificates[0].Export(X509ContentType.Cert);
store.Close();
return Convert.ToBase64String(certByteArray);
}
}
And equivalent PHP code:
public function setx509($x509file) {
$cert = openssl_x509_parse($x509file);
$base64cert = base64_encode($cert);
return $base64cert;
}
Try
pkcs = OpenSSL::PKCS12.new(File.read('cert.pfx'), 'password')
str = Base64.urlsafe_encode64(pkcs.certificate.to_der)
Probably also str.gsub(/=+$/, '') to cut off padding

How to authenticate Game Center User from 3rd party node.js server

I've been trying to get the new iOS Game Center GKPlayer method, generateIdentityVerificationSignatureWithCompletionHandler, working so we can securely rely on the Game Center credentials for authentication. We're using Node.js as the backend server, and I've been trying to verify the signature but to no avail.
Here is the code on the server side that I have - if there's anyone who can chime in on what's missing, that'd be appreciated. The question has been answered somewhat here: How to authenticate the GKLocalPlayer on my 'third party server'?, but Node.js hasn't specifically been tackled. Note that the code below doesn't ensures the validity of the certificate with a signing authority (yet).
//Client sends the payload below
//json.playerId - UTF-8 string
//json.bundleId - UTF-8 string
//json.timestamp - Hex string
//json.salt - base64 encoded
//json.publicKeyURL - UTF-8 string
//json.signature - base64 encoded
var json = JSON.parse(req.body);
console.log(JSON.stringify(json));
//get the certificate
getCertificate(json.publicKeyURL, function(cert){
//read file from fs for now, since getCertificate returns cert in DER format
fs = require('fs');
fs.readFile('/gc-sb.pem', 'utf8', function (err,data) {
if (err) {
console.log(err);
} else {
console.log(data);
var verifier = crypto.createVerify("sha1WithRSAEncryption");
verifier.write(json.playerId, "utf8");
verifier.write(json.bundleId, "utf8");
verifier.write(json.hexTimestamp, "hex");
verifier.write(json.salt, "base64");
var isValid = verifier.verify(data, json.signature, "base64");
console.log("isvalid: " + isValid);
}
});
});
One thing I've found using the crypto module in node.js is that it seems to want the certificate in PEM format, and I believe the format retrieved from Apple is DER. Until I figure out how to convert the DER file to PEM, I've temporarily converted it using
openssl x509 -in gc-sb.cer -inform der -outform pem -out gc-sb.pem
The main thing for me is being able to validate the signature first. Conversion of the certificate and verifying it against a signing authority will come later :)
EDIT: I've figured it out - I was hashing the playerId, bundleId, timestamp and salt, and then using the hashed value as information to verify. I needed to just put those pieces of information into the verifier to verify without the SHA-1 hash (since the verifier will be taking care of it). I've modified the code above to "make it work". Hope this helps anyone that comes across this.
Here is how you can validate gamecenter identity using nodejs. It convert also the der certificate format to pem on the fly.
var crypto = require('crypto');
var request = require('request');
var ref = require('ref');
var token = require('./test.json');
request({url: token.publicKeyURL, encoding: null}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var verifier = crypto.createVerify("sha1");
verifier.update(token.playerId, "utf8");
verifier.update(token.bundleId, "utf8");
var buf = ref.alloc('uint64');
ref.writeUInt64BE(buf, 0, token.timestamp.toString());
verifier.update(buf);
verifier.update(token.salt, 'base64');
var pmd = '-----BEGIN CERTIFICATE-----';
var base64 = body.toString('base64');
var size = base64.length;
for (var i = 0; i < size; i = i + 64) {
var end = i + 64 < size ? i + 64 : size;
pmd = pmd + '\n' + base64.substring(i, end);
}
pmd = pmd + '\n-----END CERTIFICATE-----';
var valid = verifier.verify(pmd, token.signature, "base64");
console.log(valid);
}
});
It's seems there's a npm package for it.
https://github.com/maeltm/node-gamecenter-identity-verifier

Apple Push Notifications Using Moon-APNS or APNS-Sharp

I am having really hard time figuring out how to send messages from my server to the APNS. I have used Moon-APNS and APNS-Sharp and I am stuck at the same error which is "parameter is incorrect". I generated the p12 file using KeyChain. I dragged the file into my Win 7 virtual environment and put it inside the bin\debug folder. Here is the code for Moon-APNS:
static void Main(string[] args)
{
var deviceToken = "21212d6fefebde4d317cab41afff65631b5a4d47e5d85da305ec610b4013e616";
var payload = new NotificationPayload(deviceToken, "hello world");
var notificationList = new List<NotificationPayload>() { payload };
var push = new PushNotification(true, "PushNotificationTest.p12", "pushchat");
var result = push.SendToApple(notificationList);
Console.WriteLine("Hello World");
}
Anyone has ideas?
I think this will help you :
OSX Keychain
After you've created the appropriate Push Notification Certificate in iPhone Developer Program Portal you should have downloaded a file named something like apn_developer_identity.cer. If you have not done so already, you should open/import this file into Keychain, into your login section.
Finally, if you filter Keychain to show your login container's Certificates, you should see your Certificate listed. Expand the certificate, and there should be a Key underneath/attached to it.
Right Click or Ctrl+Click on the appropriate Certificate and choose Export. Keychain will ask you to choose a password to export to. Pick one and remember it. You should end up with a .p12 file. You will need this file and the password you picked to use the Notification and Feedback Libraries here.
OpenSSL
Here is how to create a PKCS12 format file using open ssl, you will need your developer private key (which can be exported from the keychain) and the CertificateSigningRequest??.certSigningRequest
1. Convert apn_developer_identity.cer (der format) to pem:
openssl x509 -in apn_developer_identity.cer -inform DER -out apn_developer_identity.pem -outform PEM}
2. Next, Convert p12 private key to pem (requires the input of a minimum 4 char password):
openssl pkcs12 -nocerts -out private_dev_key.pem -in private_dev_key.p12
3. (Optional): If you want to remove password from the private key:
openssl rsa -out private_key_noenc.pem -in private_key.pem
4. Take the certificate and the key (with or without password) and create a PKCS#12 format file:
openssl pkcs12 -export -in apn_developer_identity.pem -inkey private_key_noenc.pem -certfile CertificateSigningRequest??.certSigningRequest -name "apn_developer_identity" -out apn_developer_identity.p12
I found Moon-APNS easier to use and configure on my app.
Solved the problem by following the link:
http://code.google.com/p/apns-sharp/wiki/HowToCreatePKCS12Certificate
and then generating the .p12 file using the openssl approach.
Have you tried using APNS-Sharp example project to send notifications? I am using similar code, it works just fine...this is what one of my methods looks like
public void SendToSome(List<string> tokens)
{
//Variables you may need to edit:
//---------------------------------
bool sandbox = true;
//Put your device token in here
//Put your PKCS12 .p12 or .pfx filename here.
// Assumes it is in the same directory as your app
string p12File = "Certificates.p12";
//This is the password that you protected your p12File
// If you did not use a password, set it as null or an empty string
string p12FilePassword = "";
//Number of milliseconds to wait in between sending notifications in the loop
// This is just to demonstrate that the APNS connection stays alive between messages
// int sleepBetweenNotifications = 15000;
//Actual Code starts below:
//--------------------------------
string p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p12File);
NotificationService service = new NotificationService(sandbox, p12Filename, p12FilePassword, 1);
service.SendRetries = 5; //5 retries before generating notificationfailed event
service.ReconnectDelay = 5000; //5 seconds
service.Error += new NotificationService.OnError(service_Error);
service.NotificationTooLong += new NotificationService.OnNotificationTooLong(service_NotificationTooLong);
service.BadDeviceToken += new NotificationService.OnBadDeviceToken(service_BadDeviceToken);
service.NotificationFailed += new NotificationService.OnNotificationFailed(service_NotificationFailed);
service.NotificationSuccess += new NotificationService.OnNotificationSuccess(service_NotificationSuccess);
service.Connecting += new NotificationService.OnConnecting(service_Connecting);
service.Connected += new NotificationService.OnConnected(service_Connected);
service.Disconnected += new NotificationService.OnDisconnected(service_Disconnected);
//The notifications will be sent like this:
// Testing: 1...
// Testing: 2...
// Testing: 3...
// etc...
for (int i = 0; i < tokens.Count; i++)
{
//Create a new notification to send
Notification alertNotification = new Notification();
alertNotification.DeviceToken = tokens[i];
alertNotification.Payload.Alert.Body = Text;
alertNotification.Payload.Sound = "default";
alertNotification.Payload.Badge = 1;
//Queue the notification to be sent
if (service.QueueNotification(alertNotification))
Console.WriteLine("Notification Queued!");
else
Console.WriteLine("Notification Failed to be Queued!");
//Sleep in between each message
if (i < tokens.Count)
{
// Console.WriteLine("Sleeping " + sleepBetweenNotifications + " milliseconds before next Notification...");
// System.Threading.Thread.Sleep(sleepBetweenNotifications);
}
}
Console.WriteLine("Cleaning Up...");
//First, close the service.
//This ensures any queued notifications get sent befor the connections are closed
service.Close();
//Clean up
service.Dispose();
}

Resources