Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FTP.DownloadFiles - Added search for certifications #34

Merged
merged 4 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Frends.FTP.DownloadFiles/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [1.1.0] - 2023-09-12
### Added
- Added search for local certificates from machine certification store.
- [Breaking] Added parameters ClientCertificationName and ClientCertificationThumbprint for exclusive search of client certification.

## [1.0.4] - 2023-08-08
### Changed
- Moved client.EncryptionMode setting to be done if UseFTPS is enabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,42 @@ public void DownloadFTPS_IncorrectFingerprint()
Assert.AreEqual(1, ex.InnerExceptions.Count);
Assert.AreEqual(typeof(AuthenticationException), ex.InnerExceptions[0].GetType());
}

[Test]
public void DownloadFTPS_CurrentUserHasNoCertificates()
{
// Setup
FtpHelper.CreateFileOnFTP(FtpDir, "file1.txt");
var source = new Source { Directory = FtpDir, FileName = "file1.txt" };
var destination = new Destination { Directory = LocalDirFullPath, Action = DestinationAction.Overwrite };

// Our test certificate hashes:
// SHA-256: 90:bc:7f:71:14:f5:c2:ad:03:46:d6:ff:75:d5:fe:12:ba:74:23:73:54:31:70:60:b4:8b:bd:8e:87:21:9c:16
// SHA-1: d9:11:26:29:84:de:9c:c3:2a:35:18:a1:09:4c:d2:42:49:ea:5c:49
var connection = new Connection
{
Address = FtpHelper.FtpHost,
UserName = FtpHelper.FtpUsername,
Password = FtpHelper.FtpPassword,
Port = FtpHelper.FtpsPort,
SslMode = FtpsSslMode.Explicit,
EnableClientAuth = true,
UseFTPS = true,
ValidateAnyCertificate = false,
CertificateHashStringSHA1 = "",
ClientCertificatePath = ""
};

var ex = Assert.Throws<AggregateException>(() =>
{
var result = FTP.DownloadFiles(source, destination, connection, new Options(), new Info(),
new CancellationToken());

});

Assert.AreEqual(1, ex.InnerExceptions.Count);
Assert.AreEqual(typeof(AuthenticationException), ex.InnerExceptions[0].GetType());

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,30 @@ private static FtpClient CreateFtpClient(Connection connect)
};

if (connect.EnableClientAuth)
client.ClientCertificates.Add(new X509Certificate2(connect.ClientCertificatePath));
{
if (!string.IsNullOrEmpty(connect.ClientCertificatePath))
client.ClientCertificates.Add(new X509Certificate2(connect.ClientCertificatePath));
else
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
try
{
store.Open(OpenFlags.ReadOnly);
if (!string.IsNullOrEmpty(connect.ClientCertificateName))
client.ClientCertificates.Add(store.Certificates.Find(X509FindType.FindBySubjectName, connect.ClientCertificateName, false)[0]);
else if (!string.IsNullOrEmpty(connect.ClientCertificateThumbprint))
client.ClientCertificates.Add(store.Certificates.Find(X509FindType.FindByThumbprint, connect.ClientCertificateThumbprint, false)[0]);
else
client.ClientCertificates.AddRange(store.Certificates);
}
finally
{
store.Close();
}
}
}
}

client.ValidateCertificate += (control, e) =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<IncludeSource>true</IncludeSource>
<PackageTags>Frends</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>1.0.4</Version>
<Version>1.1.0</Version>
<Description>Task for downloading files from FTP(S) servers.</Description>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,139 +1,155 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Frends.FTP.DownloadFiles.Enums;

namespace Frends.FTP.DownloadFiles.TaskConfiguration;

/// <summary>Parameters class usually contains parameters that are required.</summary>
public class Connection
{
/// <summary>
/// FTP(S) host address
/// </summary>
/// <example>my.ftp.server.com</example>
[DisplayFormat(DataFormatString = "Text")]
public string Address { get; set; }

/// <summary>
/// Port number to use in the connection to the server.
/// </summary>
/// <example>21</example>
[DefaultValue(21)]
public int Port { get; set; } = 21;

/// <summary>
/// Username to use for authentication to the server. Note that the file endpoint only supports
/// username for remote shares and the username must be in the format DOMAIN\Username.
/// </summary>
/// <example>myUsername</example>
[DisplayFormat(DataFormatString = "Text")]
public string UserName { get; set; }

/// <summary>
/// Password to use in the authentication to the server.
/// </summary>
/// <example>myPassword</example>
[PasswordPropertyText]
public string Password { get; set; }

/// <summary>
/// Type of transfer: 'Ascii' sends files as text and must only be used for sending ASCII text files. 'Binary' (default) sends files as raw data and should be used for sending e.g. UTF-encoded text files
/// </summary>
/// <example>FtpTransportType.Binary</example>
[DefaultValue(FtpTransportType.Binary)]
public FtpTransportType TransportType { get; set; }

/// <summary>
/// Connection mode to use to connect to the FTP server
/// </summary>
/// <example>FtpMode.Passive</example>
[DefaultValue(FtpMode.Passive)]
public FtpMode Mode { get; set; }

/// <summary>
/// Sends NOOP command to keep connection alive at specified time-interval in seconds. If set to 0 the connection is not kept alive. Default value is 0
/// </summary>
/// <example>60</example>
[DefaultValue(0)]
public int KeepConnectionAliveInterval { get; set; }

/// <summary>
/// The length of time, in seconds, until the connection times out. You can use value 0 to indicate that the connection does not time out. Default value is 60 seconds
/// </summary>
/// <example>60</example>
[DefaultValue(60)]
public int ConnectionTimeout { get; set; } = 60;

/// <summary>
/// If set, this encoding will be used to encode and decode command parameters and server responses, such as file names. Example values: utf-8, utf-16, windows-1252
/// </summary>
/// <example>utf-8</example>
public string Encoding { get; set; }

/// <summary>
/// Integer value of used buffer size as bytes.
/// Default value is 4 KB.
/// </summary>
/// <example>4096</example>
[DefaultValue(4096)]
public int BufferSize { get; set; }

#region FTPS settings

/// <summary>
/// Whether to use FTPS or not.
/// </summary>
/// <example>false</example>
[DefaultValue("false")]
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Frends.FTP.DownloadFiles.Enums;
namespace Frends.FTP.DownloadFiles.TaskConfiguration;
/// <summary>Parameters class usually contains parameters that are required.</summary>
public class Connection
{
/// <summary>
/// FTP(S) host address
/// </summary>
/// <example>my.ftp.server.com</example>
[DisplayFormat(DataFormatString = "Text")]
public string Address { get; set; }
/// <summary>
/// Port number to use in the connection to the server.
/// </summary>
/// <example>21</example>
[DefaultValue(21)]
public int Port { get; set; } = 21;
/// <summary>
/// Username to use for authentication to the server. Note that the file endpoint only supports
/// username for remote shares and the username must be in the format DOMAIN\Username.
/// </summary>
/// <example>myUsername</example>
[DisplayFormat(DataFormatString = "Text")]
public string UserName { get; set; }
/// <summary>
/// Password to use in the authentication to the server.
/// </summary>
/// <example>myPassword</example>
[PasswordPropertyText]
public string Password { get; set; }
/// <summary>
/// Type of transfer: 'Ascii' sends files as text and must only be used for sending ASCII text files. 'Binary' (default) sends files as raw data and should be used for sending e.g. UTF-encoded text files
/// </summary>
/// <example>FtpTransportType.Binary</example>
[DefaultValue(FtpTransportType.Binary)]
public FtpTransportType TransportType { get; set; }
/// <summary>
/// Connection mode to use to connect to the FTP server
/// </summary>
/// <example>FtpMode.Passive</example>
[DefaultValue(FtpMode.Passive)]
public FtpMode Mode { get; set; }
/// <summary>
/// Sends NOOP command to keep connection alive at specified time-interval in seconds. If set to 0 the connection is not kept alive. Default value is 0
/// </summary>
/// <example>60</example>
[DefaultValue(0)]
public int KeepConnectionAliveInterval { get; set; }
/// <summary>
/// The length of time, in seconds, until the connection times out. You can use value 0 to indicate that the connection does not time out. Default value is 60 seconds
/// </summary>
/// <example>60</example>
[DefaultValue(60)]
public int ConnectionTimeout { get; set; } = 60;
/// <summary>
/// If set, this encoding will be used to encode and decode command parameters and server responses, such as file names. Example values: utf-8, utf-16, windows-1252
/// </summary>
/// <example>utf-8</example>
public string Encoding { get; set; }
/// <summary>
/// Integer value of used buffer size as bytes.
/// Default value is 4 KB.
/// </summary>
/// <example>4096</example>
[DefaultValue(4096)]
public int BufferSize { get; set; }
#region FTPS settings
/// <summary>
/// Whether to use FTPS or not.
/// </summary>
/// <example>false</example>
[DefaultValue("false")]
public bool UseFTPS { get; set; } = false;

/// <summary>
/// Whether the data channel is secured or not.
/// </summary>
/// <example>false</example>
[DefaultValue("true")]
[UIHint(nameof(UseFTPS), "", true)]
public bool SecureDataChannel { get; set; }

/// <summary>
/// Specifies whether to use Explicit or Implicit SSL
/// </summary>
/// <example>FtpsSslMode.None</example>
[DefaultValue(FtpsSslMode.None)]
[UIHint(nameof(UseFTPS), "", true)]
public FtpsSslMode SslMode { get; set; }

/// <summary>
/// If enabled the client certificate is searched from user's certificate store
/// </summary>
/// <example>false</example>
[DefaultValue("false")]
[UIHint(nameof(UseFTPS), "", true)]
/// <summary>
/// Whether the data channel is secured or not.
/// </summary>
/// <example>false</example>
[DefaultValue("true")]
[UIHint(nameof(UseFTPS), "", true)]
public bool SecureDataChannel { get; set; }
/// <summary>
/// Specifies whether to use Explicit or Implicit SSL
/// </summary>
/// <example>FtpsSslMode.None</example>
[DefaultValue(FtpsSslMode.None)]
[UIHint(nameof(UseFTPS), "", true)]
public FtpsSslMode SslMode { get; set; }
/// <summary>
/// If enabled the client certificate is searched from user's certificate store
/// </summary>
/// <example>false</example>
[DefaultValue("false")]
[UIHint(nameof(UseFTPS), "", true)]
public bool EnableClientAuth { get; set; }

/// <summary>
/// If enabled the any certificate will be considered valid.
/// </summary>
/// <example>false</example>
[DefaultValue("false")]
[UIHint(nameof(UseFTPS), "", true)]
/// <summary>
/// Optional. Enables certification search by name from the certification store of current user.
/// </summary>
/// <example>mycert.crt</example>
[DefaultValue("")]
[UIHint(nameof(EnableClientAuth), "", true)]
public string ClientCertificateName { get; set; }

/// <summary>
/// Optional. Enables certification search by thumbprint from the certification store of current user.
/// </summary>
/// <example>a909502dd82ae41433e6f83886b00d4277a32a7b</example>
[DefaultValue("")]
[UIHint(nameof(EnableClientAuth), "", true)]
public string ClientCertificateThumbprint { get; set; }

/// <summary>
/// If enabled the any certificate will be considered valid.
/// </summary>
/// <example>false</example>
[DefaultValue("false")]
[UIHint(nameof(UseFTPS), "", true)]
public bool ValidateAnyCertificate { get; set; }

/// <summary>
/// Path to client certificate (X509).
/// </summary>
/// <example>c:\example.cer</example>
[UIHint(nameof(UseFTPS), "", true)]
[DisplayFormat(DataFormatString = "Text")]
/// <summary>
/// Path to client certificate (X509).
/// </summary>
/// <example>c:\example.cer</example>
[UIHint(nameof(UseFTPS), "", true)]
[DisplayFormat(DataFormatString = "Text")]
public string ClientCertificatePath { get; set; }

/// <summary>
/// Certificate SHA1 hash string to validate against.
/// </summary>
/// <example>BA7816BF8F01CFEA414140DE5DAE2223B00361A3</example>
[DefaultValue("")]
[UIHint(nameof(UseFTPS), "", true)]
public string CertificateHashStringSHA1 { get; set; }

#endregion
/// <summary>
/// Certificate SHA1 hash string to validate against.
/// </summary>
/// <example>BA7816BF8F01CFEA414140DE5DAE2223B00361A3</example>
[DefaultValue("")]
[UIHint(nameof(UseFTPS), "", true)]
public string CertificateHashStringSHA1 { get; set; }
#endregion
}
Loading