Наша организация управляет стабильными приложениями для 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. В любом случае, любая помощь, которая может быть оказана, будет принята с благодарностью.