[corlib] New lazily initialized X509Certificate.
authorRolf Bjarne Kvinge <rolf@xamarin.com>
Tue, 15 Mar 2016 11:54:29 +0000 (12:54 +0100)
committerRodrigo Kumpera <kumpera@gmail.com>
Mon, 28 Mar 2016 22:51:35 +0000 (15:51 -0700)
List of original contributors:

Martin Baulig <martin.baulig@xamarin.com>

mcs/class/corlib/CoreFoundation/CFHelpers.cs [new file with mode: 0644]
mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509CertificateImplApple.cs [new file with mode: 0644]
mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.Apple.cs [new file with mode: 0644]
mcs/class/corlib/System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs [deleted file]
mcs/class/corlib/monotouch_corlib.dll.sources
mcs/class/corlib/monotouch_opt_corlib.dll.sources
mcs/class/corlib/monotouch_runtime_corlib.dll.sources
mcs/class/corlib/monotouch_tv_corlib.dll.sources
mcs/class/corlib/monotouch_watch_corlib.dll.sources
mcs/class/corlib/xammac_corlib.dll.sources
mcs/class/corlib/xammac_opt_corlib.dll.sources [deleted file]

diff --git a/mcs/class/corlib/CoreFoundation/CFHelpers.cs b/mcs/class/corlib/CoreFoundation/CFHelpers.cs
new file mode 100644 (file)
index 0000000..7a765d3
--- /dev/null
@@ -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 (file)
index 0000000..bca5160
--- /dev/null
@@ -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 (file)
index 0000000..7068882
--- /dev/null
@@ -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 (file)
index 19f1c4e..0000000
+++ /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
index c8d37dfb3bd0cc3ed9dd7dc9edfea1e991bde4c5..bd0cfeaa270ac447778efbd5dc9c96cc787532d5 100644 (file)
@@ -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
index df73c7f973d8984790ed909c25b2afec5d702227..46418a319b2ee4b16865feabcae7bf030ec52390 100644 (file)
@@ -1,2 +1 @@
 System.Text/EncodingHelper.MonoTouch.opt.cs
-System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs
index c8d37dfb3bd0cc3ed9dd7dc9edfea1e991bde4c5..bd0cfeaa270ac447778efbd5dc9c96cc787532d5 100644 (file)
@@ -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
index c8d37dfb3bd0cc3ed9dd7dc9edfea1e991bde4c5..bd0cfeaa270ac447778efbd5dc9c96cc787532d5 100644 (file)
@@ -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
index c8d37dfb3bd0cc3ed9dd7dc9edfea1e991bde4c5..bd0cfeaa270ac447778efbd5dc9c96cc787532d5 100644 (file)
@@ -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
index 4d6428cc31b39deae9f5286e32460c52b843331b..0dbb5163c7042ef38fd2f08e6f1fc01a3611dd6f 100644 (file)
@@ -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 (file)
index 449e252..0000000
+++ /dev/null
@@ -1 +0,0 @@
-System.Security.Cryptography.X509Certificates/X509Helper.MonoTouch.opt.cs