Pkcs12Store.cs 39 KB

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