私たちの組織は、複数のクライアント向けに安定した iOS アプリケーションを管理しています。つまり、多くの異なる開発者 ID 証明書とプッシュ通知証明書を扱っています。
Bouncy Castle C# Crypto APIを使用して、プッシュ通知用の証明書と秘密鍵の管理を簡素化し、すべてのプッシュ通知証明書のキーチェーンを本質的に不要にすることに成功しました。
これを開発者のアイデンティティ証明書に拡張したいと思います。目標は、開発者 ID ごとにすべての秘密鍵と証明書情報をデータベースに格納することです。次に、新しい開発者またはビルド マシンをプロビジョニングする必要がある場合、サーバー側のコードは、ターゲット Mac のキーチェーンにインポートできる 1 つのパスワードを含む 1 つの p12 アーカイブにすべての証明書と秘密鍵をラップできます。
残念ながら、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 キーチェーンにインポートするときは非常に一般的なエラーで失敗します。
キーチェーンで生成された p12 と自分で生成した p12 ファイルをロードするときに確認できる大きな違いが 1 つありますが、これが原因かどうかはわかりません。
Mac キーチェーンで生成された p12 を Bouncy Castle PKCS12Store にロードし、キーチェーン p12 でキーを調べると、証明書と秘密キーの両方にキー「1.2.840.113549.1.9.21」の属性があり、値は同等です。 (値 #af8a1d6891efeb32756c12b7bdd96b5ec673e11e を持つ DerOctetString)。
生成された p12 ファイルに対して同じことを行うと、秘密鍵には「1.2.840.113549.1.9.21」属性が含まれますが、証明書には含まれません。
"1.2.840.113549.1.9.21"をGoogleで検索すると、この OID が PKCS_12_LOCAL_KEY_ID を意味することがわかります。私の唯一の理論は、キーチェーンがこれに依存して証明書と秘密鍵を照合し、生成されたファイルにはこれがないため、失敗するということです。
ただし、これらの値を Hashtable に追加してから、属性 hashtable を取る CertificateEntry コンストラクターを使用してみました。それを行ってからバイトを保存してからバイトをリロードすると、その属性が再び失われます。
だから私は困惑しています。この属性は、Bouncy Castle API の不具合でしょうか? 多分私が間違っていることがあります。おそらく、キーチェーンには、受信する p12 ファイルに対してばかげた非標準の要件があります。いずれにせよ、提供できるヘルプは大歓迎です。