Repository navigation
FTPS Connection using BouncyCastle
FluentFTP normally uses .NET SslStream/SChannel for secure encrypted connections. This combination unfortunately does not support TLS session resumption.
Therefore we integrated the powerful BouncyCastle networking library, a .NET library ported from Java, which contains a more flexible implementation of TLS, as an alternative secure and encrypted transport medium. It does NOT currently support TLS 1.3.
We developed a FluentFTP adapter wrapper for BouncyCastle called FluentFTP.BouncyCastle, which works on Windows, Unix and Mac OSX.

It is published as an independent Nuget FluentFTP.BouncyCastle, and integrates with the FluentFTP client object.

- Allows for TLS session resumption
- Allows an FTPS data connection to explicitly resume the TLS 1.2 session established by the control connection
- Useful for 3D printers and other hardware FTP servers that reject data connections unless the TLS session is reused
- Useful to fix this error
522 SSL connection failed: session reuse required.
- Only supports TLS 1.2
- Does not support client certificates.
- If
client.Config.ClientCertificatesis filled, a warning is logged and the handshake proceeds without one. - FTP connection handshake is synchronous, even when used through
AsyncFtpClient(we do not pass a cancellation token to the handshake) - Cancellation tokens are not supported (Bouncy Castle's stream does not observe cancellation tokens on reads and writes)
All these settings are properties of the FtpClient object.
-
Config.CustomStream - Set this option to
typeof(BouncyCastleFtpStream)to activate the BouncyCastle package in your FTP client. -
Config.CustomStreamConfig - Set this to an instance of
BouncyCastleFtpConfigto configure the GnuTLS connection settings.
All these settings are properties of the BouncyCastleFtpConfig object.
-
RequireSessionResumption- Indicating whether a data connection must fail when the server does not resume the control connection's TLS session. -
AllowLegacyResumption- Indicating whether TLS 1.2 sessions without RFC 7627 Extended Master Secret may be resumed. Keep disabled unless a known legacy server requires it. -
Diagnostic- Optional callback that receives the non-sensitive connection diagnostics the stream also writes to FluentFTP's log at the Verbose level.
using FluentFTP;
using FluentFTP.BouncyCastle;
var client = new AsyncFtpClient(host, username, password, 990);
client.Config.EncryptionMode = FtpEncryptionMode.Implicit;
client.Config.CustomStream = typeof(BouncyCastleFtpStream);
client.Config.CustomStreamConfig = new BouncyCastleFtpConfig {
RequireSessionResumption = true,
};Warning: This section was generated by Claude and may be difficult to read.
The adapter checks certificate chain trust, server-authentication usage, and the requested server's identity before calling FluentFTP's validation callback. DNS Subject Alternative Names (SANs) take precedence over the Common Name (CN). A DNS wildcard matches one whole label only. CN fallback uses an exact match when no DNS SAN is present; an IP address always requires a matching IP SAN. Internationalized hostnames are converted to ASCII for matching and SNI. SNI is sent for DNS hosts and omitted for IP addresses.
The adapter preserves Bouncy Castle's default cipher suites, whose AES support
stops at AES-128, and adds the two forward-secret AES-256-GCM suites needed to
reach servers restricted to AES-256 — ECDHE-RSA-AES256-GCM-SHA384 (the only
suite vsftpd's default configuration accepts) and its ECDSA-certificate
counterpart ECDHE-ECDSA-AES256-GCM-SHA384. It adds no AES-256 CBC or
static-RSA suites. The original defaults include AES-128 static-RSA suites,
which remain available for compatibility and lack forward secrecy.
This is a compatibility measure, not a hardening one. Whether the stock
SslStream path would reach the same servers depends on the platform TLS stack
and its configured cipher policy, which an administrator can change, so it is
not a fixed comparison.
client.Config.ValidateCertificateRevocation controls online revocation checks
(off by default, matching FluentFTP). When enabled, unavailable revocation
information is reported as a validation error, just like a revoked certificate.
Validation failures are passed through FluentFTP's custom-stream callback and
reject the connection by default. An explicit validation callback or
ValidateAnyCertificate can override them. For self-signed devices, prefer a
callback that checks a known certificate fingerprint; accepting any certificate
disables server identity protection. FluentFTP's existing custom-stream API
carries validation details in PolicyErrorMessage; its PolicyErrors enum
does not distinguish chain failures from hostname failures.
A rejected certificate sends a bad_certificate TLS alert to the server. The
certificate passed to the validation callback stays valid until the stream is
disposed, as with SslStream; copy it if you need it for longer.
Handshake and certificate failures are raised as
System.Security.Authentication.AuthenticationException, matching FluentFTP's
default SslStream behaviour, so FluentFTP's own error handling and existing
catch blocks keep working. The Bouncy Castle TlsException is attached as
the inner exception and carries the TLS alert description.
Connection diagnostics (session offers, resumption results and certificate
acceptance) are written to FluentFTP's log at the Verbose level with a
BouncyCastle: prefix. BouncyCastleFtpConfig.Diagnostic optionally receives
the same messages.
Some legacy servers require session resumption without RFC 7627 Extended Master Secret. Bouncy Castle blocks that by default. Enable compatibility only for a known server that requires it:
client.Config.CustomStreamConfig = new BouncyCastleFtpConfig {
RequireSessionResumption = true,
AllowLegacyResumption = true,
};Allowing legacy resumption reduces TLS protections and should not be enabled as a general fallback.
# Only needed if the .NET 7 runtime is absent.
$env:DOTNET_ROLL_FORWARD = 'Major'
dotnet test FluentFTP.Tests --filter FullyQualifiedName~Unit.BouncyCastleThe tests generate their own certificates, use custom chain trust without changing the operating system's trusted roots, and run loopback TLS, FTPS and CRL servers. No printer or external server is required.
The original adapter was developed against a Bambu Lab X1 Carbon printer.
We tested real printer listing, upload, and download tests with both sync and async clients on Windows/.NET 8.
ach client uploaded 32,769 bytes, downloaded them twice with an exact byte match, resumed five TLS data connections, and deleted its test file.
Both negotiated ECDHE-RSA-AES256-GCM-SHA384.
The tests used ValidateAnyCertificate = true and AllowLegacyResumption = true, matching the existing printer connection settings; they do not establish acceptance under the default certificate policy.
Warning: This section was generated by Claude and may be difficult to read. These notes cover the
FluentFTP.BouncyCastlestream only. They do not apply to, FluentFTP's defaultSslStream.
-
High. The TLS target host was not used during certificate validation, so
a trusted certificate for a different hostname satisfied the default policy.
ServerCertificateValidationnow checks DNS or IP identity independently of chain trust. - Medium. The chain was built without a server-authentication application policy, so trust alone did not enforce the certificate's intended usage. The server-authentication extended key usage is now required.
-
Medium. Revocation checking was always disabled, even when
ValidateCertificateRevocationwas enabled. The setting is now honoured, and missing revocation information fails closed when checking is enabled. - Low. An exception thrown while closing the TLS protocol during a failed initialization could replace the original failure. The original exception is now preserved.
-
Low. Handshake and certificate failures surfaced as Bouncy Castle
TlsFatalAlertexceptions and sent aninternal_erroralert to the server. They are now raised asAuthenticationException, matchingSslStream, and a rejected certificate sendsbad_certificate.
- Chain trust uses the operating system's trust store, with the certificates sent by the server as extra intermediates.
- DNS SANs take precedence over the CN; a wildcard matches one label; an IP address host requires a matching IP SAN; IDN hosts are matched in ASCII.
- Malformed SAN or subject data fails closed.
- Online revocation checks follow
client.Config.ValidateCertificateRevocation. - The validation result is passed to FluentFTP's callback, which can override it explicitly.
$env:DOTNET_ROLL_FORWARD = 'Major'
dotnet test FluentFTP.Tests --filter FullyQualifiedName~Unit.BouncyCastleThe tests generate disposable certificates, use custom chain trust without altering the operating system's roots, and run loopback TLS, FTPS and CRL servers. They exercise TLS streams and FluentFTP login; they are not end-to-end file transfer tests.
Cipher review date: 2026-09-13. The adapter preserves the original Bouncy Castle defaults, which stop at AES-128, and adds two forward-secret AES-256-GCM suites: ECDHE-RSA-AES256-GCM-SHA384 and ECDHE-ECDSA-AES256-GCM-SHA384. Both are a compatibility measure for servers restricted to AES-256, not a hardening change. Loopback tests negotiate each suite against a server offering only that suite, using an RSA or an EC server certificate respectively, and verify repeated data connection resumption plus an exact 32769-byte round trip on every handshake. Negative tests confirm the addition did not widen further: AES-256 CBC (ECDHE-RSA and ECDHE-ECDSA), DHE-RSA-AES256-GCM and the AES-256 static-RSA suites are all still refused. The original AES-128 static-RSA compatibility remains; this is not a policy that guarantees forward secrecy for every accepted connection.
- Auto Connection
- Auto Reconnection
- FTP(S) Connection
- FTP(S) Connection using GnuTLS
- FTP(S) Connection using BouncyCastle
- FTPS Proxies
- Custom Servers
- Custom Commands
- v40 Migration Guide