Заставьте Apple Keychain распознавать магазин Bouncy Castle, созданный .NET PKCS12 (.p12)

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

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

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

К сожалению, Mac Keychain не любит файлы 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.

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

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

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

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

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

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


person David Boike    schedule 09.02.2011    source источник
comment
Привет, Дэвид, Вам удалось решить эту проблему???   -  person Nirmal Patel    schedule 20.12.2011


Ответы (1)


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

Вот как я сохраняю удостоверение разработчика iPhone в экземпляре 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 здесь.

person Tayschrenn    schedule 28.03.2012