Xceed .NET Libraries Documentation
Secure FTP (SSL/TLS)

Welcome to Xceed .NET, .NET Standard and Xamarin Libraries! > Basic Concepts > FTP capabilities > Secure FTP (SSL/TLS)

As of version 2.0, Xceed FTP for .NET supports both SSL 3.0 and TLS (SSL 3.1). SSL and TLS are protocols layered above connection protocols (such as TCP /IP ) but beneath application protocols (such as FTP) that provide encrypted, authenticated communications between a client and a server. Note: Secure FTP is not supported by Xceed FTP for .NET Compact Framework.

Implicit versus explicit SSL connections (per RFC 2228)

Connecting securely and authenticating are two distinct methods of establishing a secure connection with an FTP server. In the first case, the Secure FTP server may require an SSL connection to be established first, before it sends its initial welcome message. This is called an implicit SSL connection. In the second case,   the connection is established in clear text and a special FTP command must be sent to the Secure FTP server to change the connection into a secure connection. This is called an explicit SSL connection. 

In most cases, FTP servers that support SSL authentication will accept a normal connection on port 21. Once the connection is established, it is necessary to authenticate before logging in, using the Authenticate method. When securing the connection explicitly, it is also possible to secure data connections using the overload of the Authenticate method which requires a DataChannelProtection enum as a parameter. 

Servers that require an implicit SSL connection usually listen on port 990 rather than 21.

Certificates

A certificate is a digitally signed statement from one entity (person, company, etc.) that states that the public key of another entity has a particular value. Trusting the certificate's signature implies that you trust that the association in the certificate between the specified public key and the other entity is authentic. 

The certificate that is received from the FTP server is verified against the VerificationFlags provided at connection or authentication. By default, if a certificate received from an FTP server contains anomalies, it will be rejected. If no anomalies are detected, it will be accepted. 

To reduce the severity with which the FTP server's certificate is verified, the CertificateReceived event must be handled and the verification flags modified. Once the verification flags have been modified, the verification action must be set to VerifyAgain to verify the server's certificate again. To indiscriminately accept the certificate received from the FTP sever, regardless of anomalies, the verification action must be set to Accept. 

Keep in mind that the server certificate that was accepted during the command channel connection will be the only certificate that will be accepted during the data channel connection. 

Client certificates can be sent to the FTP server when connecting (implicit SSL), when authenticating (explicit SSL), or via the CertificateRequired event. If the FTP server rejects the client certificate (or no certificate was provided when connecting or authenticating), the CertificateRequired event will be raised allowing a new certificate to be provided. If the same invalid certificate is provided in the CertificateReceived event, an exception will be thrown.

Client certificates are often optional. It depends on how the FTP server has been configured. If the CertificateRequired event is triggered, the server is informing you that you must supply a certificate for the connection to be successful.

If different invalid certificates are provided in the CertificateRequired event, a loop will occur!

Keep in mind that the client certificate that was used for the command channel connection will be the same one that is used for the data channel connection.

SSL demonstration

 Example: Implicit SSL connection

The following example demonstrates how to use an implicit SSL connection to securely connect to, log into, and disconnect from a secure FTP server using the FtpClient interface. We will handle the CertificateReceived event to validate the certificate received from the FTP server. For an explicit SSL connection demonstration, refer to the Quick Tour Sample. An example using the FtpConnection class follows below.

static void ImplicitSSLExample()

{

  try

  {

    FtpClient ftp = new FtpClient();

    //ftp.TraceWriter = Console.Out;



    // Subscribe to the CertificateReceived event

    ftp.CertificateReceived += new CertificateReceivedEventHandler( OnCertificateReceived );



    // Pick an authentication method

    AuthenticationMethod authenticationMethod = AuthenticationMethod.Ssl;



    // Pick verification flags. If unsure, pick 'None'.

    VerificationFlags verificationFlags = VerificationFlags.None;



    // Supply a client certificate to submit to the server. This example doesn't use one

    Certificate clientCertificate = null;



    // Connect implicitly to the server using encryption. Notice the port number reserved for this

    // This form always enables encryption for the data channel (for file transfers)

    ftp.Connect( "localhost", 990, authenticationMethod, verificationFlags, clientCertificate );



    try

    {

      // Login. The exchanged information will be encrypted

      ftp.Login( "username", "password" );



      /* Perform your file transfers */

    }

    finally

    {

      // Make sure we always disconnect

      ftp.Disconnect();



      ftp.CertificateReceived -= new CertificateReceivedEventHandler( OnCertificateReceived );

    }

  }

  catch( Exception exception )

  {

    // Output some information about it

    Console.WriteLine( "-->{0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



    // Fetch the inner exception

    exception = exception.InnerException;



    // While there is an exception

    while( exception != null )

    {

      // Output some information about it

      Console.WriteLine( "-->Inner exception: {0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



      // Fetch the inner exception

      exception = exception.InnerException;

    }

  }

}



static void OnCertificateReceived( object sender, CertificateReceivedEventArgs e )

{

  // The Status argument property tells you if the server certificate was accepted

  // based on the VerificationFlags provided in the call to Connect().

  if( e.Status != VerificationStatus.ValidCertificate )

  {

    Console.WriteLine( "The server certificate is invalid: {0}", e.Status.ToString() );

    Console.WriteLine( e.ServerCertificate.ToString() );



    // You have three choices here:

    //

    //  1) Refuse the certificate by setting e.Action to VerificationAction.Reject,

    //      thus making the authentication fail. This is e.Action's default value

    //      when the server certificate isn't valid.

    //

    //  2) Set e.Flags to less restrictive criterion and ask the library to

    //      validate the certificate again by setting e.Action to

    //      VerificationAction.VerifyAgain.

    //

    //  3) Force the library to accept this certificate by setting e.Action to

    //      VerificationAction.Accept.

    //

    // We'll do #1 or #3, depending on the user's answer.



    Console.WriteLine( "Do you want to accept this certificate anyway? [Y/N]" );



    int answer = Console.Read();

    if( ( answer == 'y' ) || ( answer == 'Y' ) )

    {

      e.Action = VerificationAction.Accept;

    }

  }

  else

  {

    // e.Action's default value is VerificationAction.Accept

    Console.WriteLine( "Valid certificate received from server." );

  }

}
Private Shared Sub ImplicitSSLExample()

  Try

    Dim ftp As New FtpClient()

    'ftp.TraceWriter = Console.Out;



    ' Subscribe to the CertificateReceived event

    AddHandler ftp.CertificateReceived, AddressOf OnCertificateReceived



    ' Pick an authentication method

    Dim authenticationMethod As AuthenticationMethod = AuthenticationMethod.Ssl



    ' Pick verification flags. If unsure, pick 'None'.

    Dim verificationFlags As VerificationFlags = VerificationFlags.None



    ' Supply a client certificate to submit to the server. This example doesn't use one

    Dim clientCertificate As Certificate = Nothing



    ' Connect implicitly to the server using encryption. Notice the port number reserved for this

    ' This form always enables encryption for the data channel (for file transfers)

    ftp.Connect("localhost", 990, authenticationMethod, verificationFlags, clientCertificate)



    Try

      ' Login. The exchanged information will be encrypted

      ftp.Login("username", "password")



      ' Perform your file transfers 

    Finally

      ' Make sure we always disconnect

      ftp.Disconnect()



      RemoveHandler ftp.CertificateReceived, AddressOf OnCertificateReceived

    End Try

  Catch exception As Exception

    ' Output some information about it

    Console.WriteLine("-->{0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



    ' Fetch the inner exception

    exception = exception.InnerException



    ' While there is an exception

    Do While exception IsNot Nothing

      ' Output some information about it

      Console.WriteLine("-->Inner exception: {0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



      ' Fetch the inner exception

      exception = exception.InnerException

    Loop

  End Try

End Sub



Private Shared Sub OnCertificateReceived(ByVal sender As Object, ByVal e As CertificateReceivedEventArgs)

  ' The Status argument property tells you if the server certificate was accepted

  ' based on the VerificationFlags provided in the call to Connect().

  If e.Status <> VerificationStatus.ValidCertificate Then

    Console.WriteLine("The server certificate is invalid: {0}", e.Status.ToString())

    Console.WriteLine(e.ServerCertificate.ToString())



    ' You have three choices here:

    '

    '  1) Refuse the certificate by setting e.Action to VerificationAction.Reject,

    '      thus making the authentication fail. This is e.Action's default value

    '      when the server certificate isn't valid.

    '

    '  2) Set e.Flags to less restrictive criterion and ask the library to

    '      validate the certificate again by setting e.Action to

    '      VerificationAction.VerifyAgain.

    '

    '  3) Force the library to accept this certificate by setting e.Action to

    '      VerificationAction.Accept.

    '

    ' We'll do #1 or #3, depending on the user's answer.



    Console.WriteLine("Do you want to accept this certificate anyway? [Y/N]")



    Dim answer As Integer = Console.Read()

    If (answer = AscW("y"c)) OrElse (answer = AscW("Y"c)) Then

      e.Action = VerificationAction.Accept

    End If

  Else

    ' e.Action's default value is VerificationAction.Accept

    Console.WriteLine("Valid certificate received from server.")

  End If

End Sub
 Example: Explicit SSL connection

Here, an explicit SSL connection is made:

static void ExplicitSSLExample()

{

  try

  {

    FtpClient ftp = new FtpClient();

    //ftp.TraceWriter = Console.Out;



    // Subscribe to the CertificateReceived event

    ftp.CertificateReceived += new CertificateReceivedEventHandler( OnCertificateReceived );



    // Connect to the server normally, unencrypted, at the usual ftp port

    ftp.Connect( "localhost", 21 );



    try

    {

      // Pick an authentication method

      AuthenticationMethod authenticationMethod = AuthenticationMethod.Ssl;



      // Pick verification flags. If unsure, pick 'None'.

      VerificationFlags verificationFlags = VerificationFlags.None;



      // Supply a client certificate to submit to the server. This example doesn't use one

      Certificate clientCertificate = null;



      // Decide if the data channel (for file transfers) will be encrypted or not

      DataChannelProtection dataChannelProtection = DataChannelProtection.Private;



      // Authenticate and encrypt the connection

      ftp.Authenticate( authenticationMethod, verificationFlags, clientCertificate, dataChannelProtection );



      // Login. The exchanged information will be encrypted

      ftp.Login( "username", "password" );



      /* Perform your file transfers */

    }

    finally

    {

      // Make sure we always disconnect

      ftp.Disconnect();



      ftp.CertificateReceived -= new CertificateReceivedEventHandler( OnCertificateReceived );

    }

  }

  catch( Exception exception )

  {

    // Output some information about it

    Console.WriteLine( "-->{0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



    // Fetch the inner exception

    exception = exception.InnerException;



    // While there is an exception

    while( exception != null )

    {

      // Output some information about it

      Console.WriteLine( "-->Inner exception: {0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



      // Fetch the inner exception

      exception = exception.InnerException;

    }

  }

}



static void OnCertificateReceived( object sender, CertificateReceivedEventArgs e )

{

  // The Status argument property tells you if the server certificate was accepted

  // based on the VerificationFlags provided in the call to Connect().

  if( e.Status != VerificationStatus.ValidCertificate )

  {

    Console.WriteLine( "The server certificate is invalid: {0}", e.Status.ToString() );

    Console.WriteLine( e.ServerCertificate.ToString() );



    // You have three choices here:

    //

    //  1) Refuse the certificate by setting e.Action to VerificationAction.Reject,

    //      thus making the authentication fail. This is e.Action's default value

    //      when the server certificate isn't valid.

    //

    //  2) Set e.Flags to less restrictive criterion and ask the library to

    //      validate the certificate again by setting e.Action to

    //      VerificationAction.VerifyAgain.

    //

    //  3) Force the library to accept this certificate by setting e.Action to

    //      VerificationAction.Accept.

    //

    // We'll do #1 or #3, depending on the user's answer.



    Console.WriteLine( "Do you want to accept this certificate anyway? [Y/N]" );



    int answer = Console.Read();

    if( ( answer == 'y' ) || ( answer == 'Y' ) )

    {

      e.Action = VerificationAction.Accept;

    }

  }

  else

  {

    // e.Action's default value is VerificationAction.Accept

    Console.WriteLine( "Valid certificate received from server." );

  }

}
Private Shared Sub ExplicitSSLExample()

  Try

    Dim ftp As New FtpClient()

    'ftp.TraceWriter = Console.Out;



    ' Subscribe to the CertificateReceived event

    AddHandler ftp.CertificateReceived, AddressOf OnCertificateReceived



    ' Connect to the server normally, unencrypted, at the usual ftp port

    ftp.Connect("localhost", 21)



    Try

      ' Pick an authentication method

      Dim authenticationMethod As AuthenticationMethod = AuthenticationMethod.Ssl



      ' Pick verification flags. If unsure, pick 'None'.

      Dim verificationFlags As VerificationFlags = VerificationFlags.None



      ' Supply a client certificate to submit to the server. This example doesn't use one

      Dim clientCertificate As Certificate = Nothing



      ' Decide if the data channel (for file transfers) will be encrypted or not

      Dim dataChannelProtection As DataChannelProtection = DataChannelProtection.Private



      ' Authenticate and encrypt the connection

      ftp.Authenticate(authenticationMethod, verificationFlags, clientCertificate, dataChannelProtection)



      ' Login. The exchanged information will be encrypted

      ftp.Login("username", "password")



      ' Perform your file transfers 

    Finally

      ' Make sure we always disconnect

      ftp.Disconnect()



      RemoveHandler ftp.CertificateReceived, AddressOf OnCertificateReceived

    End Try

  Catch exception As Exception

    ' Output some information about it

    Console.WriteLine("-->{0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



    ' Fetch the inner exception

    exception = exception.InnerException



    ' While there is an exception

    Do While exception IsNot Nothing

      ' Output some information about it

      Console.WriteLine("-->Inner exception: {0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



      ' Fetch the inner exception

      exception = exception.InnerException

    Loop

  End Try

End Sub



Private Shared Sub OnCertificateReceived(ByVal sender As Object, ByVal e As CertificateReceivedEventArgs)

  ' The Status argument property tells you if the server certificate was accepted

  ' based on the VerificationFlags provided in the call to Connect().

  If e.Status <> VerificationStatus.ValidCertificate Then

    Console.WriteLine("The server certificate is invalid: {0}", e.Status.ToString())

    Console.WriteLine(e.ServerCertificate.ToString())



    ' You have three choices here:

    '

    '  1) Refuse the certificate by setting e.Action to VerificationAction.Reject,

    '      thus making the authentication fail. This is e.Action's default value

    '      when the server certificate isn't valid.

    '

    '  2) Set e.Flags to less restrictive criterion and ask the library to

    '      validate the certificate again by setting e.Action to

    '      VerificationAction.VerifyAgain.

    '

    '  3) Force the library to accept this certificate by setting e.Action to

    '      VerificationAction.Accept.

    '

    ' We'll do #1 or #3, depending on the user's answer.



    Console.WriteLine("Do you want to accept this certificate anyway? [Y/N]")



    Dim answer As Integer = Console.Read()

    If (answer = AscW("y"c)) OrElse (answer = AscW("Y"c)) Then

      e.Action = VerificationAction.Accept

    End If

  Else

    ' e.Action's default value is VerificationAction.Accept

    Console.WriteLine("Valid certificate received from server.")

  End If

End Sub

Client Certificates

Some FTP servers are configured to require the client to supply a client certificate when connecting using SSL/TLS.  Unfortunately, there is no way to know the requirement before making a connection. The FTP server administrator must communicate this requirement to you. The administrator will also provide you with the certificate that must be sent to the server when connection.

The .NET framework exposes certificates using the %System.Security.Cryptography.X509Certificates.X509Certificate2% class. The FTP component wraps this class into the Certificate class. The class can load X509 certificate files encoded in the DER or Base64 encoding. Most certificate files are encrypted and require a password, often called the private key, to decode them.

The .NET framework also supports the PKCS #7 file format with the %System.Security.Cryptography.Pkcs.SignedCms% class. In that case, however, a certificate file might contain more than one certificate so care will need to be taken when handling such files.

 Example: Client certificate from an encrypted DER file

The following example shows the use of the CertificateRequired Event to supply a client certificate when the server requires one.

static void ClientCertificateExample()

{

  try

  {

    FtpClient ftp = new FtpClient();

    //ftp.TraceWriter = Console.Out;



    // Subscribe to the CertificateReceived event

    ftp.CertificateReceived += new CertificateReceivedEventHandler( OnCertificateReceived );



    // Subscribe to the CertificateRequired event

    ftp.CertificateRequired += new CertificateRequiredEventHandler( OnCertificateRequired );



    // Connect to the server normally, unencrypted, at the usual ftp port

    ftp.Connect( "localhost", 21 );



    try

    {

      // Pick an authentication method

      AuthenticationMethod authenticationMethod = AuthenticationMethod.Ssl;



      // Pick verification flags. If unsure, pick 'None'.

      VerificationFlags verificationFlags = VerificationFlags.None;



      /* In this example, we will not provide a client certificate at Connect(). Instead,

       * we subscribe to the CertificateRequired event and if the FTP server asks us for

       * a certificate, we will provide one at that time. 

       * 

       * That way, if the server is not configured to expect a certificate, it 

       * won't receive one for no reason. */



      // The client certificate to submit to the server

      Certificate clientCertificate = null;



      // Decide if the data channel (for file transfers) will be encrypted or not

      DataChannelProtection dataChannelProtection = DataChannelProtection.Private;



      // Authenticate and encrypt the connection

      ftp.Authenticate( authenticationMethod, verificationFlags, clientCertificate, dataChannelProtection );



      // Login. The exchanged information will be encrypted

      ftp.Login( "username", "password" );



      /* Perform your file transfers */

    }

    finally

    {

      // Make sure we always disconnect

      ftp.Disconnect();



      ftp.CertificateRequired -= new CertificateRequiredEventHandler( OnCertificateRequired );

      ftp.CertificateReceived -= new CertificateReceivedEventHandler( OnCertificateReceived );

    }

  }

  catch( Exception exception )

  {

    // Output some information about it

    Console.WriteLine( "-->{0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



    // Fetch the inner exception

    exception = exception.InnerException;



    // While there is an exception

    while( exception != null )

    {

      // Output some information about it

      Console.WriteLine( "-->Inner exception: {0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



      // Fetch the inner exception

      exception = exception.InnerException;

    }

  }

}



static void OnCertificateRequired( object sender, CertificateRequiredEventArgs e )

{

  try

  {

    /* The .NET framework exposes certificates using the X509Certificate2 class.

     * The FTP component wraps this class into the Certificate class.

     * The class can load X509 certificate files encoded in the DER or Base64 encoding.

     * Most certificate files are encrypted and require a password, often called

     * the private key, to decode them. */



    // Load a certificate file to submit to the server

    Certificate clientCertificate = new Certificate( @"D:\Xceed\MyX509Certificate.der", "certificate password" );



    e.Certificate = clientCertificate;

  }

  catch( System.Security.Cryptography.CryptographicException )

  {

    // Trigger failure by not providing a certificate

    e.Certificate = null;

  }

}



static void OnCertificateReceived( object sender, CertificateReceivedEventArgs e )

{

  // Always accept the certificate

  e.Action = VerificationAction.Accept;

}
    Private Shared Sub ClientCertificateExample()

      Try

        Dim ftp As New FtpClient()

        'ftp.TraceWriter = Console.Out;



        ' Subscribe to the CertificateReceived event

        AddHandler ftp.CertificateReceived, AddressOf OnCertificateReceived



        ' Subscribe to the CertificateRequired event

        AddHandler ftp.CertificateRequired, AddressOf OnCertificateRequired



        ' Connect to the server normally, unencrypted, at the usual ftp port

        ftp.Connect("localhost", 21)



        Try

          ' Pick an authentication method

          Dim authenticationMethod As AuthenticationMethod = AuthenticationMethod.Ssl



          ' Pick verification flags. If unsure, pick 'None'.

          Dim verificationFlags As VerificationFlags = VerificationFlags.None



'           In this example, we will not provide a client certificate at Connect(). Instead,

'           * we subscribe to the CertificateRequired event and if the FTP server asks us for

'           * a certificate, we will provide one at that time. 

'           * 

'           * That way, if the server is not configured to expect a certificate, it 

'           * won't receive one for no reason. 



          ' The client certificate to submit to the server

          Dim clientCertificate As Certificate = Nothing



          ' Decide if the data channel (for file transfers) will be encrypted or not

          Dim dataChannelProtection As DataChannelProtection = DataChannelProtection.Private



          ' Authenticate and encrypt the connection

          ftp.Authenticate(authenticationMethod, verificationFlags, clientCertificate, dataChannelProtection)



          ' Login. The exchanged information will be encrypted

          ftp.Login("username", "password")



          ' Perform your file transfers 

        Finally

          ' Make sure we always disconnect

          ftp.Disconnect()



          RemoveHandler ftp.CertificateRequired, AddressOf OnCertificateRequired

          RemoveHandler ftp.CertificateReceived, AddressOf OnCertificateReceived

        End Try

      Catch exception As Exception

        ' Output some information about it

        Console.WriteLine("-->{0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



        ' Fetch the inner exception

        exception = exception.InnerException



        ' While there is an exception

        Do While exception IsNot Nothing

          ' Output some information about it

          Console.WriteLine("-->Inner exception: {0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



          ' Fetch the inner exception

          exception = exception.InnerException

        Loop

      End Try

    End Sub



    Private Shared Sub OnCertificateRequired(ByVal sender As Object, ByVal e As CertificateRequiredEventArgs)

      Try

'         The.NET framework exposes certificates using the X509Certificate2 class.

'         * The FTP component wraps this class into the Certificate class.

'         * The class can load X509 certificate files encoded in the DER or Base64 encoding.

'         * Most certificate files are encrypted and require a password, often called

'         * the private key, to decode them. 



        ' Load a certificate file to submit to the server

        Dim clientCertificate As New Certificate("D:\Xceed\MyX509Certificate.der", "certificate password")



        e.Certificate = clientCertificate

      Catch e1 As System.Security.Cryptography.CryptographicException

        ' Trigger failure by not providing a certificate

        e.Certificate = Nothing

      End Try

    End Sub



    Private Shared Sub OnCertificateReceived(ByVal sender As Object, ByVal e As CertificateReceivedEventArgs)

      ' Always accept the certificate

      e.Action = VerificationAction.Accept

    End Sub
 Example: Client certificate from a PKCS #7 file

Finally, this example shows the usage of the %System.Security.Cryptography.Pkcs.SignedCms% class to select a single certificate from a PKCS #7 file.

static void ClientCertificateExample()

{

  try

  {

    // Load the certificate file into a byte array

    byte[] certificateBytes = File.ReadAllBytes( @"D:\Xceed\PKCS7Certificates.p7b" );



    // Create an object that can decode the certificate data

    System.Security.Cryptography.Pkcs.SignedCms cms = new System.Security.Cryptography.Pkcs.SignedCms();

    

    // Decode the certificates

    cms.Decode( certificateBytes );



    FtpClient ftp = new FtpClient();

    //ftp.TraceWriter = Console.Out;



    string host = "localhost";

    int port = 990;



    // Pick an authentication method

    AuthenticationMethod authenticationMethod = AuthenticationMethod.Ssl;



    // Pick verification flags. If unsure, pick 'None'.

    VerificationFlags verificationFlags = VerificationFlags.None;



    // The client certificate to submit to the server

    Certificate clientCertificate = null;



    // Subscribe to the CertificateReceived event

    ftp.CertificateReceived += new CertificateReceivedEventHandler( OnCertificateReceived );



    /* A PKCS #7 file can contain more than one certificate. The certificates are in the collection

     * specified in the cms.Certificates property.

     * 

     * If you know which certificate the FTP server is waiting for, simply supply it to

     * the Connect() method. If you do not know which is the correct one, you can try to connect

     * using each one in turn until the server accepts one. */

     

    // Go through each certificate in the collection

    foreach( System.Security.Cryptography.X509Certificates.X509Certificate2 x509certificate in cms.Certificates )

    {

      // Create a client certificate out of the current x509 certificate

      clientCertificate = new Certificate( x509certificate );



      try

      {

        // Connect implicitly to the server using encryption.

        // This form always enables encryption for the data channel (for file transfers)

        ftp.Connect( host, port, authenticationMethod, verificationFlags, clientCertificate );

      }

      catch( FtpSslException )

      {

        // An FtpSslException exception will be thrown if the client certificate is rejected

      }



      // If we connected successfully to the server

      if( ftp.Connected )

      {

        // No need to try again

        break;

      }

    }



    try

    {

      // Login. The exchanged information will be encrypted

      ftp.Login( "username", "password" );



      /* Perform your file transfers */

    }

    finally

    {

      // Make sure we always disconnect

      ftp.Disconnect();



      ftp.CertificateReceived -= new CertificateReceivedEventHandler( OnCertificateReceived );

    }

  }

  catch( Exception exception )

  {

    // Output some information about it

    Console.WriteLine( "-->{0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



    // Fetch the inner exception

    exception = exception.InnerException;



    // While there is an exception

    while( exception != null )

    {

      // Output some information about it

      Console.WriteLine( "-->Inner exception: {0}: {1}\n{2}", exception.GetType().Name, exception.Message, exception.StackTrace );



      // Fetch the inner exception

      exception = exception.InnerException;

    }

  }

}



static void OnCertificateReceived( object sender, CertificateReceivedEventArgs e )

{

  // Always accept the certificate

  e.Action = VerificationAction.Accept;

}
    Private Shared Sub ClientCertificateExample()

      Try

        ' Load the certificate file into a byte array

        Dim certificateBytes() As Byte = File.ReadAllBytes("D:\Xceed\PKCS7Certificates.p7b")



        ' Create an object that can decode the certificate data

        Dim cms As New System.Security.Cryptography.Pkcs.SignedCms()



        ' Decode the certificates

        cms.Decode(certificateBytes)



        Dim ftp As New FtpClient()

        'ftp.TraceWriter = Console.Out;



        Dim host As String = "localhost"

        Dim port As Integer = 990



        ' Pick an authentication method

        Dim authenticationMethod As AuthenticationMethod = AuthenticationMethod.Ssl



        ' Pick verification flags. If unsure, pick 'None'.

        Dim verificationFlags As VerificationFlags = VerificationFlags.None



        ' The client certificate to submit to the server

        Dim clientCertificate As Certificate = Nothing



        ' Subscribe to the CertificateReceived event

        AddHandler ftp.CertificateReceived, AddressOf OnCertificateReceived



'         A PKCS #7 file can contain more than one certificate. The certificates are in the collection

'         * specified in the cms.Certificates property.

'         * 

'         * If you know which certificate the FTP server is waiting for, simply supply it to

'         * the Connect() method. If you do not know which is the correct one, you can try to connect

'         * using each one in turn until the server accepts one. 



        ' Go through each certificate in the collection

        For Each x509certificate As System.Security.Cryptography.X509Certificates.X509Certificate2 In cms.Certificates

          ' Create a client certificate out of the current x509 certificate

          clientCertificate = New Certificate(x509certificate)



          Try

            ' Connect implicitly to the server using encryption.

            ' This form always enables encryption for the data channel (for file transfers)

            ftp.Connect(host, port, authenticationMethod, verificationFlags, clientCertificate)

          Catch e1 As FtpSslException

            ' An FtpSslException exception will be thrown if the client certificate is rejected

          End Try



          ' If we connected successfully to the server

          If ftp.Connected Then

            ' No need to try again

            Exit For

          End If

        Next x509certificate



        Try

          ' Login. The exchanged information will be encrypted

          ftp.Login("username", "password")



          ' Perform your file transfers 

        Finally

          ' Make sure we always disconnect

          ftp.Disconnect()



          RemoveHandler ftp.CertificateReceived, AddressOf OnCertificateReceived

        End Try

      Catch exception As Exception

        ' Output some information about it

        Console.WriteLine("-->{0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



        ' Fetch the inner exception

        exception = exception.InnerException



        ' While there is an exception

        Do While exception IsNot Nothing

          ' Output some information about it

          Console.WriteLine("-->Inner exception: {0}: {1}" & Constants.vbLf & "{2}", exception.GetType().Name, exception.Message, exception.StackTrace)



          ' Fetch the inner exception

          exception = exception.InnerException

        Loop

      End Try

    End Sub



    Private Shared Sub OnCertificateReceived(ByVal sender As Object, ByVal e As CertificateReceivedEventArgs)

      ' Always accept the certificate

      e.Action = VerificationAction.Accept

    End Sub
See Also