PEMReader.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Text;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Nist;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Pkcs;
  11. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Sec;
  12. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.TeleTrust;
  13. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
  14. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X9;
  15. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  16. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.EC;
  17. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators;
  18. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  19. using BestHTTP.SecureProtocol.Org.BouncyCastle.Pkcs;
  20. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  21. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  22. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Encoders;
  23. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO.Pem;
  24. using BestHTTP.SecureProtocol.Org.BouncyCastle.X509;
  25. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.OpenSsl
  26. {
  27. /**
  28. * Class for reading OpenSSL PEM encoded streams containing
  29. * X509 certificates, PKCS8 encoded keys and PKCS7 objects.
  30. * <p>
  31. * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Keys and
  32. * Certificates will be returned using the appropriate java.security type.</p>
  33. */
  34. public class PemReader
  35. : BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO.Pem.PemReader
  36. {
  37. // private static readonly IDictionary parsers = new Hashtable();
  38. static PemReader()
  39. {
  40. // parsers.Add("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
  41. // parsers.Add("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
  42. // parsers.Add("CERTIFICATE", new X509CertificateParser(provider));
  43. // parsers.Add("X509 CERTIFICATE", new X509CertificateParser(provider));
  44. // parsers.Add("X509 CRL", new X509CRLParser(provider));
  45. // parsers.Add("PKCS7", new PKCS7Parser());
  46. // parsers.Add("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser());
  47. // parsers.Add("EC PARAMETERS", new ECNamedCurveSpecParser());
  48. // parsers.Add("PUBLIC KEY", new PublicKeyParser(provider));
  49. // parsers.Add("RSA PUBLIC KEY", new RSAPublicKeyParser(provider));
  50. // parsers.Add("RSA PRIVATE KEY", new RSAKeyPairParser(provider));
  51. // parsers.Add("DSA PRIVATE KEY", new DSAKeyPairParser(provider));
  52. // parsers.Add("EC PRIVATE KEY", new ECDSAKeyPairParser(provider));
  53. // parsers.Add("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser(provider));
  54. // parsers.Add("PRIVATE KEY", new PrivateKeyParser(provider));
  55. }
  56. private readonly IPasswordFinder pFinder;
  57. /**
  58. * Create a new PemReader
  59. *
  60. * @param reader the Reader
  61. */
  62. public PemReader(
  63. TextReader reader)
  64. : this(reader, null)
  65. {
  66. }
  67. /**
  68. * Create a new PemReader with a password finder
  69. *
  70. * @param reader the Reader
  71. * @param pFinder the password finder
  72. */
  73. public PemReader(
  74. TextReader reader,
  75. IPasswordFinder pFinder)
  76. : base(reader)
  77. {
  78. this.pFinder = pFinder;
  79. }
  80. public object ReadObject()
  81. {
  82. PemObject obj = ReadPemObject();
  83. if (obj == null)
  84. return null;
  85. // TODO Follow Java build and map to parser objects?
  86. // if (parsers.Contains(obj.Type))
  87. // return ((PemObjectParser)parsers[obj.Type]).ParseObject(obj);
  88. if (BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.EndsWith(obj.Type, "PRIVATE KEY"))
  89. return ReadPrivateKey(obj);
  90. switch (obj.Type)
  91. {
  92. case "PUBLIC KEY":
  93. return ReadPublicKey(obj);
  94. case "RSA PUBLIC KEY":
  95. return ReadRsaPublicKey(obj);
  96. case "CERTIFICATE REQUEST":
  97. case "NEW CERTIFICATE REQUEST":
  98. return ReadCertificateRequest(obj);
  99. case "CERTIFICATE":
  100. case "X509 CERTIFICATE":
  101. return ReadCertificate(obj);
  102. case "PKCS7":
  103. case "CMS":
  104. return ReadPkcs7(obj);
  105. case "X509 CRL":
  106. return ReadCrl(obj);
  107. case "ATTRIBUTE CERTIFICATE":
  108. return ReadAttributeCertificate(obj);
  109. // TODO Add back in when tests done, and return type issue resolved
  110. //case "EC PARAMETERS":
  111. // return ReadECParameters(obj);
  112. default:
  113. throw new IOException("unrecognised object: " + obj.Type);
  114. }
  115. }
  116. private AsymmetricKeyParameter ReadRsaPublicKey(PemObject pemObject)
  117. {
  118. RsaPublicKeyStructure rsaPubStructure = RsaPublicKeyStructure.GetInstance(
  119. Asn1Object.FromByteArray(pemObject.Content));
  120. return new RsaKeyParameters(
  121. false, // not private
  122. rsaPubStructure.Modulus,
  123. rsaPubStructure.PublicExponent);
  124. }
  125. private AsymmetricKeyParameter ReadPublicKey(PemObject pemObject)
  126. {
  127. return PublicKeyFactory.CreateKey(pemObject.Content);
  128. }
  129. /**
  130. * Reads in a X509Certificate.
  131. *
  132. * @return the X509Certificate
  133. * @throws IOException if an I/O error occured
  134. */
  135. private X509Certificate ReadCertificate(PemObject pemObject)
  136. {
  137. try
  138. {
  139. return new X509CertificateParser().ReadCertificate(pemObject.Content);
  140. }
  141. catch (Exception e)
  142. {
  143. throw new PemException("problem parsing cert: " + e.ToString());
  144. }
  145. }
  146. /**
  147. * Reads in a X509CRL.
  148. *
  149. * @return the X509Certificate
  150. * @throws IOException if an I/O error occured
  151. */
  152. private X509Crl ReadCrl(PemObject pemObject)
  153. {
  154. try
  155. {
  156. return new X509CrlParser().ReadCrl(pemObject.Content);
  157. }
  158. catch (Exception e)
  159. {
  160. throw new PemException("problem parsing cert: " + e.ToString());
  161. }
  162. }
  163. /**
  164. * Reads in a PKCS10 certification request.
  165. *
  166. * @return the certificate request.
  167. * @throws IOException if an I/O error occured
  168. */
  169. private Pkcs10CertificationRequest ReadCertificateRequest(PemObject pemObject)
  170. {
  171. try
  172. {
  173. return new Pkcs10CertificationRequest(pemObject.Content);
  174. }
  175. catch (Exception e)
  176. {
  177. throw new PemException("problem parsing cert: " + e.ToString());
  178. }
  179. }
  180. /**
  181. * Reads in a X509 Attribute Certificate.
  182. *
  183. * @return the X509 Attribute Certificate
  184. * @throws IOException if an I/O error occured
  185. */
  186. private IX509AttributeCertificate ReadAttributeCertificate(PemObject pemObject)
  187. {
  188. return new X509V2AttributeCertificate(pemObject.Content);
  189. }
  190. /**
  191. * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS
  192. * API.
  193. *
  194. * @return the X509Certificate
  195. * @throws IOException if an I/O error occured
  196. */
  197. // TODO Consider returning Asn1.Pkcs.ContentInfo
  198. private Asn1.Cms.ContentInfo ReadPkcs7(PemObject pemObject)
  199. {
  200. try
  201. {
  202. return Asn1.Cms.ContentInfo.GetInstance(
  203. Asn1Object.FromByteArray(pemObject.Content));
  204. }
  205. catch (Exception e)
  206. {
  207. throw new PemException("problem parsing PKCS7 object: " + e.ToString());
  208. }
  209. }
  210. /**
  211. * Read a Key Pair
  212. */
  213. private object ReadPrivateKey(PemObject pemObject)
  214. {
  215. //
  216. // extract the key
  217. //
  218. Debug.Assert(BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.EndsWith(pemObject.Type, "PRIVATE KEY"));
  219. string type = pemObject.Type.Substring(0, pemObject.Type.Length - "PRIVATE KEY".Length).Trim();
  220. byte[] keyBytes = pemObject.Content;
  221. IDictionary fields = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
  222. foreach (PemHeader header in pemObject.Headers)
  223. {
  224. fields[header.Name] = header.Value;
  225. }
  226. string procType = (string) fields["Proc-Type"];
  227. if (procType == "4,ENCRYPTED")
  228. {
  229. if (pFinder == null)
  230. throw new PasswordException("No password finder specified, but a password is required");
  231. char[] password = pFinder.GetPassword();
  232. if (password == null)
  233. throw new PasswordException("Password is null, but a password is required");
  234. string dekInfo = (string) fields["DEK-Info"];
  235. string[] tknz = dekInfo.Split(',');
  236. string dekAlgName = tknz[0].Trim();
  237. byte[] iv = Hex.Decode(tknz[1].Trim());
  238. keyBytes = PemUtilities.Crypt(false, keyBytes, password, dekAlgName, iv);
  239. }
  240. try
  241. {
  242. AsymmetricKeyParameter pubSpec, privSpec;
  243. Asn1Sequence seq = Asn1Sequence.GetInstance(keyBytes);
  244. switch (type)
  245. {
  246. case "RSA":
  247. {
  248. if (seq.Count != 9)
  249. throw new PemException("malformed sequence in RSA private key");
  250. RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(seq);
  251. pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent);
  252. privSpec = new RsaPrivateCrtKeyParameters(
  253. rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent,
  254. rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2,
  255. rsa.Coefficient);
  256. break;
  257. }
  258. case "DSA":
  259. {
  260. if (seq.Count != 6)
  261. throw new PemException("malformed sequence in DSA private key");
  262. // TODO Create an ASN1 object somewhere for this?
  263. //DerInteger v = (DerInteger)seq[0];
  264. DerInteger p = (DerInteger)seq[1];
  265. DerInteger q = (DerInteger)seq[2];
  266. DerInteger g = (DerInteger)seq[3];
  267. DerInteger y = (DerInteger)seq[4];
  268. DerInteger x = (DerInteger)seq[5];
  269. DsaParameters parameters = new DsaParameters(p.Value, q.Value, g.Value);
  270. privSpec = new DsaPrivateKeyParameters(x.Value, parameters);
  271. pubSpec = new DsaPublicKeyParameters(y.Value, parameters);
  272. break;
  273. }
  274. case "EC":
  275. {
  276. ECPrivateKeyStructure pKey = ECPrivateKeyStructure.GetInstance(seq);
  277. AlgorithmIdentifier algId = new AlgorithmIdentifier(
  278. X9ObjectIdentifiers.IdECPublicKey, pKey.GetParameters());
  279. PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey.ToAsn1Object());
  280. // TODO Are the keys returned here ECDSA, as Java version forces?
  281. privSpec = PrivateKeyFactory.CreateKey(privInfo);
  282. DerBitString pubKey = pKey.GetPublicKey();
  283. if (pubKey != null)
  284. {
  285. SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pubKey.GetBytes());
  286. // TODO Are the keys returned here ECDSA, as Java version forces?
  287. pubSpec = PublicKeyFactory.CreateKey(pubInfo);
  288. }
  289. else
  290. {
  291. pubSpec = ECKeyPairGenerator.GetCorrespondingPublicKey(
  292. (ECPrivateKeyParameters)privSpec);
  293. }
  294. break;
  295. }
  296. case "ENCRYPTED":
  297. {
  298. char[] password = pFinder.GetPassword();
  299. if (password == null)
  300. throw new PasswordException("Password is null, but a password is required");
  301. return PrivateKeyFactory.DecryptKey(password, EncryptedPrivateKeyInfo.GetInstance(seq));
  302. }
  303. case "":
  304. {
  305. return PrivateKeyFactory.CreateKey(PrivateKeyInfo.GetInstance(seq));
  306. }
  307. default:
  308. throw new ArgumentException("Unknown key type: " + type, "type");
  309. }
  310. return new AsymmetricCipherKeyPair(pubSpec, privSpec);
  311. }
  312. catch (IOException e)
  313. {
  314. throw e;
  315. }
  316. catch (Exception e)
  317. {
  318. throw new PemException(
  319. "problem creating " + type + " private key: " + e.ToString());
  320. }
  321. }
  322. // TODO Add an equivalent class for ECNamedCurveParameterSpec?
  323. //private ECNamedCurveParameterSpec ReadECParameters(
  324. // private X9ECParameters ReadECParameters(PemObject pemObject)
  325. // {
  326. // DerObjectIdentifier oid = (DerObjectIdentifier)Asn1Object.FromByteArray(pemObject.Content);
  327. //
  328. // //return ECNamedCurveTable.getParameterSpec(oid.Id);
  329. // return GetCurveParameters(oid.Id);
  330. // }
  331. //private static ECDomainParameters GetCurveParameters(
  332. private static X9ECParameters GetCurveParameters(
  333. string name)
  334. {
  335. // TODO ECGost3410NamedCurves support (returns ECDomainParameters though)
  336. X9ECParameters ecP = CustomNamedCurves.GetByName(name);
  337. if (ecP == null)
  338. {
  339. ecP = ECNamedCurveTable.GetByName(name);
  340. }
  341. if (ecP == null)
  342. throw new Exception("unknown curve name: " + name);
  343. //return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
  344. return ecP;
  345. }
  346. }
  347. }
  348. #pragma warning restore
  349. #endif