Преобразование Windows Phone ANID в ANID2 на С#?

В Windows Phone 7 было свойство анонимного идентификатора пользователя, называемое ANID. Windows Phone 8 заменил его на ANID2. Разница в том, что ANID2 зависит от идентификатора издателя приложения.

Можно преобразовать ANID в ANID2 в следующем примере кода на MSDN показывает. Все, что вам нужно, это исходный WP7 ANID и идентификатор издателя (guid). Проблема в том, что пример на C++. Я пытался портировать его на С#, но безуспешно.

Сам алгоритм довольно прост:

var data = HMAC(ANID, publisherId) // Uses SHA-256
var result = ToBase64(data)

Проблема в том, что я не могу добиться совпадения результатов. Я убедился, что C++ работает правильно, создав два приложения (WP7 и WP8), запустив их на одних и тех же устройствах, а затем преобразовав ANID из приложения WP7 с помощью GUID издателя. На C++ преобразованный ANID2 и ANID2 с устройства совпадают. На C# конвертированный ANID2 — это что-то другое.

Код С# прост:

        var anidBytes = System.Text.Encoding.UTF8.GetBytes(this.anidBox.Text);
        var publisherGuid = Guid.Parse(this.publisherBox.Text.ToUpper());

        var macObject = new HMACSHA256(anidBytes);
        var hashed = macObject.ComputeHash(publisherGuid.ToByteArray());

        var result = Convert.ToBase64String(hashed);

Версия C++ использует что-то под названием CNG (Cryptography API: Next Generation). Вот код из него:

BCryptOpenAlgorithmProvider(&Algorithm, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
BCryptGetProperty(Algorithm,BCRYPT_OBJECT_LENGTH,reinterpret_cast<BYTE*>(&HashObjectLength),PropertyLength,&PropertyLength,0);
BCryptCreateHash(Algorithm, &Hash, HashObject, HashObjectLength, pAnidId, dwAnidLength, 0);
BCryptHashData(Hash, const_cast<BYTE*>(pPublisherId), dwPublisherIdLength, 0);
BCryptFinishHash(Hash, pUniqueId, GETDEVICEUNIQUEID_V1_OUTPUT, 0);

После чего «pUniqueId» преобразуется в Base64 с использованием какой-либо пользовательской встроенной функции.

Любая помощь приветствуется.

Обновление: Visual Studio сообщает, что anidBytes (C#) и pAnidId (C++) (это строка ANID, преобразованная в байты) имеют длину 44. Вот как отладчик C# сообщает массив байтов:

Массив байтов C#

А вот отладчик C++: C++ version

Я не знаю С++, поэтому я не уверен, что эти два идентичны. Они есть, но C++ имеет эти символы '\0' после каждого, и я не уверен, что это нормально. Я думаю, что да, так как длина в обоих случаях указана как 44.

Другое сравнение массива байтов — это publisherGuid (C#) и pPublisherId (C++) (идентификатор издателя в виде GUID). Я думаю, что они снова совпадают. С#:

массив байтов C#

C++:

массив байтов C++

Если я затем посмотрю на вывод после того, как значение было зашифровано, я увижу разницу:

На С# я получаю вывод из этого кода:

        var macObject = new HMACSHA256(anidBytes);
        var hashed = macObject.ComputeHash(publisherBytes);

И массив байтов выглядит так:

Результат шифрования C#

В коде C++, если я проверю pUniqueId (результат BCryptFinishHash), я увижу это:

Результат хеширования C++

Длина кажется одинаковой в обоих этих случаях, но результат - нет.

В C#, если я изменю тип кодировки с UTF8 на Unicode, массив байтов anidBytes изменится на это:

Юникод

Так что это идентично тому, что показывает отладчик C++. Также меняется результат, но он все еще отличается от C++. Вот новый результат C#:

Результаты Юникода C#

Это правильный результат от С++:

Правильный результат


person Mikael Koskinen    schedule 01.08.2013    source источник
comment
Прежде всего: проверили ли вы, что anidBytes и publisherGuid.ToByteArray() на двоичном уровне идентичны значениям, которые вы используете в C++?   -  person Kevin Gosse    schedule 01.08.2013
comment
Я добавил несколько скриншотов, которые, кажется, указывают на то, что они идентичны.   -  person Mikael Koskinen    schedule 01.08.2013
comment
Похоже, что значение закодировано в UTF-16 в версии C++ (отсюда два байта на символ). Используйте System.Text.Encoding.Unicode в C# вместо System.Text.Encoding.UTF8   -  person Kevin Gosse    schedule 01.08.2013
comment
Спасибо за предложение. К сожалению, результат все еще отличается. Я обновил пост, чтобы отразить это.   -  person Mikael Koskinen    schedule 01.08.2013
comment
Я также перебрал все варианты кодирования. ASCII и UTF8 дают одинаковый результат, другие отличаются. Тем не менее ни один из них не соответствует выходным данным C++.   -  person Mikael Koskinen    schedule 01.08.2013
comment
Вы смотрели мой ответ? Это должно просто работать. Попробуйте. Я сталкивался с этой проблемой раньше.   -  person wiyonoaten    schedule 10.08.2013


Ответы (3)


Убедитесь, что вы разрезали массив байтов ANID пополам, прежде чем использовать его в качестве секретного ключа для HMACSHA256. Алгоритму преобразования нужны только первые 44 байта, а не все 88. Не спрашивайте меня, почему, но, насколько мне известно, это ошибка в коде C++, который они используют при преобразовании ANID в ANID2, если вы посмотрите на пример кода в http://code.msdn.microsoft.com/wpapps/ANID-to-ANID2-Converter-cc428038 - они, должно быть, перепутали wchar с char!

person wiyonoaten    schedule 02.08.2013

В C# для вашего объекта HMACSHA256 попробуйте установить свойство Key, а не инициализировать его в конструкторе.

var anidBytes = System.Text.Encoding.Unicode.GetBytes(this.anidBox.Text);
var macObject = new HMACSHA256();
macObject.Key = anidBytes;

http://msdn.microsoft.com/en-us/library/9c9tf8wc.aspx говорит, что конструктор дополняет ключ до 64 байт.

(Я не уверен на 100%, что делает установщик свойств, но попробовать стоит).

person Paul Annetts    schedule 01.08.2013

person    schedule
comment
У меня есть приложение для Windows Phone 7.5, которое я обновил до Windows Phone 8, и это помогло мне. Я помню, что читал это где-то раньше, но не помню точное место. Вы можете протестировать это с помощью примера приложения в любом случае, и вы увидите, что оно действительно работает. - person Elia Karagiannis; 30.06.2015