0 votes
by (150 points)

I'm starting with working code (that I did not write!) that is connecting to a FTP server using a .pfx certificate for 2 way encryption with SHA1 cipher. The FTP site is switching to 1 way encryption with SHA2 cipher. So I'm not sure where to start. The code was using the LoadPFX command so I got the new certificate in pfx format. But I'm getting the error "No certificate with a private key was found in PFX". Do I need to use a different format for the certificate? Any help would be appreciated!

Applies to: Rebex FTP/SSL

1 Answer

+1 vote
by (143k points)

"One-way SSL" (TLS/SSL without client certificate authentication)
only means that the server does not validate the identity of the client. The rest of the process is the same - encryption and validation of transmitted data is still two way.

This said, what is actually the purpose of the new certificate in PFX format? Is this the client's certificate (which means you would still use two-way SSL), or is it something else - such as the server's certificate (which should be without a private key)?

Of course, if the PFX file does actually include the private key, the Certificate.LoadPfx (or CertificateChain.LoadPfx) method should be able to load it. Which version of Rebex FTP/SSL do you currently use? We have enhanced this method a bit recently when adding MS CNG support, so it might be useful to try the latest release of Rebex FTP/SSL. If it still doesn't work, please let us know.

by (150 points)
edited by
Thanks for the response.  You are correct.  The certificate file should only be the server certificate.  They gave me the new certificate file in PFX format because that's what I said the code was looking for.  It DOES NOT contain a private key and as you say, that's why the LoadPFX command fails.

So, I have a different version of the new 1way certificate in a CRT format that works with the command line program CURL (got that program from the server company).

I see there's a LoadDER command that apparently should handle CRT format file (from another question you answered).  But it seems to be for a certificate and the LoadPFX is for a certificate chain.  I don't understand the difference or how to modify the code.

UPDATE: I managed to get enough code changed to get the LoadDER command to not show an error before compiling.  I created a new (blank) CertificateChain and then added the certificate object loaded with the LoadDER.  But when I ran it, I got an error "Invalid Base-64 encoding of a certificate."  So maybe the certificate file is still not a valid certificate?
by (143k points)
Your original code most likely used CertificateChain.LoadPfx to load the client certificate (and associated private key) in order to use it for client certificate authentication. If you no longer want to perform client certificate authentication, then you need to remove (or disable) the relevant code. Replacing the client certificate with the server certificate is not going to work. Instead, just set Ftp object's Settings.SslClientCertificateRequestHandler to null and see whether this works.

But what about the CRT file? If the server's certificate was issued by a certification authority that is trusted by the client, you don't actually need this at all. The server sends its certificate chain to the client during TLS/SSL negotiation anyway and Rebex FTP/SSL validates it using Windows CryptoAPI by default. Unless you would like to validate the server's certificate chain yourself (using a custom certificate validator), the only thing you might have to do is to add the certificate of the chain's root CA to the trusted root certification authorities store of the user account under which the application runs.

The "Invalid Base-64 encoding of a certificate" error indicates that the certificate appeared to be Base-64 encoded, but the library was unable to decode the Base64-based data for some reason. Would it be possible to send us the CRT file for analysis? (Unlike private keys, certificates are supposed to be publicly available.)
by (150 points)
Sorry for the delay in responding.  I believe that the provided server certificate is "self-signed" so it's likely not from an official certification authority. I think they just created it themselves.  I'd be happy to send the certificate, but I don't see a way to attach a file.

Yes, the original code uses CertificateChain.LoadPFX.  Sorry I don't understand what you mean by setting the Settings.SslClientCertificateRequestHandler to null.  So, assuming the server certificate is just made up by the head office (as opposed to being from a certification auth), then what sort of certification can be done?  Is there a way to load the certificate and then have some part of Rebex compare that to the certificate sent from the server during TLS/SSL negotiation?  Thanks for all the help.
by (143k points)
Sorry for the confusion - I assumed that since you have been using client certificate authentication, you were setting Settings.SslClientCertificateRequestHandler to an instance of certificate request handler based on the CertificateChain you loaded using LoadPfx, like this: https://www.rebex.net/ftp-ssl.net/features/authentication.aspx#client-certificate
This is not needed at all when not using client certificate authentication.
by (143k points)
edited by
Please mail the certificate to support@rebex.net - we'll look into what's wrong.

Update: We have enhanced our certificate loader to handle this. See https://rebex.net/total-pack/history.aspx#2017R6.1 for release details.
by (143k points)
To validate the self-signed certificate, you have two basic options:

a) Add the certificate to the trusted root CA store (of the user account running the application). This would make the default (Windows CryptoAPI based) certificate validator accept it.

b) Use Ftp object's ValidatingCertificate event to implement a custom certificate validator. See https://www.rebex.net/ftp-ssl.net/features/tls-ssl.aspx#custom-certificate-validation for sample code. However, instead of using Certificate.Thumbprint, it might be better to use the full certificate data obtained using Certificate.GetRawCertData() method.