void SetupCertificateStore ()
{
-#if MONODROID
- ctx.CertificateStore.SetDefaultPaths ();
- ctx.CertificateStore.AddAndroidLookup ();
-#else
- var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
- if (Directory.Exists (userPath))
- ctx.CertificateStore.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
- var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
- if (Directory.Exists (machinePath))
- ctx.CertificateStore.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
-#endif
+ MonoBtlsProvider.SetupCertificateStore (ctx.CertificateStore);
if (Settings != null && Settings.TrustAnchors != null) {
var trust = IsServer ? MonoBtlsX509TrustKind.TRUST_CLIENT : MonoBtlsX509TrustKind.TRUST_SERVER;
return buffer;
}
+ public bool IsRsa {
+ get {
+ return mono_btls_key_is_rsa (Handle.DangerousGetHandle ()) != 0;
+ }
+ }
+
public MonoBtlsKey Copy ()
{
CheckThrow ();
var passptr = IntPtr.Zero;
fixed (void* ptr = buffer)
try {
- passptr = Marshal.StringToHGlobalAnsi (password ?? string.Empty);
+ if (password != null)
+ passptr = Marshal.StringToHGlobalAnsi (password);
var ret = mono_btls_pkcs12_import (
Handle.DangerousGetHandle (), ptr,
buffer.Length, passptr);
internal override X509Certificate2Impl GetNativeCertificate (
byte[] data, string password, X509KeyStorageFlags flags)
{
- var impl = new X509CertificateImplBtls (true);
+ var impl = new X509CertificateImplBtls (false);
impl.Import (data, password, flags);
return impl;
}
using (var nativeChain = MonoBtlsProvider.GetNativeChain (certificates))
using (var param = GetVerifyParam (targetHost, serverMode))
using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
- store.LoadLocations (null, GetSystemStoreLocation ());
- store.SetDefaultPaths ();
+ SetupCertificateStore (store);
storeCtx.Initialize (store, nativeChain);
}
}
+ internal static bool ValidateCertificate (MonoBtlsX509Chain chain, MonoBtlsX509VerifyParam param)
+ {
+ using (var store = new MonoBtlsX509Store ())
+ using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
+ SetupCertificateStore (store);
+
+ storeCtx.Initialize (store, chain);
+
+ if (param != null)
+ storeCtx.SetVerifyParam (param);
+
+ var ret = storeCtx.Verify ();
+
+ return ret == 1;
+ }
+ }
+
void CheckValidationResult (
ICertificateValidator validator, string targetHost, bool serverMode,
X509CertificateCollection certificates, bool wantsChain,
}
}
+ internal static void SetupCertificateStore (MonoBtlsX509Store store)
+ {
+#if MONODROID
+ store.SetDefaultPaths ();
+ store.AddAndroidLookup ();
+#else
+ var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
+ if (Directory.Exists (userPath))
+ store.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
+ var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
+ if (Directory.Exists (machinePath))
+ store.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
+#endif
+ }
+
public static string GetSystemStoreLocation ()
{
#if ANDROID
if (Compare (oidValue, emailOid))
type = MonoBtlsX509NameEntryType.Email;
}
- var text = name.GetEntryValue (index);
+ int tag;
+ var text = name.GetEntryValue (index, out tag);
if (text == null)
return false;
var oid = name.GetEntryOid (index);
// 16bits or 8bits string ? TODO not complete (+special chars!)
char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
- if (quotes) {
+ if (quotes && tag != 0x1E) {
if ((text.IndexOfAny (specials, 0, text.Length) > 0) ||
text.StartsWith (" ") || (text.EndsWith (" ")))
text = "\"" + text + "\"";
extern static int mono_btls_x509_name_get_entry_oid_data (IntPtr name, int index, out IntPtr data);
[MethodImpl (MethodImplOptions.InternalCall)]
- extern static int mono_btls_x509_name_get_entry_value (IntPtr name, int index, out IntPtr str);
+ extern static int mono_btls_x509_name_get_entry_value (IntPtr name, int index, out int tag, out IntPtr str);
[MethodImpl (MethodImplOptions.InternalCall)]
extern unsafe static IntPtr mono_btls_x509_name_from_data (void* data, int len, int use_canon_enc);
return bytes;
}
- public unsafe string GetEntryValue (int index)
+ public unsafe string GetEntryValue (int index, out int tag)
{
if (index >= GetEntryCount ())
throw new ArgumentOutOfRangeException ();
IntPtr data;
var ret = mono_btls_x509_name_get_entry_value (
- Handle.DangerousGetHandle (), index, out data);
+ Handle.DangerousGetHandle (), index, out tag, out data);
if (ret <= 0)
return null;
try {
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+#endif
+
+#if MONO_SECURITY_ALIAS
+using MX = MonoSecurity::Mono.Security.X509;
+#else
+using MX = Mono.Security.X509;
+#endif
+
using System;
using System.Text;
+using System.Collections;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
-using MX = Mono.Security.X509;
+using Mono.Security.Cryptography;
namespace Mono.Btls
{
return true;
}
- protected override byte [] GetCertHash (bool lazy)
+ protected override byte[] GetCertHash (bool lazy)
{
return X509.GetCertHash ();
}
- public override byte [] GetRawCertData ()
+ public override byte[] GetRawCertData ()
{
return X509.GetRawData (MonoBtlsX509Format.DER);
}
return PublicKey.EncodedParameters.RawData;
}
- public override byte [] Export (X509ContentType contentType, byte [] password)
+ public override byte[] Export (X509ContentType contentType, byte[] password)
{
ThrowIfContextInvalid ();
throw new InvalidOperationException ();
if (fallback != null)
return;
- fallback = X509Helper2.Import (GetRawCertData (), null, X509KeyStorageFlags.DefaultKeySet);
+ fallback = X509Helper2.Import (GetRawCertData (), null, X509KeyStorageFlags.DefaultKeySet, true);
}
- internal X509Certificate2Impl FallbackImpl {
+ internal override X509Certificate2Impl FallbackImpl {
get {
MustFallback ();
return fallback;
}
public override bool HasPrivateKey {
- get { return FallbackImpl.HasPrivateKey; }
+ get { return privateKey != null; }
}
public override X500DistinguishedName IssuerName {
}
public override AsymmetricAlgorithm PrivateKey {
- get { return FallbackImpl.PrivateKey; }
+ get {
+ if (privateKey == null || !privateKey.IsRsa)
+ return null;
+ var bytes = privateKey.GetBytes (true);
+ return PKCS8.PrivateKeyInfo.DecodeRSA (bytes);
+ }
set { FallbackImpl.PrivateKey = value; }
}
return FallbackImpl.GetNameInfo (nameType, forIssuer);
}
- public override void Import (byte [] data, string password, X509KeyStorageFlags keyStorageFlags)
+ public override void Import (byte[] data, string password, X509KeyStorageFlags keyStorageFlags)
{
if (password == null) {
- // Does it look like PEM?
- if ((data.Length > 0) && (data [0] != 0x30))
- x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.PEM);
- else
- x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.DER);
- return;
+ try {
+ Import (data);
+ } catch (Exception e) {
+ try {
+ ImportPkcs12 (data, 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 {
+ ImportPkcs12 (data, password);
+ } catch (Exception e) {
+ try {
+ // it's possible to supply a (unrequired/unusued) password
+ // fix bug #79028
+ Import (data);
+ } catch {
+ string msg = Locale.GetText ("Unable to decode certificate.");
+ // inner exception is the original (not second) exception
+ throw new CryptographicException (msg, e);
+ }
+ }
}
+ }
+
+ void Import (byte[] data)
+ {
+ // Does it look like PEM?
+ if ((data.Length > 0) && (data [0] != 0x30))
+ x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.PEM);
+ else
+ x509 = MonoBtlsX509.LoadFromData (data, MonoBtlsX509Format.DER);
+ }
+ void ImportPkcs12 (byte[] data, string password)
+ {
using (var pkcs12 = new MonoBtlsPkcs12 ()) {
- pkcs12.Import (data, password);
+ if (string.IsNullOrEmpty (password)) {
+ try {
+ // Support both unencrypted PKCS#12..
+ pkcs12.Import (data, null);
+ } catch {
+ // ..and PKCS#12 encrypted with an empty password
+ pkcs12.Import (data, string.Empty);
+ }
+ } else {
+ pkcs12.Import (data, password);
+ }
+
x509 = pkcs12.GetCertificate (0);
if (pkcs12.HasPrivateKey)
privateKey = pkcs12.GetPrivateKey ();
}
}
- public override byte [] Export (X509ContentType contentType, string password)
+ public override byte[] Export (X509ContentType contentType, string password)
{
- return FallbackImpl.Export (contentType, password);
+ ThrowIfContextInvalid ();
+
+ switch (contentType) {
+ case X509ContentType.Cert:
+ return GetRawCertData ();
+ case X509ContentType.Pfx: // this includes Pkcs12
+ return ExportPkcs12 (password);
+ case X509ContentType.SerializedCert:
+ // TODO
+ throw new NotSupportedException ();
+ default:
+ string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
+ throw new CryptographicException (msg);
+ }
+ }
+
+ byte[] ExportPkcs12 (string password)
+ {
+ var pfx = new MX.PKCS12 ();
+ try {
+ var attrs = new Hashtable ();
+ var localKeyId = new ArrayList ();
+ localKeyId.Add (new byte[] { 1, 0, 0, 0 });
+ attrs.Add (MX.PKCS9.localKeyId, localKeyId);
+ if (password != null)
+ pfx.Password = password;
+ pfx.AddCertificate (new MX.X509Certificate (GetRawCertData ()), attrs);
+ if (IntermediateCertificates != null) {
+ for (int i = 0; i < IntermediateCertificates.Count; i++)
+ pfx.AddCertificate (new MX.X509Certificate (IntermediateCertificates [i].GetRawCertData ()));
+ }
+ var privateKey = PrivateKey;
+ if (privateKey != null)
+ pfx.AddPkcs8ShroudedKeyBag (privateKey, attrs);
+ return pfx.GetBytes ();
+ } finally {
+ pfx.Password = null;
+ }
}
public override bool Verify (X509Certificate2 thisCertificate)
{
- return FallbackImpl.Verify (thisCertificate);
+ using (var chain = new MonoBtlsX509Chain ()) {
+ chain.AddCertificate (x509.Copy ());
+ if (intermediateCerts != null) {
+ for (int i = 0; i < intermediateCerts.Count; i++) {
+ var intermediate = (X509CertificateImplBtls)intermediateCerts [i];
+ chain.AddCertificate (intermediate.x509.Copy ());
+ }
+ }
+ return MonoBtlsProvider.ValidateCertificate (chain, null);
+ }
}
public override void Reset ()
}
internal X500DistinguishedName (byte[] encoded, byte[] canonEncoding, string name)
- : this (encoded)
{
this.canonEncoding = canonEncoding;
this.name = name;
// internal stuff because X509Certificate2 isn't complete enough
// (maybe X509Certificate3 will be better?)
- [Obsolete ("KILL")]
+ [MonoTODO ("See comment in X509Helper2.GetMonoCertificate().")]
internal MX.X509Certificate MonoCertificate {
get {
- var monoImpl = Impl as X509Certificate2ImplMono;
- if (monoImpl == null)
- throw new NotSupportedException ();
- return monoImpl.MonoCertificate;
+ return X509Helper2.GetMonoCertificate (this);
}
}
get;
}
+ internal abstract X509Certificate2Impl FallbackImpl {
+ get;
+ }
+
public abstract string GetNameInfo (X509NameType nameType, bool forIssuer);
public abstract void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags);
[MonoTODO ("missing KeyStorageFlags support")]
public override void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
{
+ Reset ();
MX.X509Certificate cert = null;
if (password == null) {
try {
internal MX.X509Certificate MonoCertificate {
get { return _cert; }
}
+
+ internal override X509Certificate2Impl FallbackImpl {
+ get { return this; }
+ }
}
}
#if MONO_SECURITY_ALIAS
using MonoSecurity::Mono.Security.Interface;
+using MX = MonoSecurity::Mono.Security.X509;
#else
#if !FEATURE_NO_BSD_SOCKETS
using Mono.Security.Interface;
#endif
+using MX = Mono.Security.X509;
#endif
#if !FEATURE_NO_BSD_SOCKETS
}
#endif // !FEATURE_NO_BSD_SOCKETS
- internal static X509Certificate2Impl Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
+ internal static X509Certificate2Impl Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags, bool disableProvider = false)
{
#if !FEATURE_NO_BSD_SOCKETS
- var provider = MonoTlsProviderFactory.GetProvider ();
- if (provider.HasNativeCertificates) {
- var impl = provider.GetNativeCertificate (rawData, password, keyStorageFlags);
- return impl;
+ if (!disableProvider) {
+ var provider = MonoTlsProviderFactory.GetProvider ();
+ if (provider.HasNativeCertificates) {
+ var impl = provider.GetNativeCertificate (rawData, password, keyStorageFlags);
+ return impl;
+ }
}
#endif // FEATURE_NO_BSD_SOCKETS
var impl2 = new X509Certificate2ImplMono ();
return impl2;
}
- internal static X509Certificate2Impl Import (X509Certificate cert)
+ internal static X509Certificate2Impl Import (X509Certificate cert, bool disableProvider = false)
{
#if !FEATURE_NO_BSD_SOCKETS
- var provider = MonoTlsProviderFactory.GetProvider ();
- if (provider.HasNativeCertificates) {
- var impl = provider.GetNativeCertificate (cert);
- return impl;
+ if (!disableProvider) {
+ var provider = MonoTlsProviderFactory.GetProvider ();
+ if (provider.HasNativeCertificates) {
+ var impl = provider.GetNativeCertificate (cert);
+ return impl;
+ }
}
#endif // FEATURE_NO_BSD_SOCKETS
var impl2 = cert.Impl as X509Certificate2Impl;
return Import (cert.GetRawCertData (), null, X509KeyStorageFlags.DefaultKeySet);
}
+ /*
+ * This is used by X509ChainImplMono
+ *
+ * Some of the missing APIs such as X509v3 extensions can be added to the native
+ * BTLS implementation.
+ *
+ * We should also consider replacing X509ChainImplMono with a new X509ChainImplBtls
+ * at some point.
+ */
+ [MonoTODO ("Investigate replacement; see comments in source.")]
+ internal static MX.X509Certificate GetMonoCertificate (X509Certificate2 certificate)
+ {
+ var impl2 = certificate.Impl as X509Certificate2Impl;
+ if (impl2 == null)
+ impl2 = Import (certificate, true);
+ var fallbackImpl = impl2.FallbackImpl as X509Certificate2ImplMono;
+ if (fallbackImpl == null)
+ throw new NotSupportedException ();
+ return fallbackImpl.MonoCertificate;
+ }
+
internal static X509ChainImpl CreateChainImpl (bool useMachineContext)
{
return new X509ChainImplMono (useMachineContext);
}
int
-mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, unsigned char **str)
+mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, int *tag, unsigned char **str)
{
X509_NAME_ENTRY *entry;
ASN1_STRING *data;
*str = NULL;
+ *tag = 0;
if (index >= X509_NAME_entry_count (name->name))
return 0;
if (!data)
return 0;
+ *tag = data->type;
return ASN1_STRING_to_UTF8 (str, data);
}
mono_btls_x509_name_get_entry_oid_data (MonoBtlsX509Name *name, int index, const void **data);
int
-mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, unsigned char **str);
+mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, int *tag, unsigned char **str);
#endif /* __btls__btls_x509_name__ */