public void SetCertificate(SecIdentity identify, IEnumerable <SecCertificate> certificates) { using (var array = Bundle(identify, certificates)) { var result = SSLSetCertificate(Handle, array.Handle); CheckStatusAndThrow(result); } }
CFArray Bundle(SecIdentity identity, IEnumerable <SecCertificate> certificates) { if (identity == null) { throw new ArgumentNullException("identity"); } int i = 0; int n = 0; if (certificates != null) { foreach (var obj in certificates) { n++; } } var ptrs = new IntPtr [n + 1]; ptrs [0] = identity.Handle; foreach (var certificate in certificates) { ptrs [++i] = certificate.Handle; } return(CFArray.CreateArray(ptrs)); }
public override void StartHandshake() { Debug("StartHandshake: {0}", IsServer); if (Interlocked.CompareExchange(ref handshakeStarted, 1, 1) != 0) { throw new InvalidOperationException(); } InitializeConnection(); SetSessionOption(SslSessionOption.BreakOnCertRequested, true); SetSessionOption(SslSessionOption.BreakOnClientAuth, true); SetSessionOption(SslSessionOption.BreakOnServerAuth, true); if (IsServer) { SecCertificate[] intermediateCerts; serverIdentity = AppleCertificateHelper.GetIdentity(LocalServerCertificate, out intermediateCerts); if (serverIdentity == null) { throw new SSA.AuthenticationException("Unable to get server certificate from keychain."); } SetCertificate(serverIdentity, intermediateCerts); for (int i = 0; i < intermediateCerts.Length; i++) { intermediateCerts [i].Dispose(); } } }
static INativeObject [] QueryAsReference(CFDictionary query, out SecStatusCode result) { if (query == null) { result = SecStatusCode.Param; return(null); } IntPtr ptr; result = SecItem.SecItemCopyMatching(query.Handle, out ptr); if (result == SecStatusCode.Success && ptr != IntPtr.Zero) { var array = CFArray.ArrayFromHandle <INativeObject> (ptr, p => { IntPtr cfType = CFType.GetTypeID(p); if (cfType == SecCertificate.GetTypeID()) { return(new SecCertificate(p, true)); } if (cfType == SecKey.GetTypeID()) { return(new SecKey(p, true)); } if (cfType == SecIdentity.GetTypeID()) { return(new SecIdentity(p, true)); } throw new Exception(String.Format("Unexpected type: 0x{0:x}", cfType)); }); return(array); } return(null); }
public override bool ProcessHandshake() { if (handshakeFinished) { throw new NotSupportedException("Handshake already finished."); } while (true) { lastException = null; var status = SSLHandshake(Handle); Debug("Handshake: {0} - {0:x}", status); CheckStatusAndThrow(status, SslStatus.WouldBlock, SslStatus.PeerAuthCompleted, SslStatus.PeerClientCertRequested); if (status == SslStatus.PeerAuthCompleted) { RequirePeerTrust(); } else if (status == SslStatus.PeerClientCertRequested) { RequirePeerTrust(); if (remoteCertificate == null) { throw new TlsException(AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server."); } localClientCertificate = SelectClientCertificate(remoteCertificate, null); if (localClientCertificate == null) { continue; } clientIdentity = AppleCertificateHelper.GetIdentity(localClientCertificate); if (clientIdentity == null) { throw new TlsException(AlertDescription.CertificateUnknown); } SetCertificate(clientIdentity, new SecCertificate [0]); } else if (status == SslStatus.WouldBlock) { return(false); } else if (status == SslStatus.Success) { handshakeFinished = true; return(true); } } }
public override void StartHandshake() { Debug("StartHandshake: {0}", IsServer); if (Interlocked.CompareExchange(ref handshakeStarted, 1, 1) != 0) { throw new InvalidOperationException(); } InitializeConnection(); /* * SecureTransport is bugged OS X 10.5.8+ - renegotiation after * calling SetCertificate() will not work. * * We also cannot change options after the handshake has started, * so if you want to request a client certificate, it will happen * both during the initial handshake and during renegotiation. * * You may check 'SslStream.IsAuthenticated' (which will be false * during the initial handshake) from within your * 'LocalCertificateSelectionCallback' and return null to have the * callback invoked again during renegotiation. * * However, the first time your selection callback returns a client * certificate, that certificate will be used for the rest of the * session. */ SetSessionOption(SslSessionOption.BreakOnCertRequested, true); SetSessionOption(SslSessionOption.BreakOnClientAuth, true); SetSessionOption(SslSessionOption.BreakOnServerAuth, true); if (IsServer) { SecCertificate[] intermediateCerts; serverIdentity = AppleCertificateHelper.GetIdentity(LocalServerCertificate, out intermediateCerts); if (serverIdentity == null) { throw new SSA.AuthenticationException("Unable to get server certificate from keychain."); } SetCertificate(serverIdentity, intermediateCerts); for (int i = 0; i < intermediateCerts.Length; i++) { intermediateCerts [i].Dispose(); } } }
void ClientCertificateRequested() { EvaluateTrust(); var acceptableIssuers = CopyDistinguishedNames(); localClientCertificate = SelectClientCertificate(acceptableIssuers); if (localClientCertificate == null) { return; } clientIdentity = AppleCertificateHelper.GetIdentity(localClientCertificate); if (clientIdentity == null) { throw new TlsException(AlertDescription.CertificateUnknown); } SetCertificate(clientIdentity, new SecCertificate [0]); }
static public SecIdentity ItemImport(X509Certificate2 certificate) { if (!certificate.HasPrivateKey) { throw new NotSupportedException(); } using (var key = ImportPrivateKey(certificate)) using (var cert = new SecCertificate(certificate)) { var identity = SecIdentityCreate(IntPtr.Zero, cert.Handle, key.Handle); if (CFType.GetTypeID(identity) != SecIdentity.GetTypeID()) { throw new InvalidOperationException(); } return(new SecIdentity(identity, true)); } }
public static SecIdentity GetIdentity(X509Certificate certificate) { /* * If we got an 'X509Certificate2', then we require it to have a private key * and import it. */ var certificate2 = certificate as X509Certificate2; if (certificate2 != null) { return(SecIdentity.Import(certificate2)); } /* * Otherwise, we require the private key to be in the keychain. */ using (var secCert = new SecCertificate(certificate)) { return(SecKeyChain.FindIdentity(secCert, true)); } }
public static INativeObject[] QueryAsReference(SecRecord query, int max, out SecStatusCode result) { if (query == null) { result = SecStatusCode.Param; return(null); } using (var copy = query.queryDict.MutableCopy()) { copy.SetValue(CFBoolean.True.Handle, SecItem.ReturnRef); SetLimit(copy, max); IntPtr ptr; result = SecItem.SecItemCopyMatching(copy.Handle, out ptr); if ((result == SecStatusCode.Success) && (ptr != IntPtr.Zero)) { var array = CFArray.ArrayFromHandle <INativeObject> (ptr, p => { IntPtr cfType = CFType.GetTypeID(p); if (cfType == SecCertificate.GetTypeID()) { return(new SecCertificate(p, true)); } else if (cfType == SecKey.GetTypeID()) { return(new SecKey(p, true)); } else if (cfType == SecIdentity.GetTypeID()) { return(new SecIdentity(p, true)); } else { throw new Exception(String.Format("Unexpected type: 0x{0:x}", cfType)); } }); return(array); } return(null); } }
protected override void Dispose(bool disposing) { try { if (disposed) { return; } if (disposing) { disposed = true; if (serverIdentity != null) { serverIdentity.Dispose(); serverIdentity = null; } if (clientIdentity != null) { clientIdentity.Dispose(); clientIdentity = null; } if (remoteCertificate != null) { remoteCertificate.Dispose(); remoteCertificate = null; } } } finally { disposed = true; if (context != IntPtr.Zero) { CFObject.CFRelease(context); context = IntPtr.Zero; } base.Dispose(disposing); } }
public static SecIdentity GetIdentity(X509Certificate certificate) { /* * If we got an 'X509Certificate2', then we require it to have a private key * and import it. */ var certificate2 = certificate as X509Certificate2; if (certificate2 != null) #if MONOTOUCH { return(SecIdentity.Import(certificate2)); } #else { return(SecImportExport.ItemImport(certificate2)); } #endif /* * Reading Certificates from the Mac Keychain * ========================================== * * Reading the private key from the keychain is a new feature introduced with * AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several * known issues and it also did not received any testing yet. We go back to the old * way of doing things, which is to explicitly provide an X509Certificate2 with a * private key. * * Keychain Dialog Popups * ====================== * * When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain * for the certificate and private key. * * On Xamarin.iOS, this is easy because each app has its own keychain. * * On Xamarin.Mac, the .app package needs to be trusted via code-sign * to get permission to access the user's keychain. [FIXME: I still have to * research how to actually do that.] Without this, you will get a popup * message each time, asking you whether you want to allow the app to access * the keychain, but you can make these go away by selecting "Trust always". * * On Desktop Mono, this is problematic because selecting "Trust always" * give the 'mono' binary (and thus everything you'll ever run with Mono) * permission to retrieve the private key from the keychain. * * This code would also trigger constant keychain popup messages, * which could only be suppressed by granting full trust. It also makes it * impossible to run Mono in headless mode. * * SecIdentityCreate * ================= * * To avoid these problems, we are currently using an undocumented API * called SecIdentityRef() to avoid using the Mac keychain whenever a * X509Certificate2 with a private key is used. * * On iOS and XamMac, you can still provide the X509Certificate without * a private key - in this case, a keychain search will be performed (and you * may get a popup message on XamMac). */ #if MOBILE using (var secCert = new SecCertificate(certificate)) { return(SecKeyChain.FindIdentity(secCert, true)); } #else return(null); #endif }