Получить Apple Keychain, чтобы распознать Bouncy Castle .NET созданный магазин PKCS12 (.p12) - PullRequest
4 голосов
/ 09 февраля 2011

Наша организация управляет стабильными приложениями для iOS для нескольких клиентов, что означает работу с множеством различных сертификатов идентификации разработчика и сертификатов push-уведомлений.

У меня был успех с Bouncy Castle C # Crypto API в упрощении управления сертификатами и закрытыми ключами для push-уведомлений, по существу устраняя необходимость в связке ключей для всех наших сертификатов push-уведомлений .

Я хотел бы распространить это на удостоверения личности разработчика. Цель состоит в том, чтобы хранить всю личную информацию о ключах и сертификатах в базе данных для каждого разработчика. Затем, когда необходимо подготовить нового разработчика или сборочную машину, код на стороне сервера может обернуть все сертификаты и закрытые ключи в один архив p12 с одним паролем, который можно импортировать в связку ключей целевого Mac.

К сожалению, брелок Mac не любит файлы p12, которые я генерирую. Это раздражает, так как я могу успешно импортировать эти файлы в диспетчер сертификатов Windows.

Код, который я использую (важные части), выглядит следующим образом:

private byte[] GetP12Bytes(List<DevIdentity> identities, string password)
{
    Pkcs12Store store = new Pkcs12Store();

    foreach(DevIdentity ident in identities)
    {
        // Easiest to create a Bouncy Castle cert by converting from .NET
        var dotNetCert = new X509Certificate2(ident.CertificateBytes);
        // This method (not shown) parses the CN= attribute out of the cert's distinguished name
        string friendlyName = GetFriendlyName(dotNetCert.Subject); 

        // Now reconstitute the private key from saved value strings
        BigInteger modulus = new BigInteger(ident.PrivateKey.Modulus);
        BigInteger publicExponent = new BigInteger(ident.PrivateKey.PublicExponent);
        BigInteger privateExponent = new BigInteger(ident.PrivateKey.Exponent);
        BigInteger p = new BigInteger(ident.PrivateKey.P);
        BigInteger q = new BigInteger(ident.PrivateKey.Q);
        BigInteger dP = new BigInteger(ident.PrivateKey.DP);
        BigInteger dQ = new BigInteger(ident.PrivateKey.DQ);
        BigInteger qInv = new BigInteger(ident.PrivateKey.QInv);
        RsaKeyParameters kp = new RsaPrivateCrtKeyParameters(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv);
        AsymmetricKeyEntry privateKey = new AsymmetricKeyEntry(kp);

        // Now let's convert to a Bouncy Castle cert and wrap it for packaging
        Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(dotNetCert);
        X509CertificateEntry certEntry = new X509CertificateEntry(cert);

        // Set the private key and certificate into the store
        store.SetCertificateEntry(friendlyName, certEntry);
        store.SetKeyEntry(ident.PrivateKeyName, privateKey, new X509CertificateEntry[] { certEntry });
    }

    using (MemoryStream ms = new MemoryStream())
    {
        store.Save(ms, password.ToCharArray(), new SecureRandom());
        ms.Flush();
        byte[] p12Bytes = ms.ToArray();
        return p12Bytes;
    }
}

Как я уже сказал, это прекрасно работает для импорта в Windows, но завершается неудачно с очень общей ошибкой при импорте в связку ключей Mac.

Есть одно существенное отличие, которое я вижу при загрузке сгенерированного Keychain p12 и моего собственного сгенерированного файла p12, но я не знаю, является ли это причиной.

Если я загружаю сгенерированную цепочку ключей Mac p12 в Bouncy Castle PKCS12Store, а затем проверяю ключи на цепочке ключей p12, то сертификат и закрытый ключ имеют атрибут с ключом «1.2.840.113549.1.9.21». с эквивалентными значениями (DerOctetString со значением # af8a1d6891efeb32756c12b7bdd96b5ec673e11e).

Если я делаю то же самое с моим сгенерированным файлом p12, закрытый ключ содержит атрибут «1.2.840.113549.1.9.21», а сертификат - нет.

Если я Google "1.2.840.113549.1.9.21" , я узнаю, что этот OID означает PKCS_12_LOCAL_KEY_ID . Моя единственная теория заключается в том, что цепочка для ключей полагается на это, чтобы сопоставить сертификат и закрытый ключ, и что мой сгенерированный файл не имеет этого, поэтому он терпит неудачу.

Однако я попытался добавить эти значения в Hashtable, а затем использовать конструктор CertificateEntry, который принимает атрибут hashtable. Если я сделаю это, а затем сохраню байты, а затем перезагрузлю байты, этот атрибут снова будет отсутствовать.

Так что я в замешательстве. Может быть, этот атрибут является ошибкой в ​​API Bouncy Castle? Может быть, я что-то не так делаю. Возможно, брелок имеет нелепые нестандартные требования для входящих файлов p12. В любом случае, любая помощь, которая может быть оказана, будет принята с благодарностью.

1 Ответ

1 голос
/ 29 марта 2012

BouncyCastle's Pkcs12Store заботится о настройке атрибутов Friendly Name и Local Key ID для вас (или, по крайней мере, в версии 1.7, около апреля 2011 г.) Я предполагаю, что вы, должно быть, использовали старую версию, где это не сработало.

Вот как я сохраняю идентификацию iPhone Developer в экземпляр Pkcs12Store (дополнительные вещи и безопасность не включены):

var store = new Pkcs12Store();

// pairs is IEnumerable<Tuple<X509Certificate, AsymmetricKeyParameter>>
foreach (var pair in pairs)
{
    var cn = pair.Item1.SubjectDN
         .GetValueList(X509Name.CN).OfType<string>().Single();

    var certEntry = new X509CertificateEntry(pair.Item1);
    store.SetCertificateEntry(cn, certEntry);

    var keyEntry = new AsymmetricKeyEntry(pair.Item2);
    store.SetKeyEntry("Developer Name", keyEntry, new[] { certEntry });
}

store.Save(stream, string.Empty.ToArray(), new SecureRandom());

Импорт хранилища в Keychain Access.app на OS X 10.7 правильно помещает сертификат и закрытый ключ в цепочку для ключей и помещает сертификат в закрытый ключ в пользовательском интерфейсе, как в случае сертификата и ключа, сгенерированных самим Keychain Access.

С другой стороны, кажется, что Pkcs12Store использует открытый ключ сертификата для генерации значения атрибута LocalKeyId, совместно используемого сертификатом и записями ключа.

Вы можете увидеть соответствующий раздел источника Pkcs12Store здесь .

...