|
25 | 25 | //
|
26 | 26 |
|
27 | 27 | using System.Text;
|
| 28 | +using System.Globalization; |
28 | 29 |
|
29 | 30 | using Org.BouncyCastle.Asn1;
|
| 31 | +using Org.BouncyCastle.Asn1.Nist; |
| 32 | +using Org.BouncyCastle.Asn1.Ntt; |
| 33 | +using Org.BouncyCastle.Asn1.Misc; |
| 34 | +using Org.BouncyCastle.Asn1.Oiw; |
| 35 | +using Org.BouncyCastle.Asn1.Pkcs; |
| 36 | +using Org.BouncyCastle.Asn1.Kisa; |
| 37 | +using Org.BouncyCastle.Asn1.Smime; |
30 | 38 | using Org.BouncyCastle.Asn1.X509;
|
| 39 | +using Org.BouncyCastle.Asn1.X9; |
31 | 40 | using Org.BouncyCastle.Crypto;
|
32 | 41 | using Org.BouncyCastle.Crypto.Digests;
|
33 | 42 | using Org.BouncyCastle.Crypto.Generators;
|
|
41 | 50 | using Org.BouncyCastle.Utilities;
|
42 | 51 | using Org.BouncyCastle.X509;
|
43 | 52 | using Org.BouncyCastle.X509.Extension;
|
44 |
| -using Org.BouncyCastle.Asn1.X9; |
45 | 53 |
|
46 | 54 | using MimeKit;
|
47 | 55 |
|
48 | 56 | namespace UnitTests.Cryptography {
|
49 | 57 | class X509CertificateGenerator
|
50 | 58 | {
|
| 59 | + static readonly Dictionary<string, DerObjectIdentifier> SMimeCapabilityMapping; |
51 | 60 | static readonly Dictionary<string, DerObjectIdentifier> X509NameOidMapping;
|
52 | 61 | static readonly Dictionary<string, int> X509SubjectAlternativeTagMapping;
|
53 | 62 | static readonly char[] EqualSign = new char[] { '=' };
|
@@ -102,6 +111,21 @@ static X509CertificateGenerator ()
|
102 | 111 | { "Rfc822Name", GeneralName.Rfc822Name },
|
103 | 112 | { "DnsName", GeneralName.DnsName },
|
104 | 113 | };
|
| 114 | + |
| 115 | + SMimeCapabilityMapping = new Dictionary<string, DerObjectIdentifier> (StringComparer.OrdinalIgnoreCase) { |
| 116 | + { "AES128", NistObjectIdentifiers.IdAes128Cbc }, |
| 117 | + { "AES192", NistObjectIdentifiers.IdAes192Cbc }, |
| 118 | + { "AES256", NistObjectIdentifiers.IdAes256Cbc }, |
| 119 | + { "CAMELLIA128", NttObjectIdentifiers.IdCamellia128Cbc }, |
| 120 | + { "CAMELLIA192", NttObjectIdentifiers.IdCamellia192Cbc }, |
| 121 | + { "CAMELLIA256", NttObjectIdentifiers.IdCamellia256Cbc }, |
| 122 | + { "CAST5", MiscObjectIdentifiers.cast5CBC }, |
| 123 | + { "DES", OiwObjectIdentifiers.DesCbc }, |
| 124 | + { "3DES", PkcsObjectIdentifiers.DesEde3Cbc }, |
| 125 | + { "IDEA", MiscObjectIdentifiers.as_sys_sec_alg_ideaCBC }, |
| 126 | + { "RC2", PkcsObjectIdentifiers.RC2Cbc }, |
| 127 | + { "SEED", KisaObjectIdentifiers.IdSeedCbc } |
| 128 | + }; |
105 | 129 | }
|
106 | 130 |
|
107 | 131 | static AsymmetricCipherKeyPair LoadAsymmetricCipherKeyPair (string fileName)
|
@@ -218,6 +242,8 @@ public CertificateOptions ()
|
218 | 242 |
|
219 | 243 | internal List<GeneralName> SubjectAlternativeNames { get; private set; }
|
220 | 244 |
|
| 245 | + internal SmimeCapabilityVector SMimeCapabilities { get; private set; } |
| 246 | + |
221 | 247 | public void Add (DerObjectIdentifier oid, string value)
|
222 | 248 | {
|
223 | 249 | if (oid == X509Name.CN || oid == X509Name.E)
|
@@ -253,6 +279,26 @@ public void AddSubjectAlternativeName (string property, string value)
|
253 | 279 | break;
|
254 | 280 | }
|
255 | 281 | }
|
| 282 | + |
| 283 | + public void AddSMimeCapability (string capability) |
| 284 | + { |
| 285 | + var components = capability.Split (new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries); |
| 286 | + DerObjectIdentifier oid; |
| 287 | + |
| 288 | + if (SMimeCapabilityMapping.TryGetValue (components[0], out oid) || DerObjectIdentifier.TryFromID (components[0], out oid)) { |
| 289 | + SMimeCapabilities ??= new SmimeCapabilityVector (); |
| 290 | + |
| 291 | + if (components.Length > 1) { |
| 292 | + int value = int.Parse (components[1], NumberStyles.None, CultureInfo.InvariantCulture); |
| 293 | + |
| 294 | + SMimeCapabilities.AddCapability (oid, value); |
| 295 | + } else { |
| 296 | + SMimeCapabilities.AddCapability (oid); |
| 297 | + } |
| 298 | + } else { |
| 299 | + throw new ArgumentException ($"Unknown S/MIME capability: {capability}", nameof (capability)); |
| 300 | + } |
| 301 | + } |
256 | 302 | }
|
257 | 303 |
|
258 | 304 | public sealed class GeneratorOptions
|
@@ -409,6 +455,11 @@ public static X509Certificate[] Generate (GeneratorOptions options, PrivateKeyOp
|
409 | 455 | generator.AddExtension (X509Extensions.SubjectAlternativeName, false, altNames);
|
410 | 456 | }
|
411 | 457 |
|
| 458 | + if (certificateOptions.SMimeCapabilities != null) { |
| 459 | + var capabilities = certificateOptions.SMimeCapabilities.ToAsn1EncodableVector (); |
| 460 | + generator.AddExtension (SmimeAttributes.SmimeCapabilities, false, new DerSequence (capabilities)); |
| 461 | + } |
| 462 | + |
412 | 463 | if (issuerCertificate != null)
|
413 | 464 | generator.AddExtension (X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure (issuerCertificate));
|
414 | 465 |
|
@@ -554,6 +605,13 @@ public static X509Certificate[] Generate (string cfg)
|
554 | 605 | throw new FormatException ($"Unknown [SubjectAlternativeNames] property: {property}");
|
555 | 606 | }
|
556 | 607 | break;
|
| 608 | + case "smimecapabilities": |
| 609 | + try { |
| 610 | + certificate.AddSMimeCapability (value); |
| 611 | + } catch (ArgumentException) { |
| 612 | + throw new FormatException ($"Unknown [SMimeCapabilities] value: {value}"); |
| 613 | + } |
| 614 | + break; |
557 | 615 | case "generator":
|
558 | 616 | switch (property.ToLowerInvariant ()) {
|
559 | 617 | case "basicconstraints":
|
|
0 commit comments