2

We are working on a MDM solution for iOS devices.

We are already able to install profiles with a MDM-Payload over a website and we already receive PUT requestes sent by the iOS devices containing the PushMagic, a deviceToken and other values as well.

We created a COMPANY.pem SSL certificiate using this description: http://www.softhinker.com/in-the-news/iosmdmvendorcsrsigning

We tried to send a Push Notification with the library push-sharp: https://github.com/Redth/PushSharp

We were using MDM.p12 file build with these commands

openssl pkcs12 -export -in ./COMPANY.pem -inkey ./customerPrivateKey.pem -certfile ./CertificateSigningRequest.certSigningRequest -out MDM.p12

The program sending this notification does not throw any errors and exits normally. But we are not receiving any Push-Notifications on our devices nor receiving anything through the feedback service.

Also to mention is that we tried to use the sandbox-server and the production-server as well.

Here is our code using the push-sharp library:

    using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using PushSharp;
using PushSharp.Apple;


namespace PushSharp.Sample
{
    class Program
    {
        static void Main(string[] args)
        {

            //Create our service    
            PushService push = new PushService();

            //Wire up the events
            push.Events.OnDeviceSubscriptionExpired += new Common.ChannelEvents.DeviceSubscriptionExpired(Events_OnDeviceSubscriptionExpired);
            push.Events.OnDeviceSubscriptionIdChanged += new Common.ChannelEvents.DeviceSubscriptionIdChanged(Events_OnDeviceSubscriptionIdChanged);
            push.Events.OnChannelException += new Common.ChannelEvents.ChannelExceptionDelegate(Events_OnChannelException);
            push.Events.OnNotificationSendFailure += new Common.ChannelEvents.NotificationSendFailureDelegate(Events_OnNotificationSendFailure);
            push.Events.OnNotificationSent += new Common.ChannelEvents.NotificationSentDelegate(Events_OnNotificationSent);

            //Configure and start Apple APNS
            // IMPORTANT: Make sure you use the right Push certificate.  Apple allows you to generate one for connecting to Sandbox,
            //   and one for connecting to Production.  You must use the right one, to match the provisioning profile you build your
            //   app with!
            //var appleCert = File.ReadAllBytes("C:\\TEMP\\apns-mdm.p12");
            var appleCert = File.ReadAllBytes(@".\MDM.p12");

            //IMPORTANT: If you are using a Development provisioning Profile, you must use the Sandbox push notification server 
            //  (so you would leave the first arg in the ctor of ApplePushChannelSettings as 'false')
            //  If you are using an AdHoc or AppStore provisioning profile, you must use the Production push notification server
            //  (so you would change the first arg in the ctor of ApplePushChannelSettings to 'true')
            push.StartApplePushService(new ApplePushChannelSettings(true, appleCert, "PWD"));


            //String p12File = @".\apns-mdm.p12";
            //String p12Password = "PWD";
            String pushMagicString = "00454668-00B2-4122-A1DC-72ACD64E6AFB";
            //String deviceToken = "27asObngxvVNb3RvRMs3XVaEWC1DNa3TjFE12stKsig=";


            //Configure and start Android GCM
            //IMPORTANT: The SENDER_ID is your Google API Console App Project ID.
            //  Be sure to get the right Project ID from your Google APIs Console.  It's not the named project ID that appears in the Overview,
            //  but instead the numeric project id in the url: eg: https://code.google.com/apis/console/?pli=1#project:785671162406:overview
            //  where 785671162406 is the project id, which is the SENDER_ID to use!
            //push.StartGoogleCloudMessagingPushService(new GcmPushChannelSettings("785671162406", "AIzaSyC2PZNXQDVaUpZGmtsF_Vp8tHtIABVjazI", "com.pushsharp.test"));

            //Configure and start Windows Phone Notifications
            //push.StartWindowsPhonePushService(new WindowsPhone.WindowsPhonePushChannelSettings());

            //Fluent construction of a Windows Phone Toast notification
            //push.QueueNotification(NotificationFactory.WindowsPhone().Toast()
            //.ForEndpointUri(new Uri("http://sn1.notify.live.net/throttledthirdparty/01.00/AAFCoNoCXidwRpn5NOxvwSxPAgAAAAADAgAAAAQUZm52OkJCMjg1QTg1QkZDMkUxREQ"))
            //.ForOSVersion(WindowsPhone.WindowsPhoneDeviceOSVersion.MangoSevenPointFive)
            //.WithBatchingInterval(WindowsPhone.BatchingInterval.Immediate)
            //.WithNavigatePath("/MainPage.xaml")
            //.WithText1("PushSharp")
            //.WithText2("This is a Toast"));

            //Fluent construction of an iOS notification
            //IMPORTANT: For iOS you MUST MUST MUST use your own DeviceToken here that gets generated within your iOS app itself when the Application Delegate
            //  for registered for remote notifications is called, and the device token is passed back to you
            String test = "3d 58 64 4d 90 d3 18 09 22 5c 50 d2 12 16 b5 67 71 1e be 5c 13 6e 41 3c 3e 81 b5 52 30 68 09 a5";
            test = test.Replace(" ", string.Empty);
            Console.WriteLine("Device Token length is: " + test.Length);
            Console.WriteLine("DeviceToken is: " + test);
            Console.WriteLine("PushMagic is: " + pushMagicString);

            DateTime dayAfterTomorrow = DateTime.Now.AddDays(2);
            Console.WriteLine("Expiry date is: " + dayAfterTomorrow.ToString());

            push.QueueNotification(NotificationFactory.Apple()
                .ForDeviceToken(test).WithExpiry(dayAfterTomorrow).WithCustomItem("mdm", pushMagicString));


            //push.Events.RaiseNotificationSent(NotificationFactory.Apple()
            //    .ForDeviceToken(hex).WithCustomItem("mdm", pushMagicString));


            //Fluent construction of an Android GCM Notification
            //push.QueueNotification(NotificationFactory.AndroidGcm()
            //    .ForDeviceRegistrationId("APA91bG7J-cZjkURrqi58cEd5ain6hzi4i06T0zg9eM2kQAprV-fslFiq60hnBUVlnJPlPV-4K7X39aHIe55of8fJugEuYMyAZSUbmDyima5ZTC7hn4euQ0Yflj2wMeTxnyMOZPuwTLuYNiJ6EREeI9qJuJZH9Zu9g")
            //    .WithCollapseKey("NONE")
            //    .WithJson("{\"alert\":\"Alert Text!\",\"badge\":\"7\"}"));

            Console.WriteLine("Waiting for Queue to Finish...");

            //Stop and wait for the queues to drains
            push.StopAllServices(true);

            Console.WriteLine("Queue Finished, press return to exit...");
            Console.ReadLine();
        }

        static void Events_OnDeviceSubscriptionIdChanged(Common.PlatformType platform, string oldDeviceInfo, string newDeviceInfo)
        {
            //Currently this event will only ever happen for Android GCM
            Console.WriteLine("Device Registration Changed:  Old-> " + oldDeviceInfo + "  New-> " + newDeviceInfo);
        }

        static void Events_OnNotificationSent(Common.Notification notification)
        {
            Console.WriteLine("Sent: " + notification.Platform.ToString() + " -> " + notification.ToString());
        }

        static void Events_OnNotificationSendFailure(Common.Notification notification, Exception notificationFailureException)
        {
            Console.WriteLine("Failure: " + notification.Platform.ToString() + " -> " + notificationFailureException.Message + " -> " + notification.ToString());
        }

        static void Events_OnChannelException(Exception exception)
        {
            Console.WriteLine("Channel Exception: " + exception.ToString());
        }

        static void Events_OnDeviceSubscriptionExpired(Common.PlatformType platform, string deviceInfo)
        {
            Console.WriteLine("Device Subscription Expired: " + platform.ToString() + " -> " + deviceInfo);
        }
    }
}

We encoded the received deviceToken using this website: http://home.paulschou.net/tools/xlate/ from base64 to HEX.

We are also using the APS/PC Logging profile on our iOS device to get more output through the debug console provided by IPCU.

User97693321
  • 3,336
  • 7
  • 45
  • 69
user1534860
  • 21
  • 1
  • 3

6 Answers6

3

The message sent to the Push Notification Service must NOT contain the aps key. The message should only contain the PushMagic string as the value of the mdm key. For example, using the value you defined as the pushMagicString, the message to the Push Notification Service should look like this:

{"mdm":"00454668-00B2-4122-A1DC-72ACD64E6AFB"}

I have not used the push-sharp library you are using. However, I briefly looked through the code and it seems like the AppleNotificationPayload.ToJson method is always adding an aps key to the message. I suggest commenting out line 114 and then trying it. Here is the ToJson method with the line commented out:

    public string ToJson()
    {
        JObject json = new JObject();

        JObject aps = new JObject();

        if (!this.Alert.IsEmpty)
        {
            if (!string.IsNullOrEmpty(this.Alert.Body)
                && string.IsNullOrEmpty(this.Alert.LocalizedKey)
                && string.IsNullOrEmpty(this.Alert.ActionLocalizedKey)
                && (this.Alert.LocalizedArgs == null || this.Alert.LocalizedArgs.Count <= 0)
                && !this.HideActionButton)
            {
                aps["alert"] = new JValue(this.Alert.Body);
            }
            else
            {
                JObject jsonAlert = new JObject();

                if (!string.IsNullOrEmpty(this.Alert.LocalizedKey))
                    jsonAlert["loc-key"] = new JValue(this.Alert.LocalizedKey);

                if (this.Alert.LocalizedArgs != null && this.Alert.LocalizedArgs.Count > 0)
                    jsonAlert["loc-args"] = new JArray(this.Alert.LocalizedArgs.ToArray());

                if (!string.IsNullOrEmpty(this.Alert.Body))
                    jsonAlert["body"] = new JValue(this.Alert.Body);

                if (this.HideActionButton)
                    jsonAlert["action-loc-key"] = new JValue((string)null);
                else if (!string.IsNullOrEmpty(this.Alert.ActionLocalizedKey))
                    jsonAlert["action-loc-key"] = new JValue(this.Alert.ActionLocalizedKey);

                aps["alert"] = jsonAlert;
            }
        }

        if (this.Badge.HasValue)
            aps["badge"] = new JValue(this.Badge.Value);

        if (!string.IsNullOrEmpty(this.Sound))
            aps["sound"] = new JValue(this.Sound);

        if (this.ContentAvailable.HasValue)
            aps["content-available"] = new JValue(this.ContentAvailable.Value);

        //json["aps"] = aps;

        foreach (string key in this.CustomItems.Keys)
        {
            if (this.CustomItems[key].Length == 1)
                json[key] = new JValue(this.CustomItems[key][0]);
            else if (this.CustomItems[key].Length > 1)
                json[key] = new JArray(this.CustomItems[key]);
        }

        string rawString = json.ToString(Newtonsoft.Json.Formatting.None, null);

        StringBuilder encodedString = new StringBuilder();
        foreach (char c in rawString)
        {
            if ((int)c < 32 || (int)c > 127)
                encodedString.Append("\\u" + String.Format("{0:x4}", Convert.ToUInt32(c)));
            else
                encodedString.Append(c);
        }
        return rawString;// encodedString.ToString();
    }
Michael Gaffney
  • 495
  • 2
  • 7
  • 1
    According to https://media.blackhat.com/bh-us-11/Schuetz/BH_US_11_Schuetz_InsideAppleMDM_WP.pdf ios versions 4.2.1 onwards don't really care about the presence of the aps key. – stephenmuss Aug 08 '12 at 12:45
  • 1
    The official MDM spec from Apple says "There should not be an aps key. " (I copy/pasted it right from the spec so it's word for word). – Michael Gaffney Aug 17 '12 at 21:40
2

have you made sure that during MDM enrollment, the MDM payload (PayloadType = com.apple.mdm) you deliver has the correct Topic?

i.e. in your APN push cert that you've downloaded from apple.you'll see something like CN = com.apple.mgmt.external.[GUID]. This needs to be the same value delivered to your IOS device during MDM enrollment.

If APN does not give any error during the message upload, or the feedback service does not return the deviceID which indicates that the device is not available, it should mean that it will be delivered correctly to the device. The next phase is to determine if the IOS device is setup to listen to the APN message via this topic.

You can try connecting your device to XCode or IPCU, in the console logs, it will contain logs indicating if APN was able to successfully deliver this message using the agreed upon topic.

Here's also an article on troubleshootin APN http://developer.apple.com/library/ios/#technotes/tn2265/_index.html

there's a downloadable profile in the above article that you can load to your device for additional verbose logging.

savagepanda
  • 857
  • 12
  • 25
0

The Push-sharp library seems designed for application notification. To make it work for MDM notification you can fix the function AppleNotificationPayload.ToJson, line 114 by

if (aps.HasValues == true)
{
    json["aps"] = aps;
}

This fix will make sure "aps" key will be included only for application notification. And the library could be used for both MDM notification and application notification.

vetti
  • 299
  • 1
  • 3
  • 13
0

The "aps" key will be ignored by the ios. You can see the screenshot log. It ignore the "aps" and execute the clearpasscode command successfully.

j0k
  • 22,600
  • 28
  • 79
  • 90
Kun
  • 21
  • 3
0

Unable to send iOS MDM Push Notification using Push Sharp

this answer work well

var pushService = new PushService();
// attach event listeners

// override the production/development auto-detection as it doesn't
// work for MDM certificates
var cert = null; // load your push client certificate
var channel = new ApplePushChannelSettings(true, cert, true);
pushService.StartApplePushService(channel);

// create and send the notification
var notification = NotificationFactory
    .Apple()
    .ForDeviceToken("your-device-token-received-from-checkin")
    .WithExpiry(DateTime.UtcNow.AddDays(1))
    .WithCustomItem("mdm", "your-push-magic-received-in-checkin");
pushService.QueueNotification(notification);
Community
  • 1
  • 1
gulgui
  • 1
0

I have also followed same process and commands. One thing i have noticed in your code that you have commented deviceToken recieved from "MessageType:TokenUpdate"

//String deviceToken = "27asObngxvVNb3RvRMs3XVaEWC1DNa3TjFE12stKsig=";

And you are using following variable "test" as device token, to send Push Notification.

String test = "3d 58 64 4d 90 d3 18 09 22 5c 50 d2 12 16 b5 67 71 1e be 5c 13 6e 41 3c 3e 81 b5 52 30 68 09 a5";
test = test.Replace(" ", string.Empty);

When i use device token from "MessageType:TokenUpdate"; pushsharp is giving me error

APNS NotificationFailureException -> 8 : Invalid token

Edit:

I have figured it our we need to convert byte[] to hex string; use following method for the same:

public static string ByteArrayToString(byte[] ba) 
{ 
    StringBuilder hex = new StringBuilder(ba.Length * 2); 
    foreach (byte b in ba) 
        hex.AppendFormat("{0:x2}", b); 

    return hex.ToString(); 
}
Parrish Husband
  • 3,148
  • 18
  • 40
vivek
  • 447
  • 4
  • 6
  • I have figured it our we need to convert byte[] to hex string; use following method for the same. public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) hex.AppendFormat("{0:x2}", b); return hex.ToString(); } – vivek Sep 24 '14 at 11:37