Skip to content

Implements back the port based SSL certificate fetching + real SNI support. #7

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

Closed
wants to merge 5 commits into from

Conversation

GitHubProUser67
Copy link
Contributor

Implements back the port based SSL certificate fetching.

This modernize the Mono code base to allow booth, a in-memory cache and that same file-based cache if the in-memory cache doesn't contain the entry.

This makes the previous SetCertificate constructor obsolete, but for good, since we can now set a different certificate per ports.

This is not SNI level, but it's still leagues beyond the actual .NET implementation (the MYSTICAL null stubbed return value with the TODO ^^).

Implements back the port based SSL certificate fetching.

This modernize the Mono code base to allow booth, a in-memory cache and that same file-based cache if the in-memory cache doesn't contain the entry.

This makes the previous SetCertificate constructor obsolete, but for good, since we can now set a different certificate per ports.
@PJB3005
Copy link
Member

PJB3005 commented Aug 11, 2024

Do you have an actual use case for this? The reason the mono code even has that port-based logic is dated and ancient, I don't see any reason to keep it as functionality here.

@GitHubProUser67
Copy link
Contributor Author

I do have an use case if I host various CN names on various ports without having to rely on SAN attributes.

@PJB3005
Copy link
Member

PJB3005 commented Aug 11, 2024

I do have an use case if I host various CN names on various ports without having to rely on SAN attributes.

Do you have an actual use case for this? Sure obviously this would allow you to have separate TLS certs served per port but what use case could that possibly have in 2024 to warrant implementing?

I would rather have a more flexible API that also incorporated SNI support or similar via e.g. a callback. Then you can just add the destination socket port in one of the parameters the selection logic and users that really need this can take it from there.

@GitHubProUser67
Copy link
Contributor Author

So, the full story is I did this mostly for "completeness" more than anything, I do plan under the hood onto making a SNI api based on the work I did on my server suit (a certificate signing chain).

Long story short, my plan is making a check for if the certificate that is set is a Root CA or a chain signed cert, if Root CA enable the SNI signing system.

I do have locally a system like this in place, but for some reasons it not work as intended just yet (the reason it's not there yet).

@GitHubProUser67 GitHubProUser67 changed the title Implements back the port based SSL certificate fetching. Implements back the port based SSL certificate fetching + real SNI support. Aug 12, 2024
@GitHubProUser67
Copy link
Contributor Author

The SNI support has finally be added in the repository @PJB3005 !

The way it works is simply by inserting the Root CA Certificate instead of the classic chain signed certificate (via the same constructor), and HTTPListener will sign itself intermediate certificates that are instanced per-session.

Simple and powerfull ^^.

Implements SNI Support.

Uses the same constructor as the previous solution, instead you will need to pass-in the CA Certificate.
HttpConnection - fixes non-https path.
…idation for older NET SDKs.

HttpListener.Certificates - Implement certificate Authority input validation for older NET SDKs.
@PJB3005
Copy link
Member

PJB3005 commented Aug 13, 2024

and HTTPListener will sign itself intermediate certificates that are instanced per-session.

Uh, what? Does that actually work? You can't self-sign certificates. And you can't get a root CA cert from anywhere? And isn't signing way too expensive to do for every TLS connection?

@GitHubProUser67
Copy link
Contributor Author

GitHubProUser67 commented Aug 13, 2024

I can confirm it works just fine, the root CA will need to be obtained from an outside source (I can make a generator for it too if needed), then this CA cert will be used to sign all the certificates that are generated on the fly.

They are all valid for a period of 7 days to avoids making too much of them (this can be extended) and are cached locally during that period.

EDIT: if a classic cert, it will naturally fallback to the classic certificate processing like how it worked previously.

Some web-crawlers tries to send a request with an empty url, we forbid these in order to not throw a debug assertion.
{
public class CertificateHelper
{
private static readonly string[] tlds = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extremely outdated list of TLDs. You cannot hardcode something like this.


// set up a certificate creation request.
CertificateRequest certRequestAny = new CertificateRequest($"CN={certSubject} [{GetRandomInt64(100, 999)}], OU=SpaceStation14 Department," +
$" O=\"SpaceWizards Corp\", L=New York, S=Northeastern United, C=US", rsa, certHashAlgorithm, RSASignaturePadding.Pkcs1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This organization does not exist and it should not be hardcoded in the library.

SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();

sanBuilder.AddDnsName(certSubject); // Some legacy clients will not recognize the cert serial-number.
sanBuilder.AddEmailAddress("[email protected]");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This email address does not exist and it should not be hardcoded in the library.

Comment on lines +85 to +86
byte[] certSerialNumber = new byte[16];
new Random().NextBytes(certSerialNumber);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rando isn't cryptographically secure.

Comment on lines +139 to +147
private static long GetRandomInt64(long minValue, long maxValue)
{
#if NET6_0_OR_GREATER
return new Random().NextInt64(minValue, maxValue);
#else
Random random = new Random();
return (long)(((random.Next() << 32) | random.Next()) * (double)(maxValue - minValue) / 0xFFFFFFFFFFFFFFFF) + minValue;
#endif
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This random isn't cryptographically secure.

Comment on lines +126 to +130
public static bool IsCertificateAuthority(X509Certificate certificate)
{
// Compare the Issuer and Subject properties of the certificate
return certificate.Issuer == certificate.Subject;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a correct way to check this, nor should this library care.

/// </summary>
sealed class RsaPkcs1SignatureGenerator : X509SignatureGenerator
{
// Workaround for SHA1 and MD5 ban in .NET 4.7.2 and .NET Core.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not implementing workarounds to security issues like this.

Comment on lines +216 to +224
public static byte[] HexToByteArray(string hex)
{
//copypasted from:
//https://social.msdn.microsoft.com/Forums/en-US/851492fa-9ddb-42d7-8d9a-13d5e12fdc70/convert-from-a-hex-string-to-a-byte-array-in-c?forum=aspgettingstarted
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.NET has built-in libraries for this.

@PJB3005
Copy link
Member

PJB3005 commented Mar 5, 2025

I am closing this PR. Based on the code I have skimmed over so far it is clear to me that:

  1. This code has absolutely no place, whatsoever, in being in a library like this.
  2. I hate to say this, but you likely do not know what you are doing when it comes to TLS certificates.

If you want to have a way to specify custom certificates dynamically like this, a PR that adds a simple callback so that library consumers can pass their own cert is all that should be necessary. You can implement whatever... this is.. on top of that.

@PJB3005 PJB3005 closed this Mar 5, 2025
@GitHubProUser67
Copy link
Contributor Author

GitHubProUser67 commented Mar 6, 2025

Okay, most of these are valid points I admit.

However, if I am being completely honest, I do not understand, nor like, the 2. sentence there, I would like some examples and proof as to where I didn't understand how TLS worked.

Some code might not be the best way to put it, but, as far as I know, the certs "works" so does TLS, it is simply skimming over some security, which is a valid point for it not being merged.

@PJB3005
Copy link
Member

PJB3005 commented Mar 6, 2025

However, if I am being completely honest, I do not understand, nor like, the 2. sentence there, I would like some examples and proof as to where I didn't understand how TLS worked.

You are self-signing certificates to implement SNI, directly for any domain clients ask for. This is not something you can do in the real world unless you have your own CA certs preinstalled on client machines. Even if you did have that, it would be grossly irresponsible from a security perspective to have your web server self signing certificates right then and there for anything other than dev environment testing.

@metalgearsloth
Copy link

as far as I know, the certs "works" so does TLS, it is simply skimming over some security,

Isn't security the whole point of the exercise?

@GitHubProUser67
Copy link
Contributor Author

I better understand the point now, the security argument is valid, I did use this system so old systems where SSL is a requirement could work.

On my fork I re-did that a bit, to also support chain signed certificate from an other CA (by loading the file directly if it exists) with a fallback to that system if not found.

But after all, this is quite use-case specific, so maybe it is best suited for the fork only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants