Pkcs12Store.cs 42 KB

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