Pkcs12Store.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Misc;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Oiw;
  9. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Pkcs;
  10. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
  11. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  12. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
  13. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  14. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
  15. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.Encoders;
  16. using Best.HTTP.SecureProtocol.Org.BouncyCastle.X509;
  17. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Pkcs
  18. {
  19. public class Pkcs12Store
  20. {
  21. public const string IgnoreUselessPasswordProperty = "Best.HTTP.SecureProtocol.Org.BouncyCastle.Pkcs12.IgnoreUselessPassword";
  22. private readonly Dictionary<string, AsymmetricKeyEntry> m_keys =
  23. new Dictionary<string, AsymmetricKeyEntry>(StringComparer.OrdinalIgnoreCase);
  24. private readonly Dictionary<string, string> m_localIds = new Dictionary<string, string>();
  25. private readonly Dictionary<string, X509CertificateEntry> m_certs =
  26. new Dictionary<string, X509CertificateEntry>(StringComparer.OrdinalIgnoreCase);
  27. private readonly Dictionary<CertId, X509CertificateEntry> m_chainCerts =
  28. new Dictionary<CertId, X509CertificateEntry>();
  29. private readonly Dictionary<string, X509CertificateEntry> m_keyCerts =
  30. new Dictionary<string, X509CertificateEntry>();
  31. private readonly DerObjectIdentifier keyAlgorithm;
  32. private readonly DerObjectIdentifier keyPrfAlgorithm;
  33. private readonly DerObjectIdentifier certAlgorithm;
  34. private readonly bool useDerEncoding;
  35. private AsymmetricKeyEntry unmarkedKeyEntry = null;
  36. private const int MinIterations = 1024;
  37. private const int SaltSize = 20;
  38. private static SubjectKeyIdentifier CreateSubjectKeyID(AsymmetricKeyParameter pubKey)
  39. {
  40. return new SubjectKeyIdentifier(
  41. SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey));
  42. }
  43. internal class CertId
  44. {
  45. private readonly byte[] id;
  46. internal CertId(
  47. AsymmetricKeyParameter pubKey)
  48. {
  49. this.id = CreateSubjectKeyID(pubKey).GetKeyIdentifier();
  50. }
  51. internal CertId(
  52. byte[] id)
  53. {
  54. this.id = id;
  55. }
  56. internal byte[] Id
  57. {
  58. get { return id; }
  59. }
  60. public override int GetHashCode()
  61. {
  62. return Arrays.GetHashCode(id);
  63. }
  64. public override bool Equals(
  65. object obj)
  66. {
  67. if (obj == this)
  68. return true;
  69. CertId other = obj as CertId;
  70. if (other == null)
  71. return false;
  72. return Arrays.AreEqual(id, other.id);
  73. }
  74. }
  75. internal Pkcs12Store(DerObjectIdentifier keyAlgorithm, DerObjectIdentifier keyPrfAlgorithm,
  76. DerObjectIdentifier certAlgorithm, bool useDerEncoding)
  77. {
  78. this.keyAlgorithm = keyAlgorithm;
  79. this.keyPrfAlgorithm = keyPrfAlgorithm;
  80. this.certAlgorithm = certAlgorithm;
  81. this.useDerEncoding = useDerEncoding;
  82. }
  83. protected virtual void LoadKeyBag(PrivateKeyInfo privKeyInfo, Asn1Set bagAttributes)
  84. {
  85. AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo);
  86. var attributes = new Dictionary<DerObjectIdentifier, Asn1Encodable>();
  87. AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privKey, attributes);
  88. string alias = null;
  89. Asn1OctetString localId = null;
  90. if (bagAttributes != null)
  91. {
  92. foreach (Asn1Sequence sq in bagAttributes)
  93. {
  94. DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]);
  95. Asn1Set attrSet = Asn1Set.GetInstance(sq[1]);
  96. Asn1Encodable attr = null;
  97. if (attrSet.Count > 0)
  98. {
  99. // TODO We should be adding all attributes in the set
  100. attr = attrSet[0];
  101. // TODO We might want to "merge" attribute sets with
  102. // the same OID - currently, differing values give an error
  103. if (attributes.TryGetValue(aOid, out var attributeValue))
  104. {
  105. // OK, but the value has to be the same
  106. if (!attributeValue.Equals(attr))
  107. throw new IOException("attempt to add existing attribute with different value");
  108. }
  109. else
  110. {
  111. attributes[aOid] = attr;
  112. }
  113. if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
  114. {
  115. alias = ((DerBmpString)attr).GetString();
  116. // TODO Do these in a separate loop, just collect aliases here
  117. m_keys[alias] = keyEntry;
  118. }
  119. else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
  120. {
  121. localId = (Asn1OctetString)attr;
  122. }
  123. }
  124. }
  125. }
  126. if (localId != null)
  127. {
  128. string name = Hex.ToHexString(localId.GetOctets());
  129. if (alias == null)
  130. {
  131. m_keys[name] = keyEntry;
  132. }
  133. else
  134. {
  135. // TODO There may have been more than one alias
  136. m_localIds[alias] = name;
  137. }
  138. }
  139. else
  140. {
  141. unmarkedKeyEntry = keyEntry;
  142. }
  143. }
  144. protected virtual void LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo encPrivKeyInfo, Asn1Set bagAttributes,
  145. char[] password, bool wrongPkcs12Zero)
  146. {
  147. if (password != null)
  148. {
  149. PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
  150. password, wrongPkcs12Zero, encPrivKeyInfo);
  151. LoadKeyBag(privInfo, bagAttributes);
  152. }
  153. }
  154. public void Load(Stream input, char[] password)
  155. {
  156. if (input == null)
  157. throw new ArgumentNullException("input");
  158. Pfx bag = Pfx.GetInstance(Asn1Object.FromStream(input));
  159. ContentInfo info = bag.AuthSafe;
  160. bool wrongPkcs12Zero = false;
  161. if (bag.MacData != null) // check the mac code
  162. {
  163. if (password == null)
  164. throw new ArgumentNullException("password", "no password supplied when one expected");
  165. MacData mData = bag.MacData;
  166. DigestInfo dInfo = mData.Mac;
  167. AlgorithmIdentifier algId = dInfo.AlgorithmID;
  168. byte[] salt = mData.GetSalt();
  169. int itCount = mData.IterationCount.IntValue;
  170. byte[] data = Asn1OctetString.GetInstance(info.Content).GetOctets();
  171. byte[] mac = CalculatePbeMac(algId.Algorithm, salt, itCount, password, false, data);
  172. byte[] dig = dInfo.GetDigest();
  173. if (!Arrays.ConstantTimeAreEqual(mac, dig))
  174. {
  175. if (password.Length > 0)
  176. throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
  177. // Try with incorrect zero length password
  178. mac = CalculatePbeMac(algId.Algorithm, salt, itCount, password, true, data);
  179. if (!Arrays.ConstantTimeAreEqual(mac, dig))
  180. throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
  181. wrongPkcs12Zero = true;
  182. }
  183. }
  184. else if (password != null)
  185. {
  186. string ignoreProperty = Org.BouncyCastle.Utilities.Platform.GetEnvironmentVariable(IgnoreUselessPasswordProperty);
  187. bool ignore = ignoreProperty != null && Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase("true", ignoreProperty);
  188. if (!ignore)
  189. {
  190. throw new IOException("password supplied for keystore that does not require one");
  191. }
  192. }
  193. m_keys.Clear();
  194. m_localIds.Clear();
  195. unmarkedKeyEntry = null;
  196. var certBags = new List<SafeBag>();
  197. if (info.ContentType.Equals(PkcsObjectIdentifiers.Data))
  198. {
  199. Asn1OctetString content = Asn1OctetString.GetInstance(info.Content);
  200. AuthenticatedSafe authSafe = AuthenticatedSafe.GetInstance(content.GetOctets());
  201. ContentInfo[] cis = authSafe.GetContentInfo();
  202. foreach (ContentInfo ci in cis)
  203. {
  204. DerObjectIdentifier oid = ci.ContentType;
  205. byte[] octets = null;
  206. if (oid.Equals(PkcsObjectIdentifiers.Data))
  207. {
  208. octets = Asn1OctetString.GetInstance(ci.Content).GetOctets();
  209. }
  210. else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData))
  211. {
  212. if (password != null)
  213. {
  214. EncryptedData d = EncryptedData.GetInstance(ci.Content);
  215. octets = CryptPbeData(false, d.EncryptionAlgorithm,
  216. password, wrongPkcs12Zero, d.Content.GetOctets());
  217. }
  218. }
  219. else
  220. {
  221. // TODO Other data types
  222. }
  223. if (octets != null)
  224. {
  225. Asn1Sequence seq = Asn1Sequence.GetInstance(octets);
  226. foreach (Asn1Sequence subSeq in seq)
  227. {
  228. SafeBag b = SafeBag.GetInstance(subSeq);
  229. if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
  230. {
  231. certBags.Add(b);
  232. }
  233. else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
  234. {
  235. LoadPkcs8ShroudedKeyBag(EncryptedPrivateKeyInfo.GetInstance(b.BagValue),
  236. b.BagAttributes, password, wrongPkcs12Zero);
  237. }
  238. else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag))
  239. {
  240. LoadKeyBag(PrivateKeyInfo.GetInstance(b.BagValue), b.BagAttributes);
  241. }
  242. else
  243. {
  244. // TODO Other bag types
  245. }
  246. }
  247. }
  248. }
  249. }
  250. m_certs.Clear();
  251. m_chainCerts.Clear();
  252. m_keyCerts.Clear();
  253. foreach (SafeBag b in certBags)
  254. {
  255. CertBag certBag = CertBag.GetInstance(b.BagValue);
  256. byte[] octets = ((Asn1OctetString)certBag.CertValue).GetOctets();
  257. X509Certificate cert = new X509CertificateParser().ReadCertificate(octets);
  258. //
  259. // set the attributes
  260. //
  261. var attributes = new Dictionary<DerObjectIdentifier, Asn1Encodable>();
  262. Asn1OctetString localId = null;
  263. string alias = null;
  264. if (b.BagAttributes != null)
  265. {
  266. foreach (Asn1Sequence sq in b.BagAttributes)
  267. {
  268. DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]);
  269. Asn1Set attrSet = Asn1Set.GetInstance(sq[1]);
  270. if (attrSet.Count > 0)
  271. {
  272. // TODO We should be adding all attributes in the set
  273. Asn1Encodable attr = attrSet[0];
  274. // TODO We might want to "merge" attribute sets with
  275. // the same OID - currently, differing values give an error
  276. if (attributes.TryGetValue(aOid, out var attributeValue))
  277. {
  278. // we've found more than one - one might be incorrect
  279. if (PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Equals(aOid))
  280. {
  281. string id = Hex.ToHexString(Asn1OctetString.GetInstance(attr).GetOctets());
  282. if (!m_keys.ContainsKey(id) && !m_localIds.ContainsKey(id))
  283. continue; // ignore this one - it's not valid
  284. }
  285. // OK, but the value has to be the same
  286. if (!attributeValue.Equals(attr))
  287. {
  288. throw new IOException("attempt to add existing attribute with different value");
  289. }
  290. }
  291. else
  292. {
  293. attributes[aOid] = attr;
  294. }
  295. if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
  296. {
  297. alias = ((DerBmpString)attr).GetString();
  298. }
  299. else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
  300. {
  301. localId = (Asn1OctetString)attr;
  302. }
  303. }
  304. }
  305. }
  306. CertId certId = new CertId(cert.GetPublicKey());
  307. X509CertificateEntry certEntry = new X509CertificateEntry(cert, attributes);
  308. m_chainCerts[certId] = certEntry;
  309. if (unmarkedKeyEntry != null)
  310. {
  311. if (m_keyCerts.Count == 0)
  312. {
  313. string name = Hex.ToHexString(certId.Id);
  314. m_keyCerts[name] = certEntry;
  315. m_keys[name] = unmarkedKeyEntry;
  316. }
  317. else
  318. {
  319. m_keys["unmarked"] = unmarkedKeyEntry;
  320. }
  321. }
  322. else
  323. {
  324. if (localId != null)
  325. {
  326. string name = Hex.ToHexString(localId.GetOctets());
  327. m_keyCerts[name] = certEntry;
  328. }
  329. if (alias != null)
  330. {
  331. // TODO There may have been more than one alias
  332. m_certs[alias] = certEntry;
  333. }
  334. }
  335. }
  336. }
  337. public AsymmetricKeyEntry GetKey(string alias)
  338. {
  339. if (alias == null)
  340. throw new ArgumentNullException(nameof(alias));
  341. return CollectionUtilities.GetValueOrNull(m_keys, alias);
  342. }
  343. public bool IsCertificateEntry(string alias)
  344. {
  345. if (alias == null)
  346. throw new ArgumentNullException(nameof(alias));
  347. return m_certs.ContainsKey(alias) && !m_keys.ContainsKey(alias);
  348. }
  349. public bool IsKeyEntry(string alias)
  350. {
  351. if (alias == null)
  352. throw new ArgumentNullException(nameof(alias));
  353. return m_keys.ContainsKey(alias);
  354. }
  355. public IEnumerable<string> Aliases
  356. {
  357. get
  358. {
  359. var aliases = new HashSet<string>(m_certs.Keys);
  360. aliases.UnionWith(m_keys.Keys);
  361. return CollectionUtilities.Proxy(aliases);
  362. }
  363. }
  364. public bool ContainsAlias(string alias)
  365. {
  366. if (alias == null)
  367. throw new ArgumentNullException(nameof(alias));
  368. return m_certs.ContainsKey(alias) || m_keys.ContainsKey(alias);
  369. }
  370. /**
  371. * simply return the cert entry for the private key
  372. */
  373. public X509CertificateEntry GetCertificate(string alias)
  374. {
  375. if (alias == null)
  376. throw new ArgumentNullException(nameof(alias));
  377. if (m_certs.TryGetValue(alias, out var cert))
  378. return cert;
  379. var keyCertKey = alias;
  380. if (m_localIds.TryGetValue(alias, out var localId))
  381. {
  382. keyCertKey = localId;
  383. }
  384. return CollectionUtilities.GetValueOrNull(m_keyCerts, keyCertKey);
  385. }
  386. public string GetCertificateAlias(X509Certificate cert)
  387. {
  388. if (cert == null)
  389. throw new ArgumentNullException(nameof(cert));
  390. foreach (var entry in m_certs)
  391. {
  392. if (entry.Value.Certificate.Equals(cert))
  393. return entry.Key;
  394. }
  395. foreach (var entry in m_keyCerts)
  396. {
  397. if (entry.Value.Certificate.Equals(cert))
  398. return entry.Key;
  399. }
  400. return null;
  401. }
  402. public X509CertificateEntry[] GetCertificateChain(string alias)
  403. {
  404. if (alias == null)
  405. throw new ArgumentNullException(nameof(alias));
  406. if (!IsKeyEntry(alias))
  407. return null;
  408. X509CertificateEntry c = GetCertificate(alias);
  409. if (c == null)
  410. return null;
  411. var cs = new List<X509CertificateEntry>();
  412. while (c != null)
  413. {
  414. X509Certificate x509c = c.Certificate;
  415. X509CertificateEntry nextC = null;
  416. Asn1OctetString akiValue = x509c.GetExtensionValue(X509Extensions.AuthorityKeyIdentifier);
  417. if (akiValue != null)
  418. {
  419. AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.GetInstance(akiValue.GetOctets());
  420. byte[] keyID = aki.GetKeyIdentifier();
  421. if (keyID != null)
  422. {
  423. nextC = CollectionUtilities.GetValueOrNull(m_chainCerts, new CertId(keyID));
  424. }
  425. }
  426. if (nextC == null)
  427. {
  428. //
  429. // no authority key id, try the Issuer DN
  430. //
  431. X509Name i = x509c.IssuerDN;
  432. X509Name s = x509c.SubjectDN;
  433. if (!i.Equivalent(s))
  434. {
  435. foreach (var entry in m_chainCerts)
  436. {
  437. X509Certificate cert = entry.Value.Certificate;
  438. if (cert.SubjectDN.Equivalent(i))
  439. {
  440. try
  441. {
  442. x509c.Verify(cert.GetPublicKey());
  443. nextC = entry.Value;
  444. break;
  445. }
  446. catch (InvalidKeyException)
  447. {
  448. // TODO What if it doesn't verify?
  449. }
  450. }
  451. }
  452. }
  453. }
  454. cs.Add(c);
  455. if (nextC != c) // self signed - end of the chain
  456. {
  457. c = nextC;
  458. }
  459. else
  460. {
  461. c = null;
  462. }
  463. }
  464. return cs.ToArray();
  465. }
  466. public void SetCertificateEntry(string alias, X509CertificateEntry certEntry)
  467. {
  468. if (alias == null)
  469. throw new ArgumentNullException(nameof(alias));
  470. if (certEntry == null)
  471. throw new ArgumentNullException(nameof(certEntry));
  472. if (m_keys.ContainsKey(alias))
  473. throw new ArgumentException("There is a key entry with the name " + alias + ".");
  474. m_certs[alias] = certEntry;
  475. m_chainCerts[new CertId(certEntry.Certificate.GetPublicKey())] = certEntry;
  476. }
  477. public void SetKeyEntry(string alias, AsymmetricKeyEntry keyEntry, X509CertificateEntry[] chain)
  478. {
  479. if (alias == null)
  480. throw new ArgumentNullException(nameof(alias));
  481. if (keyEntry == null)
  482. throw new ArgumentNullException(nameof(keyEntry));
  483. if (keyEntry.Key.IsPrivate && chain == null)
  484. throw new ArgumentException("No certificate chain for private key");
  485. if (m_keys.ContainsKey(alias))
  486. {
  487. DeleteEntry(alias);
  488. }
  489. m_keys[alias] = keyEntry;
  490. m_certs[alias] = chain[0];
  491. for (int i = 0; i != chain.Length; i++)
  492. {
  493. m_chainCerts[new CertId(chain[i].Certificate.GetPublicKey())] = chain[i];
  494. }
  495. }
  496. public void DeleteEntry(string alias)
  497. {
  498. if (alias == null)
  499. throw new ArgumentNullException(nameof(alias));
  500. if (CollectionUtilities.Remove(m_certs, alias, out var cert))
  501. {
  502. m_chainCerts.Remove(new CertId(cert.Certificate.GetPublicKey()));
  503. }
  504. if (m_keys.Remove(alias))
  505. {
  506. if (CollectionUtilities.Remove(m_localIds, alias, out var id))
  507. {
  508. if (CollectionUtilities.Remove(m_keyCerts, id, out var keyCert))
  509. {
  510. m_chainCerts.Remove(new CertId(keyCert.Certificate.GetPublicKey()));
  511. }
  512. }
  513. }
  514. }
  515. public bool IsEntryOfType(string alias, Type entryType)
  516. {
  517. if (entryType == typeof(X509CertificateEntry))
  518. return IsCertificateEntry(alias);
  519. if (entryType == typeof(AsymmetricKeyEntry))
  520. return IsKeyEntry(alias) && GetCertificate(alias) != null;
  521. return false;
  522. }
  523. public int Count
  524. {
  525. get
  526. {
  527. int count = m_certs.Count;
  528. foreach (var key in m_keys.Keys)
  529. {
  530. if (!m_certs.ContainsKey(key))
  531. {
  532. ++count;
  533. }
  534. }
  535. return count;
  536. }
  537. }
  538. public void Save(Stream stream, char[] password, SecureRandom random)
  539. {
  540. if (stream == null)
  541. throw new ArgumentNullException(nameof(stream));
  542. if (random == null)
  543. throw new ArgumentNullException(nameof(random));
  544. //
  545. // handle the keys
  546. //
  547. Asn1EncodableVector keyBags = new Asn1EncodableVector();
  548. foreach (var keyEntry in m_keys)
  549. {
  550. var name = keyEntry.Key;
  551. var privKey = keyEntry.Value;
  552. byte[] kSalt = new byte[SaltSize];
  553. random.NextBytes(kSalt);
  554. DerObjectIdentifier bagOid;
  555. Asn1Encodable bagData;
  556. if (password == null)
  557. {
  558. bagOid = PkcsObjectIdentifiers.KeyBag;
  559. bagData = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey.Key);
  560. }
  561. else
  562. {
  563. bagOid = PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag;
  564. if (keyPrfAlgorithm != null)
  565. {
  566. bagData = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(keyAlgorithm,
  567. keyPrfAlgorithm, password, kSalt, MinIterations, random, privKey.Key);
  568. }
  569. else
  570. {
  571. bagData = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(keyAlgorithm, password,
  572. kSalt, MinIterations, privKey.Key);
  573. }
  574. }
  575. Asn1EncodableVector kName = new Asn1EncodableVector();
  576. foreach (var oid in privKey.BagAttributeKeys)
  577. {
  578. // NB: Ignore any existing FriendlyName
  579. if (!PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Equals(oid))
  580. {
  581. kName.Add(new DerSequence(oid, new DerSet(privKey[oid])));
  582. }
  583. }
  584. //
  585. // make sure we are using the local alias on store
  586. //
  587. // NB: We always set the FriendlyName based on 'name'
  588. //if (privKey[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
  589. {
  590. kName.Add(
  591. new DerSequence(
  592. PkcsObjectIdentifiers.Pkcs9AtFriendlyName,
  593. new DerSet(new DerBmpString(name))));
  594. }
  595. //
  596. // make sure we have a local key-id
  597. //
  598. if (privKey[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null)
  599. {
  600. X509CertificateEntry ct = GetCertificate(name);
  601. AsymmetricKeyParameter pubKey = ct.Certificate.GetPublicKey();
  602. SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey);
  603. kName.Add(
  604. new DerSequence(
  605. PkcsObjectIdentifiers.Pkcs9AtLocalKeyID,
  606. new DerSet(subjectKeyID)));
  607. }
  608. keyBags.Add(new SafeBag(bagOid, bagData.ToAsn1Object(), new DerSet(kName)));
  609. }
  610. byte[] keyBagsEncoding = new DerSequence(keyBags).GetDerEncoded();
  611. ContentInfo keysInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(keyBagsEncoding));
  612. //
  613. // certificate processing
  614. //
  615. byte[] cSalt = new byte[SaltSize];
  616. random.NextBytes(cSalt);
  617. Asn1EncodableVector certBags = new Asn1EncodableVector();
  618. Pkcs12PbeParams cParams = new Pkcs12PbeParams(cSalt, MinIterations);
  619. AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object());
  620. var doneCerts = new HashSet<X509Certificate>();
  621. foreach (string name in m_keys.Keys)
  622. {
  623. X509CertificateEntry certEntry = GetCertificate(name);
  624. CertBag cBag = new CertBag(
  625. PkcsObjectIdentifiers.X509Certificate,
  626. new DerOctetString(certEntry.Certificate.GetEncoded()));
  627. Asn1EncodableVector fName = new Asn1EncodableVector();
  628. foreach (var oid in certEntry.BagAttributeKeys)
  629. {
  630. // NB: Ignore any existing FriendlyName
  631. if (!PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Equals(oid))
  632. {
  633. fName.Add(new DerSequence(oid, new DerSet(certEntry[oid])));
  634. }
  635. }
  636. //
  637. // make sure we are using the local alias on store
  638. //
  639. // NB: We always set the FriendlyName based on 'name'
  640. //if (certEntry[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
  641. {
  642. fName.Add(
  643. new DerSequence(
  644. PkcsObjectIdentifiers.Pkcs9AtFriendlyName,
  645. new DerSet(new DerBmpString(name))));
  646. }
  647. //
  648. // make sure we have a local key-id
  649. //
  650. if (certEntry[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null)
  651. {
  652. AsymmetricKeyParameter pubKey = certEntry.Certificate.GetPublicKey();
  653. SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey);
  654. fName.Add(
  655. new DerSequence(
  656. PkcsObjectIdentifiers.Pkcs9AtLocalKeyID,
  657. new DerSet(subjectKeyID)));
  658. }
  659. certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
  660. doneCerts.Add(certEntry.Certificate);
  661. }
  662. foreach (var certEntry in m_certs)
  663. {
  664. var certId = certEntry.Key;
  665. var cert = certEntry.Value;
  666. if (m_keys.ContainsKey(certId))
  667. continue;
  668. CertBag cBag = new CertBag(
  669. PkcsObjectIdentifiers.X509Certificate,
  670. new DerOctetString(cert.Certificate.GetEncoded()));
  671. Asn1EncodableVector fName = new Asn1EncodableVector();
  672. foreach (var oid in cert.BagAttributeKeys)
  673. {
  674. // a certificate not immediately linked to a key doesn't require
  675. // a localKeyID and will confuse some PKCS12 implementations.
  676. //
  677. // If we find one, we'll prune it out.
  678. if (PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Equals(oid))
  679. continue;
  680. // NB: Ignore any existing FriendlyName
  681. if (!PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Equals(oid))
  682. {
  683. fName.Add(new DerSequence(oid, new DerSet(cert[oid])));
  684. }
  685. }
  686. //
  687. // make sure we are using the local alias on store
  688. //
  689. // NB: We always set the FriendlyName based on 'certId'
  690. //if (cert[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
  691. {
  692. fName.Add(
  693. new DerSequence(
  694. PkcsObjectIdentifiers.Pkcs9AtFriendlyName,
  695. new DerSet(new DerBmpString(certId))));
  696. }
  697. // the Oracle PKCS12 parser looks for a trusted key usage for named certificates as well
  698. if (cert[MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage] == null)
  699. {
  700. Asn1OctetString ext = cert.Certificate.GetExtensionValue(X509Extensions.ExtendedKeyUsage);
  701. if (ext != null)
  702. {
  703. ExtendedKeyUsage usage = ExtendedKeyUsage.GetInstance(ext.GetOctets());
  704. Asn1EncodableVector v = new Asn1EncodableVector();
  705. IList<DerObjectIdentifier> usages = usage.GetAllUsages();
  706. for (int i = 0; i != usages.Count; i++)
  707. {
  708. v.Add(usages[i]);
  709. }
  710. fName.Add(
  711. new DerSequence(
  712. MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage,
  713. new DerSet(v)));
  714. }
  715. else
  716. {
  717. fName.Add(
  718. new DerSequence(
  719. MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage,
  720. new DerSet(KeyPurposeID.AnyExtendedKeyUsage)));
  721. }
  722. }
  723. certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
  724. doneCerts.Add(cert.Certificate);
  725. }
  726. foreach (var chainCertEntry in m_chainCerts)
  727. {
  728. var certId = chainCertEntry.Key;
  729. var cert = chainCertEntry.Value;
  730. if (doneCerts.Contains(cert.Certificate))
  731. continue;
  732. CertBag cBag = new CertBag(
  733. PkcsObjectIdentifiers.X509Certificate,
  734. new DerOctetString(cert.Certificate.GetEncoded()));
  735. Asn1EncodableVector fName = new Asn1EncodableVector();
  736. foreach (var oid in cert.BagAttributeKeys)
  737. {
  738. // a certificate not immediately linked to a key doesn't require
  739. // a localKeyID and will confuse some PKCS12 implementations.
  740. //
  741. // If we find one, we'll prune it out.
  742. if (PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Equals(oid))
  743. continue;
  744. fName.Add(new DerSequence(oid, new DerSet(cert[oid])));
  745. }
  746. certBags.Add(new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)));
  747. }
  748. byte[] certBagsEncoding = new DerSequence(certBags).GetDerEncoded();
  749. ContentInfo certsInfo;
  750. if (password == null || certAlgorithm == null)
  751. {
  752. certsInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(certBagsEncoding));
  753. }
  754. else
  755. {
  756. byte[] certBytes = CryptPbeData(true, cAlgId, password, false, certBagsEncoding);
  757. EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes));
  758. certsInfo = new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object());
  759. }
  760. ContentInfo[] info = new ContentInfo[]{ keysInfo, certsInfo };
  761. byte[] data = new AuthenticatedSafe(info).GetEncoded(
  762. useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);
  763. ContentInfo mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data));
  764. //
  765. // create the mac
  766. //
  767. MacData macData = null;
  768. if (password != null)
  769. {
  770. byte[] mSalt = new byte[20];
  771. random.NextBytes(mSalt);
  772. byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1,
  773. mSalt, MinIterations, password, false, data);
  774. AlgorithmIdentifier algId = new AlgorithmIdentifier(
  775. OiwObjectIdentifiers.IdSha1, DerNull.Instance);
  776. DigestInfo dInfo = new DigestInfo(algId, mac);
  777. macData = new MacData(dInfo, mSalt, MinIterations);
  778. }
  779. //
  780. // output the Pfx
  781. //
  782. Pfx pfx = new Pfx(mainInfo, macData);
  783. pfx.EncodeTo(stream, useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);
  784. }
  785. internal static byte[] CalculatePbeMac(
  786. DerObjectIdentifier oid,
  787. byte[] salt,
  788. int itCount,
  789. char[] password,
  790. bool wrongPkcs12Zero,
  791. byte[] data)
  792. {
  793. Asn1Encodable asn1Params = PbeUtilities.GenerateAlgorithmParameters(
  794. oid, salt, itCount);
  795. ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(
  796. oid, password, wrongPkcs12Zero, asn1Params);
  797. IMac mac = (IMac) PbeUtilities.CreateEngine(oid);
  798. mac.Init(cipherParams);
  799. return MacUtilities.DoFinal(mac, data);
  800. }
  801. private static byte[] CryptPbeData(
  802. bool forEncryption,
  803. AlgorithmIdentifier algId,
  804. char[] password,
  805. bool wrongPkcs12Zero,
  806. byte[] data)
  807. {
  808. IBufferedCipher cipher = PbeUtilities.CreateEngine(algId) as IBufferedCipher;
  809. if (cipher == null)
  810. throw new Exception("Unknown encryption algorithm: " + algId.Algorithm);
  811. if (algId.Algorithm.Equals(PkcsObjectIdentifiers.IdPbeS2))
  812. {
  813. PbeS2Parameters pbeParameters = PbeS2Parameters.GetInstance(algId.Parameters);
  814. ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(
  815. algId.Algorithm, password, pbeParameters);
  816. cipher.Init(forEncryption, cipherParams);
  817. return cipher.DoFinal(data);
  818. }
  819. else
  820. {
  821. Pkcs12PbeParams pbeParameters = Pkcs12PbeParams.GetInstance(algId.Parameters);
  822. ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(
  823. algId.Algorithm, password, wrongPkcs12Zero, pbeParameters);
  824. cipher.Init(forEncryption, cipherParams);
  825. return cipher.DoFinal(data);
  826. }
  827. }
  828. }
  829. }
  830. #pragma warning restore
  831. #endif