The remote certificate is invalid according to the validation procedure

FTP offers no encryption of any kind and hence to overcome the same, extension to FTP that is FTPS provides encryption for the username and password. It does this by using TLS, the same used to secure website. Now same as with websites you setup public and private keys and use certificates to verify trust with the host you are connecting.

If server uses a self-signed certificate then they do provide encryption but do not offer any trust. Hence when we connect to an FTP server using TLS with self-signed certificate we get a message that the connection is not trusted and connection is closed. To bypass this message we can disable certificate verification however in that case no checking is performed and we have no idea whom we are connecting to. This article describes how to verify Server certificate using the Ultimate .NET FTP Library.

Verifying Server’s Certificate

Ultimate FTP has better way to resolve it. It by default validates certificate received form the server automatically. However, we can extend the process for a specific purpose like asking the user whether to accept or reject the server's certificate. Following example demonstrates how to do that.

C#:

using System;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using ComponentPro.Net;
using ComponentPro.Security.Certificates;

...

static void Main()
{
	// Create a new class instance. 
	Ftp client = new Ftp();

	client.CertificateReceived += client_CertificateReceived;

	// Connect to the FTP server. 
	client.Connect("myserver", 21, SslSecurityMode.Explicit);

	// Authenticate. 
	client.Authenticate("userName", "password");

	// Do something here... 
	client.DownloadFile("/my remote file.dat", "my local file");

	// Disconnect. 
	client.Disconnect();
}

/// <summary> 
/// Returns all issues of the given certificate. 
/// </summary> 
/// <param name="status">The certificate verification result.</param> 
/// <param name="code">The error code.</param> 
/// <returns>Certificate problems.</returns> 
private static string GetCertProblem(CertificateVerificationStatus status, int code)
{
	switch (status)
	{
		case CertificateVerificationStatus.TimeNotValid:
			return "Server's certificate has expired or is not valid yet.";

		case CertificateVerificationStatus.Revoked:
			return "Server's certificate has been revoked.";

		case CertificateVerificationStatus.UnknownCa:
			return "Server's certificate was issued by an unknown authority.";

		case CertificateVerificationStatus.UntrustedRoot:
			return "Server's certificate was issued by an untrusted authority.";

		case CertificateVerificationStatus.IncompleteChain:
			return "Server's certificate does not chain up to a trusted root authority.";

		case CertificateVerificationStatus.Malformed:
			return "Server's certificate is malformed.";

		case CertificateVerificationStatus.CnNotMatch:
			return "Server hostname does not match the certificate.";

		case CertificateVerificationStatus.UnknownError:
			return string.Format("Error {0:x} encountered while validating server's certificate.", code);

		default:
			return status.ToString();
	}
}

static void client_CertificateReceived(object sender, ComponentPro.Security.CertificateReceivedEventArgs e)
{
	X509Certificate2 cert = e.ServerCertificates[0];

	CertificateVerificationStatus status = e.Status;

	CertificateVerificationStatus[] values = (CertificateVerificationStatus[])Enum.GetValues(typeof(CertificateVerificationStatus));

	StringBuilder sbIssues = new StringBuilder();
	for (int i = 0; i < values.Length; i++)
	{
		// Matches the validation status? 
		if ((status & values[i]) == 0)
			continue;

		// The issue is processed. 
		status ^= values[i];

		sbIssues.AppendFormat("{0}\r\n", GetCertProblem(values[i], e.ErrorCode));
	}

	Console.WriteLine("Issue: " + sbIssues.ToString());

	Console.WriteLine("Subject: " + cert.SubjectName.Name);
	Console.WriteLine("Issuer: " + cert.IssuerName.Name);
	Console.WriteLine("Effective Date: " + cert.NotBefore);
	Console.WriteLine("Expiry Date: " + cert.NotAfter);
	Console.ResetColor();
	Console.Write("Do you want to accept this certificate (Add to trusted list, Yes, No) [a,y,n]?");

	string response = Console.ReadLine().Trim().ToLower();

	// Add certiticate of the issuer CA to the trusted list. 
	if (response == "a")
	{
		e.AddToTrustedRoot = true;
	}
	else if (response == "y")
	{
		e.Accept = true;
	}
}

VB.NET:

Imports System.Security.Cryptography.X509Certificates
Imports System.Text
Imports ComponentPro.Net
Imports ComponentPro.Security.Certificates

...

Shared Sub Main()
	' Create a new class instance. 
	Dim client As New Ftp()

	AddHandler client.CertificateReceived, AddressOf client_CertificateReceived

	' Connect to the FTP server. 
	client.Connect("myserver", 21, SslSecurityMode.Explicit)

	' Authenticate. 
	client.Authenticate("userName", "password")

	' Do something here... 
	client.DownloadFile("/my remote file.dat", "my local file")

	' Disconnect. 
	client.Disconnect()
End Sub 
 
''' <summary> 
''' Returns all issues of the given certificate. 
''' </summary> 
''' <param name="status">The certificate verification result.</param> 
''' <param name="code">The error code.</param> 
''' <returns>Certificate problems.</returns> 
Private Shared Function GetCertProblem(ByVal status As CertificateVerificationStatus, ByVal code As Integer) As String 
	Select Case status
		Case CertificateVerificationStatus.TimeNotValid
			Return "Server's certificate has expired or is not valid yet." 
 
		Case CertificateVerificationStatus.Revoked
			Return "Server's certificate has been revoked." 
 
		Case CertificateVerificationStatus.UnknownCa
			Return "Server's certificate was issued by an unknown authority." 
 
		Case CertificateVerificationStatus.UntrustedRoot
			Return "Server's certificate was issued by an untrusted authority." 
 
		Case CertificateVerificationStatus.IncompleteChain
			Return "Server's certificate does not chain up to a trusted root authority." 
 
		Case CertificateVerificationStatus.Malformed
			Return "Server's certificate is malformed." 
 
		Case CertificateVerificationStatus.CnNotMatch
			Return "Server hostname does not match the certificate." 
 
		Case CertificateVerificationStatus.UnknownError
			Return String.Format("Error {0:x} encountered while validating server's certificate.", code)

		Case Else 
			Return status.ToString()
	End Select 
End Function 
 
Private Shared Sub client_CertificateReceived(ByVal sender As Object, ByVal e As ComponentPro.Security.CertificateReceivedEventArgs)
	Dim cert As X509Certificate2 = e.ServerCertificates(0)

	Dim status As CertificateVerificationStatus = e.Status

	Dim values() As CertificateVerificationStatus = CType(System.Enum.GetValues(GetType(CertificateVerificationStatus)), CertificateVerificationStatus())

	Dim sbIssues As New StringBuilder()
	For i As Integer = 0 To values.Length - 1
		' Matches the validation status? 
		If (status And values(i)) = 0 Then 
			Continue For 
		End If 
 
		' The issue is processed. 
		status = status Xor values(i)

		sbIssues.AppendFormat("{0}" & vbCrLf, GetCertProblem(values(i), e.ErrorCode))
	Next i

	Console.WriteLine("Issue: " & sbIssues.ToString())

	Console.WriteLine("Subject: " & cert.SubjectName.Name)
	Console.WriteLine("Issuer: " & cert.IssuerName.Name)
	Console.WriteLine("Effective Date: " & cert.NotBefore)
	Console.WriteLine("Expiry Date: " & cert.NotAfter)
	Console.ResetColor()
	Console.Write("Do you want to accept this certificate (Add to trusted list, Yes, No) [a,y,n]?")

	Dim response As String = Console.ReadLine().Trim().ToLower()

	' Add certiticate of the issuer CA to the trusted list. 
	If response = "a" Then 
		e.AddToTrustedRoot = True 
	ElseIf response = "y" Then 
		e.Accept = True 
	End If 
End Sub

45-Day Money Back Guarantee

We will refund your full money in 45 days
if you are not satisfied with our products

Buy Now

Dont miss out Get update on new articles and other opportunities