2 // X509CertificateImplBtls.cs
5 // Martin Baulig <martin.baulig@xamarin.com>
7 // Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com)
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #if MONO_SECURITY_ALIAS
28 extern alias MonoSecurity;
31 #if MONO_SECURITY_ALIAS
32 using MX = MonoSecurity::Mono.Security.X509;
34 using MX = Mono.Security.X509;
39 using System.Collections;
40 using System.Security;
41 using System.Security.Cryptography;
42 using System.Security.Cryptography.X509Certificates;
43 using Mono.Security.Cryptography;
47 class X509CertificateImplBtls : X509Certificate2Impl
50 MonoBtlsKey privateKey;
51 X500DistinguishedName subjectName;
52 X500DistinguishedName issuerName;
53 X509CertificateImplCollection intermediateCerts;
56 bool disallowFallback;
58 internal X509CertificateImplBtls (bool disallowFallback = false)
60 this.disallowFallback = disallowFallback;
63 internal X509CertificateImplBtls (MonoBtlsX509 x509, bool disallowFallback = false)
65 this.disallowFallback = disallowFallback;
66 this.x509 = x509.Copy ();
69 X509CertificateImplBtls (X509CertificateImplBtls other)
71 disallowFallback = other.disallowFallback;
72 x509 = other.x509 != null ? other.x509.Copy () : null;
73 privateKey = other.privateKey != null ? other.privateKey.Copy () : null;
74 if (other.intermediateCerts != null)
75 intermediateCerts = other.intermediateCerts.Clone ();
78 internal X509CertificateImplBtls (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
80 this.disallowFallback = disallowFallback;
81 x509 = MonoBtlsX509.LoadFromData (data, format);
84 public override bool IsValid {
85 get { return x509 != null && x509.IsValid; }
88 public override IntPtr Handle {
89 get { return x509.Handle.DangerousGetHandle (); }
92 public override IntPtr GetNativeAppleCertificate ()
97 internal MonoBtlsX509 X509 {
99 ThrowIfContextInvalid ();
104 internal MonoBtlsKey NativePrivateKey {
106 ThrowIfContextInvalid ();
111 public override X509CertificateImpl Clone ()
113 ThrowIfContextInvalid ();
114 return new X509CertificateImplBtls (this);
117 public override bool Equals (X509CertificateImpl other, out bool result)
119 var otherBoringImpl = other as X509CertificateImplBtls;
120 if (otherBoringImpl == null) {
125 result = MonoBtlsX509.Compare (X509, otherBoringImpl.X509) == 0;
129 protected override byte[] GetCertHash (bool lazy)
131 return X509.GetCertHash ();
134 public override byte[] GetRawCertData ()
136 return X509.GetRawData (MonoBtlsX509Format.DER);
139 public override string GetSubjectName (bool legacyV1Mode)
142 return SubjectName.Decode (X500DistinguishedNameFlags.None);
143 return SubjectName.Name;
146 public override string GetIssuerName (bool legacyV1Mode)
149 return IssuerName.Decode (X500DistinguishedNameFlags.None);
150 return IssuerName.Name;
153 public override DateTime GetValidFrom ()
155 return X509.GetNotBefore ().ToLocalTime ();
158 public override DateTime GetValidUntil ()
160 return X509.GetNotAfter ().ToLocalTime ();
163 public override byte[] GetPublicKey ()
165 return X509.GetPublicKeyData ();
168 public override byte[] GetSerialNumber ()
170 return X509.GetSerialNumber (true);
173 public override string GetKeyAlgorithm ()
175 return PublicKey.Oid.Value;
178 public override byte[] GetKeyAlgorithmParameters ()
180 return PublicKey.EncodedParameters.RawData;
183 public override byte[] Export (X509ContentType contentType, byte[] password)
185 ThrowIfContextInvalid ();
187 switch (contentType) {
188 case X509ContentType.Cert:
189 return GetRawCertData ();
190 case X509ContentType.Pfx: // this includes Pkcs12
192 throw new NotSupportedException ();
193 case X509ContentType.SerializedCert:
195 throw new NotSupportedException ();
197 string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
198 throw new CryptographicException (msg);
202 internal override X509CertificateImplCollection IntermediateCertificates {
203 get { return intermediateCerts; }
206 public override string ToString (bool full)
208 ThrowIfContextInvalid ();
211 var summary = GetSubjectName (false);
212 return string.Format ("[X509Certificate: {0}]", summary);
215 string nl = Environment.NewLine;
216 StringBuilder sb = new StringBuilder ();
217 sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, GetSubjectName (false));
219 sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, GetIssuerName (false));
220 sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
221 sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
222 sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
225 return sb.ToString ();
228 protected override void Dispose (bool disposing)
236 #region X509Certificate2Impl
238 X509Certificate2Impl fallback;
242 if (disallowFallback)
243 throw new InvalidOperationException ();
244 if (fallback != null)
246 fallback = X509Helper2.Import (GetRawCertData (), null, X509KeyStorageFlags.DefaultKeySet, true);
249 internal override X509Certificate2Impl FallbackImpl {
257 public override bool Archived {
259 ThrowIfContextInvalid ();
263 ThrowIfContextInvalid ();
268 public override X509ExtensionCollection Extensions {
269 get { return FallbackImpl.Extensions; }
272 public override bool HasPrivateKey {
273 get { return privateKey != null; }
276 public override X500DistinguishedName IssuerName {
278 ThrowIfContextInvalid ();
279 if (issuerName == null) {
280 using (var xname = x509.GetIssuerName ()) {
281 var encoding = xname.GetRawData (false);
282 var canonEncoding = xname.GetRawData (true);
283 var name = MonoBtlsUtils.FormatName (xname, true, ", ", true);
284 issuerName = new X500DistinguishedName (encoding, canonEncoding, name);
291 public override AsymmetricAlgorithm PrivateKey {
293 if (privateKey == null || !privateKey.IsRsa)
295 var bytes = privateKey.GetBytes (true);
296 return PKCS8.PrivateKeyInfo.DecodeRSA (bytes);
298 set { FallbackImpl.PrivateKey = value; }
301 public override PublicKey PublicKey {
303 ThrowIfContextInvalid ();
304 if (publicKey == null) {
305 var keyAsn = X509.GetPublicKeyAsn1 ();
306 var keyParamAsn = X509.GetPublicKeyParameters ();
307 publicKey = new PublicKey (keyAsn.Oid, keyParamAsn, keyAsn);
313 public override Oid SignatureAlgorithm {
315 ThrowIfContextInvalid ();
316 return X509.GetSignatureAlgorithm ();
320 public override X500DistinguishedName SubjectName {
322 ThrowIfContextInvalid ();
323 if (subjectName == null) {
324 using (var xname = x509.GetSubjectName ()) {
325 var encoding = xname.GetRawData (false);
326 var canonEncoding = xname.GetRawData (true);
327 var name = MonoBtlsUtils.FormatName (xname, true, ", ", true);
328 subjectName = new X500DistinguishedName (encoding, canonEncoding, name);
335 public override int Version {
336 get { return X509.GetVersion (); }
339 public override string GetNameInfo (X509NameType nameType, bool forIssuer)
341 return FallbackImpl.GetNameInfo (nameType, forIssuer);
344 public override void Import (byte[] data, string password, X509KeyStorageFlags keyStorageFlags)
346 if (password == null) {
349 } catch (Exception e) {
351 ImportPkcs12 (data, null);
353 string msg = Locale.GetText ("Unable to decode certificate.");
354 // inner exception is the original (not second) exception
355 throw new CryptographicException (msg, e);
361 ImportPkcs12 (data, password);
362 } catch (Exception e) {
364 // it's possible to supply a (unrequired/unusued) password
368 string msg = Locale.GetText ("Unable to decode certificate.");
369 // inner exception is the original (not second) exception
370 throw new CryptographicException (msg, e);
376 void Import (byte[] data)
378 // Does it look like PEM?
379 if ((data.Length > 0) && (data [0] != 0x30))
380 x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.PEM);
382 x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.DER);
385 void ImportPkcs12 (byte[] data, string password)
387 using (var pkcs12 = new MonoBtlsPkcs12 ()) {
388 if (string.IsNullOrEmpty (password)) {
390 // Support both unencrypted PKCS#12..
391 pkcs12.Import (data, null);
393 // ..and PKCS#12 encrypted with an empty password
394 pkcs12.Import (data, string.Empty);
397 pkcs12.Import (data, password);
400 x509 = pkcs12.GetCertificate (0);
401 if (pkcs12.HasPrivateKey)
402 privateKey = pkcs12.GetPrivateKey ();
403 if (pkcs12.Count > 1) {
404 intermediateCerts = new X509CertificateImplCollection ();
405 for (int i = 0; i < pkcs12.Count; i++) {
406 using (var ic = pkcs12.GetCertificate (i)) {
407 if (MonoBtlsX509.Compare (ic, x509) == 0)
409 var impl = new X509CertificateImplBtls (ic, true);
410 intermediateCerts.Add (impl, true);
417 public override byte[] Export (X509ContentType contentType, string password)
419 ThrowIfContextInvalid ();
421 switch (contentType) {
422 case X509ContentType.Cert:
423 return GetRawCertData ();
424 case X509ContentType.Pfx: // this includes Pkcs12
425 return ExportPkcs12 (password);
426 case X509ContentType.SerializedCert:
428 throw new NotSupportedException ();
430 string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
431 throw new CryptographicException (msg);
435 byte[] ExportPkcs12 (string password)
437 var pfx = new MX.PKCS12 ();
439 var attrs = new Hashtable ();
440 var localKeyId = new ArrayList ();
441 localKeyId.Add (new byte[] { 1, 0, 0, 0 });
442 attrs.Add (MX.PKCS9.localKeyId, localKeyId);
443 if (password != null)
444 pfx.Password = password;
445 pfx.AddCertificate (new MX.X509Certificate (GetRawCertData ()), attrs);
446 if (IntermediateCertificates != null) {
447 for (int i = 0; i < IntermediateCertificates.Count; i++)
448 pfx.AddCertificate (new MX.X509Certificate (IntermediateCertificates [i].GetRawCertData ()));
450 var privateKey = PrivateKey;
451 if (privateKey != null)
452 pfx.AddPkcs8ShroudedKeyBag (privateKey, attrs);
453 return pfx.GetBytes ();
459 public override bool Verify (X509Certificate2 thisCertificate)
461 using (var chain = new MonoBtlsX509Chain ()) {
462 chain.AddCertificate (x509.Copy ());
463 if (intermediateCerts != null) {
464 for (int i = 0; i < intermediateCerts.Count; i++) {
465 var intermediate = (X509CertificateImplBtls)intermediateCerts [i];
466 chain.AddCertificate (intermediate.x509.Copy ());
469 return MonoBtlsProvider.ValidateCertificate (chain, null);
473 public override void Reset ()
479 if (privateKey != null) {
487 intermediateCerts = null;
488 if (fallback != null)