4

私たちの組織は、複数のクライアント向けに安定した 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 ファイルに対してばかげた非標準の要件があります。いずれにせよ、提供できるヘルプは大歓迎です。

4

1 に答える 1

1

BouncyCastle の Pkcs12Store は、Friendly Name 属性と Local Key ID 属性の両方を設定します (または、少なくとも 2011 年 4 月頃の 1.7 リリースではそうします)。私の推測では、これが機能しない古いバージョンを使用したに違いありません。

iPhone Developer ID を 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());

OS X 10.7 の Keychain Access.app にストアをインポートすると、Keychain Access 自体によって生成された証明書とキーと同様に、証明書と秘密キーがキーチェーンに正しく配置され、UI の秘密キー内に証明書が配置されます。

ちなみに、Pkcs12Store は証明書の公開鍵を使用して、証明書と鍵エントリによって共有される LocalKeyId 属性の値を生成しているようです。

ここで、Pkcs12Store ソースの関連セクションを参照できます。

于 2012-03-28T20:40:29.753 に答える