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