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

Error in Secure Websocket #214

Closed
ghost opened this issue Feb 6, 2018 · 17 comments
Closed

Error in Secure Websocket #214

ghost opened this issue Feb 6, 2018 · 17 comments

Comments

@ghost
Copy link

ghost commented Feb 6, 2018

Hello,

i need your help, i'm using your console app sample to test secure WebSockets (wss)
i did changes to your code as below:

console cs:
var server = new WebSocketServer("wss://127.0.0.1:2080");
server.Certificate = new X509Certificate2(@"[path]\beaweb.cert.pfx", "password");

client.html:
window.ws = new wsImpl('wss://127.0.0.1:2080/');

i have used openssl to generate my certificate:

  1. openssl req -x509 -days 1460 -newkey rsa:2048 -keyout beaweb.key.pem -out beaweb.cert.pem
    pwd: [password]

  2. openssl pkcs12 -export -in beaweb.cert.pem -inkey beaweb.key.pem -out beaweb.cert.pfx
    pwd: [password]

  3. openssl pkcs12 -in beaweb.cert.pfx -clcerts -nokeys -out beaweb.cert.public.pem
    pwd: [password]

when i tried to run your sample after this i got the below exception:

2/6/2018 3:55:40 PM [Info] Server started at wss://127.0.0.1:2080 (actual port 2080)
2/6/2018 3:55:40 PM [Debug] Using default TLS 1.0 security protocol.
2/6/2018 3:55:55 PM [Debug] Client connected from 127.0.0.1:10953
2/6/2018 3:55:55 PM [Debug] Authenticating Secure Connection
2/6/2018 3:55:55 PM [Warn] Failed to Authenticate System.AggregateException: One or more errors occurred. ---> System.IO.IOException: Authentication failed because the remote party has closed the transport stream.
at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsServer(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization)
--- End of inner exception stack trace ---
---> (Inner Exception #0) System.IO.IOException: Authentication failed because the remote party has closed the transport stream.
at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsServer(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization)<---

i need to know if i missed something, i need your help because i built mvc web application communicating to client hardware like camera, fingerprint and passport reader using your library,
it's working fine when i running my site as http, but it faild when i switch to https!

thank you

@AdrianBathurst
Copy link

If you are testing in Chrome, you may need to use TLS 1.2. Seems recently Chrome has tightened up security protocols that can be used. I had to change this to get it to work.

To use TLS 1.2 on the socket server, set:
server.EnabledSslProtocols = SslProtocols.Tls12;
or more options, use:
server.EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Ssl3 | SslProtocols.Tls11 | SslProtocols.Tls;

On the socket server, try listening on 0.0.0.0
var server = new WebSocketServer("wss://0.0.0.0:2080");

On the client, if using wss, you need to use the url of the domain the ssl certificate is registered for, not an ip address:
window.ws = new wsImpl('wss://yourdomain.com:2080/');

@ghost
Copy link
Author

ghost commented Feb 8, 2018

i did everything as you said, only worked on IE, not working on (Edge, Chrome, Firefox)

what i did:
generate certificate using openssl
imported into trusted root certification
modify the host file --> 127.0.0.1 [certificate url (CN name)]

code:

var server = new WebSocketServer("wss://127.0.0.1:2080"); // also i tried 0.0.0.0
server.Certificate = new X509Certificate2(@"[path]\beaweb.cert.pfx", "password");
server.EnabledSslProtocols = SslProtocols.Tls12;

result:
IE ... working good
Edge, Chrome, Firefox ... not working i got
from console ->
"Authenticating Secure Connection"
"0 bytes read. Closing"

from client.html ->
received error code 1006 from onerror callback

1006 | Abnormal Closure | Reserved. Used to indicate that a connection was closed abnormally (that is, with no close frame being sent) when a status code is expected.

still trying to figure out this, hope you help me to solve this also.

thank you

@AdrianBathurst
Copy link

There must be something wrong with your certificate.

My test below works fine.

Server, I run this...

[STAThread]
static void Main(string[] args)
{
    Console.WriteLine("Socket Test");
    Console.WriteLine("====================");

    X509Certificate2 certificate = new X509Certificate2("C:\\test.pfx", "password");
    WebSocketServer server = new WebSocketServer("wss://0.0.0.0:2000");

    if (certificate != null)
    {
        server.Certificate = certificate;
        //server.EnabledSslProtocols = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
        server.EnabledSslProtocols = SslProtocols.Tls12;
    }

    server.Start(socket =>
    {
        socket.OnOpen = () =>
        {
        };

        socket.OnClose = () =>
        {
        };

        socket.OnMessage = message =>
        {
        };

        socket.OnError = exception =>
        {
        };
    });

    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}

client...

var socket = new WebSocket('wss://test.domain.com:2000');

I added test.domain.com to host file with local pc ip address obviously because the domain is not registered.

I exported the certificate file from the certificate store, including all "extended properties".
as a pfx file.

Just keep experimenting with the certificate file,

@ghost
Copy link
Author

ghost commented Feb 8, 2018

I spent my whole day to figure out what is the problem, I know the problem is about my generated certificate.

would you please help me? What tool you used to create the certificate? and the generated certificate should be in trusted root?

Appreciate your help.

Thank you so much

@AdrianBathurst
Copy link

AdrianBathurst commented Feb 9, 2018

Everyone seems to have a different way of generating these certificates & 99% never seem to work. So here goes with how I get it to work...

This will create an ssl certificate using test.com to test secure WebSockets in your local environment.

Install Win32 OpenSSL v1.1.0g from https://slproweb.com/products/Win32OpenSSL.html

Save below as openssl.cnf to C:\OpenSSL-Win32\

[ req ]
default_bits        = 2048
default_keyfile     = server-key.pem
distinguished_name  = subject
req_extensions      = req_ext
x509_extensions     = x509_ext
string_mask         = utf8only

# The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
#   Its sort of a mashup. For example, RFC 4514 does not provide emailAddress.
[ subject ]
countryName         = Country Name (2 letter code)
countryName_default     = US

stateOrProvinceName     = State or Province Name (full name)
stateOrProvinceName_default = NY

localityName            = Locality Name (eg, city)
localityName_default        = New York

organizationName         = Organization Name (eg, company)
organizationName_default    = Example, LLC

# Use a friendly name here because its presented to the user. The server's DNS
#   names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
#   by both IETF and CA/Browser Forums. If you place a DNS name here, then you 
#   must include the DNS name in the SAN too (otherwise, Chrome and others that
#   strictly follow the CA/Browser Baseline Requirements will fail).
commonName          = Common Name (e.g. server FQDN or YOUR name)
commonName_default      = Example Company

emailAddress            = Email Address
emailAddress_default        = test@test.com

# Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
[ x509_ext ]

subjectKeyIdentifier        = hash
authorityKeyIdentifier  = keyid,issuer

# You only need digitalSignature below. *If* you don't allow
#   RSA Key transport (i.e., you use ephemeral cipher suites), then
#   omit keyEncipherment because that's key transport.
basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage  = serverAuth, clientAuth

# Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
[ req_ext ]

subjectKeyIdentifier        = hash

basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage  = serverAuth, clientAuth

[ alternate_names ]

DNS.1       = test.com
DNS.2       = www.test.com
DNS.3       = mail.test.com
DNS.4       = ftp.test.com

# Add these if you need them. But usually you don't want them or
#   need them in production. You may need them for development.
# DNS.5       = localhost
# DNS.6       = localhost.localdomain
# DNS.7       = 127.0.0.1

# IPv6 localhost
# DNS.8     = ::1

open cmd

run:
cd "c:\OpenSSL-Win32\bin"

run:
set OPENSSL_CONF=c:\OpenSSL-Win32\openssl.cnf

run:
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout test.key -out test.crt -subj "/CN=test.com" -days 3650

Import "test.crt" into certificate store:

  1. Open certlm.msc
  2. Go to Trusted Root Certificate Autorities > Certificates
  3. Right-click somewhere, select All Tasks > Import
  4. Browse for the file test.crt just created (will be in C:\OpenSSL-Win32\bin)
  5. Select Place all certificates in the following store: Trusted Root Certification Authorities
  6. Click Finish

Export this certificate from the store:

  1. Refresh the Trusted Root Certificate Authorities > Certificates, locate & right-click the test.com certificate
  2. Select All Tasks > Export
  3. Export as test.p7b & save it in C:\OpenSSL-Win32\bin\
  4. Delete test.com certificate that's currently in cert store

run:
openssl pkcs7 -in test.p7b -inform DER -out result.pem -print_certs

run:
openssl pkcs12 -export -inkey test.key -in result.pem -name test.com -out final_result.pfx

Import "final_result.pfx" into the certificate store:

  1. Double-click the generated final_result.pfx file to run the installer
  2. Select local machine
  3. Enter the password
  4. Select mark this key exportable
  5. Select include all extended properties
  6. Select place all certificates in the following store: Trusted Root Certification Authorities
  7. Click Finish

Repeat above import steps 1 to 7 again, but this time in step 6, place the certificate in the "Web Hosting" certificate store (so that IIS can see the certificate when binding to https)

Export this certificate from the store (to use in the socket server app):

  1. Refresh the Trusted Root Certificate Authorities > Certificates, locate & right-click the test.com certificate
  2. Select All Tasks > Export
  3. Select yes, export private key
  4. Select Personal Information Exchange
  5. Select include all certificates if possible
  6. Select export all extended properties
  7. Select enable certificate privacy
  8. Add password
  9. Add filename test.com.pfx (save it to C:\ or the location where your "socket server app" will reference it, see below)
    X509Certificate2 certificate = new X509Certificate2("C:\\test.com.pfx", "password");

Add test.com to host file along with local pc ip

Create IIS website & add bindings to test.com for both http & https (and select the test.com certificate in the https bindings)

Visit https://test.com/your-socket-client.htm (in Chrome/Edge, click advanced > proceed when prompted for ssl warning)

Socket client should now connect successfully over wss using wss://test.com:xxxx

@ghost
Copy link
Author

ghost commented Feb 9, 2018

Thank you so much, I’ll walkthrough your steps Sunday morning since we in weekend here.

I’ll let you know

Appreciate your help

@ghost
Copy link
Author

ghost commented Feb 11, 2018

Thank you so much, it's working in all browsers on local machine.

but i have 1 question, what i should do when i publish my site on production? of course i can't use test certificate due to company privacy, i have to use the issued certificate by security team. is there any recommendations or steps to make my site running as my local machine?

appreciate your help.

thank you so much again

@AdrianBathurst
Copy link

AdrianBathurst commented Feb 12, 2018

Good to hear!

For production, this is easier. There are no openssl processes to do at all. Add the real certificate to the Certificate Store, then find it in the Certificate Store, right click it, select all Tasks > Export, then follow the same steps as in the last export process described above. It must be saved as a .pfx file.

You don't need to add any host file entries.

@ghost
Copy link
Author

ghost commented Feb 12, 2018

so it's security team task.

hope it work on production

thank you so much

@zlbcdn
Copy link

zlbcdn commented Apr 23, 2021

Everyone seems to have a different way of generating these certificates & 99% never seem to work. So here goes with how I get it to work...

install Win32 OpenSSL v1.1.0g from https://slproweb.com/products/Win32OpenSSL.html

save below as openssl.cnf to C:\OpenSSL-Win32\

[ req ]
default_bits        = 2048
default_keyfile     = server-key.pem
distinguished_name  = subject
req_extensions      = req_ext
x509_extensions     = x509_ext
string_mask         = utf8only

# The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
#   Its sort of a mashup. For example, RFC 4514 does not provide emailAddress.
[ subject ]
countryName         = Country Name (2 letter code)
countryName_default     = US

stateOrProvinceName     = State or Province Name (full name)
stateOrProvinceName_default = NY

localityName            = Locality Name (eg, city)
localityName_default        = New York

organizationName         = Organization Name (eg, company)
organizationName_default    = Example, LLC

# Use a friendly name here because its presented to the user. The server's DNS
#   names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
#   by both IETF and CA/Browser Forums. If you place a DNS name here, then you 
#   must include the DNS name in the SAN too (otherwise, Chrome and others that
#   strictly follow the CA/Browser Baseline Requirements will fail).
commonName          = Common Name (e.g. server FQDN or YOUR name)
commonName_default      = Example Company

emailAddress            = Email Address
emailAddress_default        = test@test.com

# Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
[ x509_ext ]

subjectKeyIdentifier        = hash
authorityKeyIdentifier  = keyid,issuer

# You only need digitalSignature below. *If* you don't allow
#   RSA Key transport (i.e., you use ephemeral cipher suites), then
#   omit keyEncipherment because that's key transport.
basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage  = serverAuth, clientAuth

# Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
[ req_ext ]

subjectKeyIdentifier        = hash

basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage  = serverAuth, clientAuth

[ alternate_names ]

DNS.1       = test.com
DNS.2       = www.test.com
DNS.3       = mail.test.com
DNS.4       = ftp.test.com

# Add these if you need them. But usually you don't want them or
#   need them in production. You may need them for development.
# DNS.5       = localhost
# DNS.6       = localhost.localdomain
# DNS.7       = 127.0.0.1

# IPv6 localhost
# DNS.8     = ::1

open cmd

run:
cd "c:\OpenSSL-Win32\bin"

run:
set OPENSSL_CONF=c:\OpenSSL-Win32\openssl.cnf

run:
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout test.key -out test.crt -subj "/CN=test.com" -days 3650

import "test.crt" into certificate store:
open certlm.msc
go to Trusted Root Certificate Autorities > Certificates
right-click somewhere, select All Tasks > Import
browse for the file "test.crt" just created (will be in C:\OpenSSL-Win32\bin)
select Place all certificates in the following store: Trusted Root Certification Authorities
click Finish

export this certificate from the store:
refresh the Trusted Root Certificate Autorities > Certificates, locate & right-click the test.com certificate
select All Tasks > Export
export as test.p7b & save it in C:\OpenSSL-Win32\bin
delete test.com certificate that's currently in cert store

run:
openssl pkcs7 -in test.p7b -inform DER -out result.pem -print_certs

run:
openssl pkcs12 -export -inkey test.key -in result.pem -name test.com -out final_result.pfx

import "final_result.pfx" into the certificate store:
double-click the generated "final_result.pfx" file to run the installer
select local machine
enter the password
select mark this key exportable
select include all extended properties
select place all certificates in the following store: Trusted Root Certification Authorities
click Finish

repeat above import steps again, but this time place the certificate in the "Web Hosting" certificate store (so that IIS can see the certificate when binding to https)

export this certificate from the store (to use in the socket server app):
refresh the Trusted Root Certificate Autorities > Certificates, locate & right-click the test.com certificate
select All Tasks > Export
select yes, export private key
select Personal Information Exchange
select include all certificates if possible
select export all extended properties
select enable certificate privacy
add password
add filename test.com.pfx (save it to C:\ or the location where your "socket server app" will reference it, see below)
X509Certificate2 certificate = new X509Certificate2("C:\\test.com.pfx", "password");

add test.com to host file along with local pc ip

create iis website & add bindings to test.com for both http & https

visit https://test.com/your-socket-client.htm (in Chrome, click advanced > proceed when prompted for ssl warning)

socket client should now connect successfully over wss using wss://test.com:xxxx

you are the best!!!

@lyq95
Copy link

lyq95 commented Sep 13, 2021

我按照你上的弄 为啥不可以呢 都弄了呀

@zlbcdn
Copy link

zlbcdn commented Sep 14, 2021

每一步有异常信息吗?我测试没问题

@m-siscogit
Copy link

I'm new to all of this and I'm struggling to get this procedure to work with a secure websocket server. The server will be running on the client's machine (i.e. wss://localhost:996/), and I'm not sure how this plays with the use of test.com in the certificate. Do I need to change the DNS.x entries in the cnf to somehow match the use of localhost? Or is test.com just a placeholder?

By the way, the error I'm seeing when I try to connect is on the server side. I'm getting a AuthenticationException with a message of "A call to SSPI failed" and an inner exception with the message "function requested is not supported". This means little to me. Which function is not supported? Any insight you could provide would be most helpful.

@AdrianBathurst
Copy link

Run your site through IIS with a "test" domain as per instructions above. The certificate name has to match the domain you're using to run the site. Can be any domain name of your choice, just that test.com is fitting. Follow the steps in comment #214 (comment) Do not skip/miss anything and you should be good.

Get Visual Studio to run through the "test.com" IIS site instead of IIS Express so you still have the usual debugging experience.

@cuican6
Copy link

cuican6 commented Apr 16, 2023

hello sir,w want run wss server in linux, my app is net6 framework, i create by openssl by "openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem", but when i run app,err:"Listener socket is closed System.AggregateException: One or more errors occurred. (The server mode SSL must use a certificate with the associated private key.)
";

@cuican6
Copy link

cuican6 commented Apr 16, 2023

@AdrianBathurst

@AdrianBathurst
Copy link

@cuican6 follow the instructions above (#214 (comment)) exactly. The end result certificate file has to be .pfx file type, not .pem

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

No branches or pull requests

6 participants