2011-02-09 46 views
3

我們的組織爲多個客戶端管理穩定的iOS應用程序,這意味着要處理大量不同的開發人員身份證書和推送通知證書。獲取Apple Keychain以識別Bouncy Castle .NET創建的PKCS12(.p12)商店

我已經成功推出了Bouncy Castle C# Crypto API簡化了推送通知的證書和私鑰管理,essentially eliminating the need for the Keychain for all our push notification certificates

我想將其擴展到開發人員身份證書。目標是將每個開發人員身份的所有私鑰和證書信息存儲在數據庫中。然後,當需要配置新的開發人員或構建計算機時,服務器端代碼可以將所有證書和私鑰包裝到一個p12存檔中,並使用一個可導入目標Mac Keychain的密碼。

不幸的是,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文件時,我所看到的,但我不知道這是原因。

如果我將Mac Keychain生成的p12加載到Bouncy Castle PKCS12Store中,然後檢查Keychain p12上的密鑰,那麼證書和私鑰都具有一個屬性,其中鍵爲「1.2.840.113549.1.9.21 「與等價值(一個值爲#af8a1d6891efeb32756c12b7bdd96b5ec673e11e的DerOctetString)。

如果我做同樣的我產生P12文件,私鑰包含「1.2.840.113549.1.9.21」屬性,但該證書不。

如果我Google "1.2.840.113549.1.9.21",我find out that this OID means PKCS_12_LOCAL_KEY_ID。我唯一的理論是鑰匙串依靠這個來匹配證書和私鑰,而且我生成的文件沒有這個,所以它失敗了。

不過,我試着將這些值到一個Hashtable,然後使用CertificateEntry構造函數的屬性哈希表。如果我這樣做,然後保存字節,然後重新加載字節,該屬性再次丟失。

所以我很失望。也許這個屬性是Bouncy Castle API中的一個小故障?也許有什麼我做錯了。也許鑰匙串對傳入的p12文件有非常荒謬的非標準要求。無論如何,任何可能提供的幫助將不勝感激。

+0

嗨大衛,你有解決這個問題? – 2011-12-20 08:49:59

回答

1

BouncyCastle的的Pkcs12Store需要同時設置好記的名稱和本地密鑰ID的保健屬性爲您(或者至少是在1.7版本這樣做,大約在2011年4月)。我的猜測是,你必須使用一箇舊版本,如果這不起作用。

這裏是我如何節省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()); 

導入店鑰匙扣Access.app在OS X 10。7將證書和私鑰正確放置在鑰匙串中,並將證書放置在UI中的私鑰內,就像由鑰匙串訪問本身產生的證書和密鑰一樣。

在附註中,似乎Pkcs12Store使用證書的公鑰來生成由證書和密鑰條目共享的LocalKeyId屬性的值。

您可以看到Pkcs12Store源的相關部分here