From dc79f64c7e080dda3ea32d052ec9a81d76314f7a Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 15 Mar 2016 12:54:29 +0100 Subject: [PATCH] [corlib] New lazily initialized X509Certificate. List of original contributors: Martin Baulig --- mcs/class/corlib/CoreFoundation/CFHelpers.cs | 110 ++++++++++ .../X509CertificateImplApple.cs | 193 ++++++++++++++++++ .../X509Helper.Apple.cs | 50 +++++ .../X509Helper.MonoTouch.opt.cs | 20 -- mcs/class/corlib/monotouch_corlib.dll.sources | 3 + .../corlib/monotouch_opt_corlib.dll.sources | 1 - .../monotouch_runtime_corlib.dll.sources | 3 + .../corlib/monotouch_tv_corlib.dll.sources | 3 + .../corlib/monotouch_watch_corlib.dll.sources | 3 + mcs/class/corlib/xammac_corlib.dll.sources | 3 + .../corlib/xammac_opt_corlib.dll.sources | 1 - 11 files changed, 368 insertions(+), 22 deletions(-) create mode 100644 mcs/class/corlib/CoreFoundation/CFHelpers.cs create mode 100644 mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs create mode 100644 mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs delete mode 100644 mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs delete mode 100644 mcs/class/corlib/xammac_opt_corlib.dll.sources diff --git a/mcs/class/corlib/CoreFoundation/CFHelpers.cs b/mcs/class/corlib/CoreFoundation/CFHelpers.cs new file mode 100644 index 00000000000..7a765d35b55 --- /dev/null +++ b/mcs/class/corlib/CoreFoundation/CFHelpers.cs @@ -0,0 +1,110 @@ +using System; +using System.Runtime.InteropServices; + +namespace XamMac.CoreFoundation +{ + internal static class CFHelpers + { + internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; + internal const string SecurityLibrary = "/System/Library/Frameworks/Security.framework/Security"; + + [DllImport (CoreFoundationLibrary)] + internal extern static void CFRelease (IntPtr obj); + + [DllImport (CoreFoundationLibrary)] + internal extern static IntPtr CFRetain (IntPtr obj); + + [StructLayout (LayoutKind.Sequential)] + struct CFRange { + public IntPtr loc; + public IntPtr len; + + public CFRange (int loc, int len) + : this ((long) loc, (long) len) + { + } + + public CFRange (long l, long len) + { + this.loc = (IntPtr) l; + this.len = (IntPtr) len; + } + } + + [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] + extern static IntPtr CFStringCreateWithCharacters (IntPtr allocator, string str, IntPtr count); + + [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] + extern static IntPtr CFStringGetLength (IntPtr handle); + + [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] + extern static IntPtr CFStringGetCharactersPtr (IntPtr handle); + + [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] + extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer); + + internal static string FetchString (IntPtr handle) + { + if (handle == IntPtr.Zero) + return null; + + string str; + + int l = (int)CFStringGetLength (handle); + IntPtr u = CFStringGetCharactersPtr (handle); + IntPtr buffer = IntPtr.Zero; + if (u == IntPtr.Zero){ + CFRange r = new CFRange (0, l); + buffer = Marshal.AllocCoTaskMem (l * 2); + CFStringGetCharacters (handle, r, buffer); + u = buffer; + } + unsafe { + str = new string ((char *) u, 0, l); + } + + if (buffer != IntPtr.Zero) + Marshal.FreeCoTaskMem (buffer); + + return str; + } + + [DllImport (CoreFoundationLibrary)] + extern static IntPtr CFDataGetLength (IntPtr handle); + + [DllImport (CoreFoundationLibrary)] + extern static IntPtr CFDataGetBytePtr (IntPtr handle); + + internal static byte[] FetchDataBuffer (IntPtr handle) + { + var length = (int)CFDataGetLength (handle); + var buffer = new byte [length]; + var ptr = CFDataGetBytePtr (handle); + Marshal.Copy (ptr, buffer, 0, buffer.Length); + return buffer; + } + + [DllImport (CoreFoundationLibrary)] + extern static IntPtr CFDataCreateWithBytesNoCopy (IntPtr allocator, IntPtr bytes, IntPtr length, IntPtr bytesDeallocator); + + [DllImport (CoreFoundationLibrary)] + extern static IntPtr CFDataCreate (IntPtr allocator, IntPtr bytes, IntPtr length); + + [DllImport (SecurityLibrary)] + extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr cfData); + + unsafe internal static IntPtr CreateCertificateFromData (byte[] data) + { + fixed (void *ptr = data) { + var cfdata = CFDataCreate (IntPtr.Zero, (IntPtr)ptr, new IntPtr (data.Length)); + if (cfdata == IntPtr.Zero) + return IntPtr.Zero; + + var certificate = SecCertificateCreateWithData (IntPtr.Zero, cfdata); + if (cfdata != IntPtr.Zero) + CFRelease (cfdata); + return certificate; + } + } + } +} diff --git a/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs b/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs new file mode 100644 index 00000000000..bca51601728 --- /dev/null +++ b/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs @@ -0,0 +1,193 @@ +using System; +using System.Text; +using System.Runtime.InteropServices; +using XamMac.CoreFoundation; +using MX = Mono.Security.X509; + +namespace System.Security.Cryptography.X509Certificates +{ + class X509CertificateImplApple : X509CertificateImpl + { + IntPtr handle; + X509CertificateImpl fallback; + + public X509CertificateImplApple (IntPtr handle, bool owns) + { + this.handle = handle; + if (!owns) + CFHelpers.CFRetain (handle); + } + + public override bool IsValid { + get { return handle != IntPtr.Zero; } + } + + public override IntPtr Handle { + get { return handle; } + } + + public override X509CertificateImpl Clone () + { + ThrowIfContextInvalid (); + return new X509CertificateImplApple (handle, false); + } + + [DllImport (CFHelpers.SecurityLibrary)] + extern static IntPtr SecCertificateCopySubjectSummary (IntPtr cert); + + [DllImport (CFHelpers.SecurityLibrary)] + extern static IntPtr SecCertificateCopyData (IntPtr cert); + + public override byte[] GetRawCertData () + { + ThrowIfContextInvalid (); + var data = SecCertificateCopyData (handle); + if (data == IntPtr.Zero) + throw new ArgumentException ("Not a valid certificate"); + + try { + return CFHelpers.FetchDataBuffer (data); + } finally { + CFHelpers.CFRelease (data); + } + } + + public override string GetSubjectSummary () + { + ThrowIfContextInvalid (); + IntPtr cfstr = SecCertificateCopySubjectSummary (handle); + string ret = CFHelpers.FetchString (cfstr); + CFHelpers.CFRelease (cfstr); + return ret; + } + + protected override byte[] GetCertHash (bool lazy) + { + // FIXME: might just return 'null' when 'lazy' is true. + ThrowIfContextInvalid (); + SHA1 sha = SHA1.Create (); + return sha.ComputeHash (GetRawCertData ()); + } + + public override bool Equals (X509CertificateImpl other, out bool result) + { + var otherAppleImpl = other as X509CertificateImplApple; + if (otherAppleImpl != null && otherAppleImpl.handle == handle) { + result = true; + return true; + } + + result = false; + return false; + } + + void MustFallback () + { + ThrowIfContextInvalid (); + if (fallback != null) + return; + var mxCert = new MX.X509Certificate (GetRawCertData ()); + fallback = new X509CertificateImplMono (mxCert); + } + + public X509CertificateImpl FallbackImpl { + get { + MustFallback (); + return fallback; + } + } + + public override string GetSubjectName (bool legacyV1Mode) + { + return FallbackImpl.GetSubjectName (legacyV1Mode); + } + + public override string GetIssuerName (bool legacyV1Mode) + { + return FallbackImpl.GetIssuerName (legacyV1Mode); + } + + public override DateTime GetEffectiveDateString () + { + return FallbackImpl.GetEffectiveDateString (); + } + + public override DateTime GetExpirationDateString () + { + return FallbackImpl.GetExpirationDateString (); + } + + public override string GetKeyAlgorithm () + { + return FallbackImpl.GetKeyAlgorithm (); + } + + public override byte[] GetKeyAlgorithmParameters () + { + return FallbackImpl.GetKeyAlgorithmParameters (); + } + + public override byte[] GetPublicKey () + { + return FallbackImpl.GetPublicKey (); + } + + public override byte[] GetSerialNumber () + { + return FallbackImpl.GetSerialNumber (); + } + + public override byte[] Export (X509ContentType contentType, byte[] password) + { + ThrowIfContextInvalid (); + + switch (contentType) { + case X509ContentType.Cert: + return GetRawCertData (); + case X509ContentType.Pfx: // this includes Pkcs12 + // TODO + throw new NotSupportedException (); + case X509ContentType.SerializedCert: + // TODO + throw new NotSupportedException (); + default: + string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType); + throw new CryptographicException (msg); + } + } + + public override string ToString (bool full) + { + ThrowIfContextInvalid (); + + if (!full || fallback == null) { + var summary = GetSubjectSummary (); + return string.Format ("[X509Certificate: {0}]", summary); + } + + string nl = Environment.NewLine; + StringBuilder sb = new StringBuilder (); + sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, GetSubjectName (false)); + + sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, GetIssuerName (false)); + sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetEffectiveDateString ()); + sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetExpirationDateString ()); + sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, X509Helper.ToHexString (GetCertHash ())); + + sb.Append (nl); + return sb.ToString (); + } + + protected override void Dispose (bool disposing) + { + if (handle != IntPtr.Zero){ + CFHelpers.CFRelease (handle); + handle = IntPtr.Zero; + } + if (fallback != null) { + fallback.Dispose (); + fallback = null; + } + } + } +} diff --git a/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs b/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs new file mode 100644 index 00000000000..7068882bc9a --- /dev/null +++ b/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.InteropServices; +using MX = Mono.Security.X509; +using XamMac.CoreFoundation; + +namespace System.Security.Cryptography.X509Certificates +{ + static partial class X509Helper + { + public static X509CertificateImpl InitFromHandle (IntPtr handle) + { + return new X509CertificateImplApple (handle, false); + } + + public static X509CertificateImpl Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) + { + MX.X509Certificate x509; + IntPtr handle; + if (password == null) { + handle = CFHelpers.CreateCertificateFromData (rawData); + if (handle != IntPtr.Zero) + return new X509CertificateImplApple (handle, true); + + try { + x509 = new MX.X509Certificate (rawData); + } catch (Exception e) { + try { + x509 = X509Helper.ImportPkcs12 (rawData, null); + } catch { + string msg = Locale.GetText ("Unable to decode certificate."); + // inner exception is the original (not second) exception + throw new CryptographicException (msg, e); + } + } + } else { + // try PKCS#12 + try { + x509 = X509Helper.ImportPkcs12 (rawData, password); + } + catch { + // it's possible to supply a (unrequired/unusued) password + // fix bug #79028 + x509 = new MX.X509Certificate (rawData); + } + } + + return new X509CertificateImplMono (x509); + } + } +} diff --git a/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs b/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs deleted file mode 100644 index 19f1c4eabb5..00000000000 --- a/mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs +++ /dev/null @@ -1,20 +0,0 @@ -#if MONOTOUCH || XAMMAC - -// this file is a shim to enable compiling monotouch profiles without mono-extensions -namespace System.Security.Cryptography.X509Certificates -{ - static partial class X509Helper - { - public static X509CertificateImpl InitFromHandle (IntPtr handle) - { - throw new NotSupportedException (); - } - - public static X509CertificateImpl Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) - { - throw new NotSupportedException (); - } - } -} - -#endif diff --git a/mcs/class/corlib/monotouch_corlib.dll.sources b/mcs/class/corlib/monotouch_corlib.dll.sources index c8d37dfb3bd..bd0cfeaa270 100644 --- a/mcs/class/corlib/monotouch_corlib.dll.sources +++ b/mcs/class/corlib/monotouch_corlib.dll.sources @@ -21,3 +21,6 @@ CommonCrypto/MD4Managed.g.cs System/Environment.iOS.cs System/Guid.MonoTouch.cs System/NotSupportedException.iOS.cs +CoreFoundation/CFHelpers.cs +System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs +System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs diff --git a/mcs/class/corlib/monotouch_opt_corlib.dll.sources b/mcs/class/corlib/monotouch_opt_corlib.dll.sources index df73c7f973d..46418a319b2 100644 --- a/mcs/class/corlib/monotouch_opt_corlib.dll.sources +++ b/mcs/class/corlib/monotouch_opt_corlib.dll.sources @@ -1,2 +1 @@ System.Text/EncodingHelper.MonoTouch.opt.cs -System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs diff --git a/mcs/class/corlib/monotouch_runtime_corlib.dll.sources b/mcs/class/corlib/monotouch_runtime_corlib.dll.sources index c8d37dfb3bd..bd0cfeaa270 100644 --- a/mcs/class/corlib/monotouch_runtime_corlib.dll.sources +++ b/mcs/class/corlib/monotouch_runtime_corlib.dll.sources @@ -21,3 +21,6 @@ CommonCrypto/MD4Managed.g.cs System/Environment.iOS.cs System/Guid.MonoTouch.cs System/NotSupportedException.iOS.cs +CoreFoundation/CFHelpers.cs +System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs +System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs diff --git a/mcs/class/corlib/monotouch_tv_corlib.dll.sources b/mcs/class/corlib/monotouch_tv_corlib.dll.sources index c8d37dfb3bd..bd0cfeaa270 100644 --- a/mcs/class/corlib/monotouch_tv_corlib.dll.sources +++ b/mcs/class/corlib/monotouch_tv_corlib.dll.sources @@ -21,3 +21,6 @@ CommonCrypto/MD4Managed.g.cs System/Environment.iOS.cs System/Guid.MonoTouch.cs System/NotSupportedException.iOS.cs +CoreFoundation/CFHelpers.cs +System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs +System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs diff --git a/mcs/class/corlib/monotouch_watch_corlib.dll.sources b/mcs/class/corlib/monotouch_watch_corlib.dll.sources index c8d37dfb3bd..bd0cfeaa270 100644 --- a/mcs/class/corlib/monotouch_watch_corlib.dll.sources +++ b/mcs/class/corlib/monotouch_watch_corlib.dll.sources @@ -21,3 +21,6 @@ CommonCrypto/MD4Managed.g.cs System/Environment.iOS.cs System/Guid.MonoTouch.cs System/NotSupportedException.iOS.cs +CoreFoundation/CFHelpers.cs +System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs +System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs diff --git a/mcs/class/corlib/xammac_corlib.dll.sources b/mcs/class/corlib/xammac_corlib.dll.sources index 4d6428cc31b..0dbb5163c70 100644 --- a/mcs/class/corlib/xammac_corlib.dll.sources +++ b/mcs/class/corlib/xammac_corlib.dll.sources @@ -17,3 +17,6 @@ CommonCrypto/RijndaelManaged.cs CommonCrypto/RC4CommonCrypto.cs CommonCrypto/MD2Managed.g.cs CommonCrypto/MD4Managed.g.cs +CoreFoundation/CFHelpers.cs +System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs +System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs diff --git a/mcs/class/corlib/xammac_opt_corlib.dll.sources b/mcs/class/corlib/xammac_opt_corlib.dll.sources deleted file mode 100644 index 449e2520dde..00000000000 --- a/mcs/class/corlib/xammac_opt_corlib.dll.sources +++ /dev/null @@ -1 +0,0 @@ -System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs -- 2.25.1