I'm currently trying to connect to a LDAPS Server using the following VB.NET Code, which should set the right parameters and use the function seen below to verify the certificate when the Bind function is called.
The value of LdapHost is the IP, 10.100.11.10, and the value for LdapPort is 7636.
connection = new LdapConnection(new LdapDirectoryIdentifier(LdapHost, LdapPort));
connection.AuthType = 2; // Negotiate
connection.SessionOptions.SecureSocketLayer = true;
connection.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(VerifyServerCertificate);
//Both username and password are correct
connection.Credential = new System.Net.NetworkCredential(strUsername, strPassword);
connection.Bind();
This,
But upon trying to verify the Server Certificate, using the following code:
private bool VerifyServerCertificate(LdapConnection ldapConnection, X509Certificate certificate)
{
try
{
X509Certificate2 certificate2 = new X509Certificate2(certificate);
return certificate2.Verify();
}
catch (Exception ex)
{
throw new LdapException(9999, "Invalid certificate or path.");
}
}
It Errors out at the Bind function saying that it cannot connect to the LDAP Server at all with the message "The LDAP Server cannot be reached"
Although upon testing the connection via PowerShell, the Server is available just fine.
Is there something wrong with my verification method? Should I try a different approach entirely?
I have found the reason why the verification did not work.
Using
X509Chain chain = new X509Chain();
X509Certificate2 certificate2 = new X509Certificate2(certificate);
var chainBuilt = chain.Build(certificate2);
LogEvent("Val", 0, "Chain building status: " + chainBuilt);
if (chainBuilt == false) {
foreach (X509ChainStatus chainStatus in chain.ChainStatus)
LogEvent("Val", 0, "Chain error: " + chainStatus.Status + " " + chainStatus.StatusInformation);
chain.Reset();
return false;
} else {
chain.Reset();
return true;
}
if the verification fails helped me understand that the Root Certificate was not trusted on that specific server.
Furthermore, it told me that it could not reach the Revokation Server to check if the Certificate is still valid.
This couldn't be checked though, since the configuration uses a StartTLS certificate, which does not have a Revokation Server.
Therefore, I added
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreRootRevocationUnknown | X509VerificationFlags.IgnoreEndRevocationUnknown | X509VerificationFlags.IgnoreCtlSignerRevocationUnknown;
to ignore every property regarding the Revokation Server. It can now connect as intended.
Related
From my code below, I should be getting the certificate of the mail server "mailgw.th-nuernberg.de".
That didn't work and I get the error "the handshake failed due to an unexpected packet format" by calling the method "AuthenticateAsClient".
I tried the same code with the mail server "smtp.gmail.com" on port 993. That works and I get the full certificate.
The mail server "mailgw.th-nuernberg.de" exists but I don't know why Google's mail server is working and it isn't.
Here is my Code:
X509Certificate2 cert = null;
var client = new TcpClient("mailgw.th-nuernberg.de", 25);
var certValidation = new RemoteCertificateValidationCallback(delegate (object snd, X509Certificate certificate,
X509Chain chainLocal, SslPolicyErrors sslPolicyErrors)
{
return true; //Accept every certificate, even if it's invalid
});
// Create an SSL stream and takeover client's stream
using (var sslStream = new SslStream(client.GetStream(), true, certValidation))
{
sslStream.AuthenticateAsClient("mailgw.th-nuernberg.de", null, System.Security.Authentication.SslProtocols.Tls13 | System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11, true);
var serverCertificate = sslStream.RemoteCertificate;
cert = new X509Certificate2(serverCertificate);
//System.Diagnostics.Debug.WriteLine("Heruntergeladenes Zertifikat: " + cert);
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
//throw some fancy exception ;-)
}
Does anyone know what the problem is? What's the difference using the Google mail server instead of using the mail server from my University?
I am using IIS in my local machine for testing FTP with SSL connection. I am using the FluentFTP library for connecting to the FTP. I am using the following code to connect to the Server.
FtpClient conn = new FtpClient();
conn.Host = firewallSslDetails.Address;
conn.Credentials = new NetworkCredential(firewallSslDetails.UserName, firewallSslDetails.Password);
conn.SslProtocols = System.Security.Authentication.SslProtocols.Default;
X509Certificate2 cert = new X509Certificate2(#"C:\Users\BizTalk360\Desktop\FtpSites\ServerCert.cer");
conn.EncryptionMode = FtpEncryptionMode.Implicit;
conn.DataConnectionType = FtpDataConnectionType.AutoActive;
conn.DataConnectionEncryption = true;
conn.EnableThreadSafeDataConnections = false;
conn.ClientCertificates.Add(cert);
conn.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
conn.Connect();
The server is returning me with the following error.
FluentFTP.FtpCommandException: Policy requires SSL.; Win32 error: Access is denied.; Error details: SSL policy requires SSL for control channel.;
For connecting over FTP the above code is working fine and for FTP with SSL it is not working.
As you seem to be connecting to the default port 21 (no explicit port specified anywhere), you need to use the "Explicit" mode:
conn.EncryptionMode = FtpEncryptionMode.Explicit;
If the server uses a self-signed certificate, you may need to verify it programmatically. Do not blindly accept any certificate, as the answer by #Ivan does. That's a security flaw. Validate the specific certificate, e.g. by checking its fingerprint.
See FtpWebRequest "The remote certificate is invalid according to the validation procedure".
//try this ,
var cl = new FtpClient(Server, Port, User, Password);
cl.EncryptionMode = FtpEncryptionMode.Implicit;
cl.DataConnectionType = FtpDataConnectionType.AutoPassive;
cl.DataConnectionEncryption = true;
cl.SslProtocols = protocol;
cl.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
var cer = new X509Certificate2(certificate);
cl.ClientCertificates.Add(cer);
System.Net.ServicePointManager.ServerCertificateValidationCallback = ServerCertificateValidationCallback;
client.Connect();
void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
{
// add logic to test if certificate is valid here
e.Accept = true;
}
private bool ServerCertificateValidationCallback(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
}
Currently I am working in POC with CRUD operations using AmazonS3 Sdk for .net 3.5 version 3. I am trying to retrieve the Region Endpoint(Location) of the specific bucket name using secret key and Access Key and bucket name( has Location: EU (Frankfurt) (eu-central-1)). in order to establish connection
with AmazonS3 and perform CRUD operations
So I get the A WebException with status TrustFailure was thrown when I tried to get the Region Endpoint from share point(web page I create my own page using the master page of SharePoint) in order to create AmazonS3Client instance with Region Retrieve.
with the following code:
private string defaultAmazonHttpsHost = "https://s3.amazonaws.com";
private string defaultAmazonHttpHost = "http://s3.amazonaws.com";
private Amazon.RegionEndpoint GetRegionEndpoint(string bucket, BasicAWSCredentials amazonCredentials, bool useSSL)
{
Amazon.RegionEndpoint regiongEndpoint = null;
AmazonS3Config configurationClient = new AmazonS3Config();
configurationClient.UseHttp = !useSSL;
configurationClient.ServiceURL = useSSL ? defaultAmazonHttpsHost : defaultAmazonHttpHost;
try
{
using (AmazonS3Client clientConnection = new AmazonS3Client(amazonCredentials, configurationClient))
{
GetBucketLocationRequest locationRequest = new GetBucketLocationRequest();
locationRequest.BucketName = bucket;
string locationName = clientConnection.GetBucketLocation(locationRequest).Location.Value;
if (locationName.Equals("EU", StringComparison.InvariantCultureIgnoreCase))
{
regiongEndpoint = Amazon.RegionEndpoint.EUWest1;
}
else if (string.IsNullOrEmpty(locationName))
{
regiongEndpoint = Amazon.RegionEndpoint.USEast1;
}
else
{
regiongEndpoint = Amazon.RegionEndpoint.GetBySystemName(locationName);
}
}
}
catch (AmazonS3Exception amazonS3Exception)
{
throw amazonS3Exception;
}
catch (Exception unExpectedException)
{
throw unExpectedException;
}
return regiongEndpoint;
}
BasicAWSCredentials credentials = new BasicAWSCredentials("my access Key", "my secret key");
AmazonS3Config configurationAmazon = new AmazonS3Config();
configurationAmazon.RegionEndpoint = GetRegionEndpoint("bucketName", credentials, false);
AmazonS3Client _s3 = new AmazonS3Client(credentials, configurationAmazon );
My task Perform CRUD operations + test connection with AmazonS3 Sdk .net 3.5 version 3 , with the source information :
-secret key
- access key
- bucket Name
the strange is if this part code run(execute) since another Project (without share point interaction for example: Console Project) I do not get this exception) Do you know what is the problem?
I used the following before execute any request to amazonS3 and now it works as expected I think the problem was with the certificates that sharepoint is using .
ServicePointManager.ServerCertificateValidationCallback +=
delegate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
};
the post provide a explanation about it
The key point here is "TrustFailure". There's something wrong with the certificate. In my case, this error was caused because my company uses Websense, a web filter/security suite that intercepts and reissues https certificates for web traffic so it can spy on you. Even if you don't use anything like that, the bottom line is that your computer doesn't trust the issuer of the certificate being used by the remote computer. The server on which I was receiving this error did not have the correct certificate in its Trusted Root Certification Authorities. After importing the correct trusted root cert (Add trusted root certificate authority to local computer), I no longer received the error.
If you don't think this is the case, you can get more details on what the exception is by either writing the details to console or putting a breakpoint on the Console.WriteLine here and actually inspect the certificate and ssl errors:
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
.....
.....
//before you make the request
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate (
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine("Subject: " + certificate.Subject + ", Issuer: " + certificate.Issuer + ". SSL Errors: " + sslPolicyErrors.ToString());
return false;
};
The key point here is that you need to find the certificate issue and resolve it instead of leaving yourself vulnerable by ignoring all ssl errors.
TrustFailure can also be caused by the date being incorrect on the machine.
In my WCF self-hosting WebService using mutual certificate to validate the client, i set the CertificateValidationMode = PeerTrust but its seems ignored, since i can still execute the methods with some client wich i have deleted the corresponding certificate of the TrustedPeople server store.
Heres the host example:
static void Main()
{
var httpsUri = new Uri("https://192.168.0.57:xxx/HelloServer");
var binding = new WSHttpBinding
{
Security =
{
Mode = SecurityMode.Transport,
Transport = {ClientCredentialType = HttpClientCredentialType.Certificate}
};
var host = new ServiceHost(typeof(HelloWorld), httpsUri);
//This line is not working
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =X509CertificateValidationMode.PeerTrust;
host.AddServiceEndpoint(typeof(IHelloWorld), binding, string.Empty, httpsUri);
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"server.com");
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The client app:
static void Main(string[] args)
{
try
{
var c = new HelloWorld.HelloWorldClient();
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => true;
c.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"client.com");
Console.WriteLine(c.GetIp());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
I generate the server.com and the client.com with a RootCA certificate. This RootCA certificate is instaled on the trusted root store of the client and server.
The question is, i should not execute the GetIp() method if my client.com certificate is not in the TrustedPeople store of the server, right? But im executing it without any problems.
The question is, how to, in this scenario, validate the client certificate put its public key on TrustedPeople of server?
ps: In this MSDN article of Transport security with client certificate, theres a quote saying The server’s certificate must be trusted by the client and the client’s certificate must be trusted by the server. But i can execute the webmethods from client even if the client certificate isnt in the server TrustedPeople store.
My suggestion would be to use custom validation. This way you can set some breakpoints and watch the validation take place as well as see what other validation options you could come up with based on the data available throughout the validation process.
First make sure you have your binding requiring Certificate for Message Client Credentials. If you only use Certificate for Transport, the Client in my tests did not validate. This alone may fix your issue.
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType =
MessageCredentialType.Certificate;
To setup a custom validator follow the rest.
Replace:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.PeerTrust;
With:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.Custom;
host.Credentials.ClientCertificate.Authentication.CustomCertificateValidator =
new IssuerNameCertValidator("CN=client.com");
Then add this to create the custom validator and tweak as needed (this one validates based on Issuer):
public class IssuerNameCertValidator : X509CertificateValidator
{
string allowedIssuerName;
public IssuerNameCertValidator(string allowedIssuerName)
{
if (allowedIssuerName == null)
{
throw new ArgumentNullException("allowedIssuerName");
}
this.allowedIssuerName = allowedIssuerName;
}
public override void Validate(X509Certificate2 certificate)
{
// Check that there is a certificate.
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
// Check that the certificate issuer matches the configured issuer.
if (allowedIssuerName != certificate.IssuerName.Name)
{
throw new SecurityTokenValidationException
("Certificate was not issued by a trusted issuer");
}
}
}
My C#.NET SSL connect works when I import the certificate manually in IE (Tools/Internet Options/Content/Certificates), but how can I load the certificate by code?
Here is my code:
TcpClient client = new TcpClient(ConfigManager.SSLSwitchIP, Convert.ToInt32(ConfigManager.SSLSwitchPort));
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
sslStream.AuthenticateAsClient("Test");
The above code works fine if i import my certificate file manually in Internet Explorer. But if i remove my certificate from IE and use the following code instead, i get Authentication exception:
sslStream.AuthenticateAsClient("Test", GetX509CertificateCollection(), SslProtocols.Default, false);
and here is the 'GetX509CertificateCollection' method :
public static X509CertificateCollection GetX509CertificateCollection()
{
X509Certificate2 certificate1 = new X509Certificate2("c:\\ssl.txt");
X509CertificateCollection collection1 = new X509CertificateCollection();
collection1.Add(certificate1);
return collection1;
}
What should I do to load my certificate dynamically?
To build upon owlstead's answer, here's how I use a single CA certificate and a custom chain in the verification callback to avoid Microsoft's store.
I have not figured out how to use this chain (chain2 below) by default such that there's no need for the callback. That is, install it on the ssl socket and the connection will "just work". And I have not figured out how install it such that its passed into the callback. That is, I have to build the chain for each invocation of the callback. I think these are architectural defects in .Net, but I might be missing something obvious.
The name of the function does not matter. Below, VerifyServerCertificate is the same callback as RemoteCertificateValidationCallback. You can also use it for the ServerCertificateValidationCallback in ServicePointManager.
static bool VerifyServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
String CA_FILE = "ca-cert.der";
X509Certificate2 ca = new X509Certificate2(CA_FILE);
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// Check all properties
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Build the chain
chain2.Build(new X509Certificate2(certificate));
// Are there any failures from building the chain?
if (chain2.ChainStatus.Length == 0)
return true;
// If there is a status, verify the status is NoError
bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
Debug.Assert(result == true);
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
A quick Google pointed me to a piece of text from the Microsoft SslStream class.
The authentication is handled by the Security Support Provider (SSPI)
channel provider. The client is given an opportunity to control
validation of the server's certificate by specifying a
RemoteCertificateValidationCallback delegate when creating an
SslStream. The server can also control validation by supplying a
RemoteCertificateValidationCallback delegate. The method referenced by
the delegate includes the remote party's certificate and any errors
SSPI encountered while validating the certificate. Note that if the
server specifies a delegate, the delegate's method is invoked
regardless of whether the server requested client authentication. If
the server did not request client authentication, the server's
delegate method receives a null certificate and an empty array of
certificate errors.
So simply implement the delegate and do the verification yourself.
I wrote another method to add my certificate to Trusted Root Certification Authorities (root) before attempting to authenticate as client via SSLStream object:
public static void InstallCertificate()
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
string fileName = "sslcert.pem";
X509Certificate2 certificate1;
try
{
certificate1 = new X509Certificate2(fileName);
}
catch (Exception ex)
{
throw new Exception("Error loading SSL certificate file." + Environment.NewLine + fileName);
}
store.Add(certificate1);
store.Close();
}
And then:
InstallCertificate();
sslStream.AuthenticateAsClient("Test");
It works fine without any warnings or errors. But base question still remains unsolved:
How can I use a certificate to authenticate as client without installing it in Windows?