title: WebSocket secure connection self signed certificate
body: <p>The goal is a web application that exchanges information with an C# application that is installed on the user's pc.
  The client application is the websocket server and the browser is the websocket client.</p>
  
  <p>In the end the websocket client in the user's browser is created persistently via Angular and the application is running on the pc and doing some things.</p>
  
  <p>The C# library used is <a href="https://github.com/sta/websocket-sharp" rel="noreferrer">WebSocket-Sharp</a>. The websocket client is normal javascript.</p>
  
  <p>Obviously this connection happens only local so the client connects to localhost.
  As the website is secured via HTTPS the websocket has to be secured too. For this purpose the C# application creates a certificate when it starts up (it's just for testing purposes actually).</p>
  
  <p>The connection doesn't works because the certificate is untrusted. All server checks for the client are disabled but the connection won't establish.</p>
  
  <p>This is the part where the server is created</p>
  
  <pre><code>_server = new WebSocketServer($"wss://localhost:4649")
  {
      SslConfiguration =
      {
          ServerCertificate = Utils.Certificate.CreateSelfSignedCert(),
          ClientCertificateRequired = false,
          CheckCertificateRevocation = false,
          ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =&gt; true
      }
  };
  _server.AddWebSocketService&lt;CommandsBehaviour&gt;("/commands");
  _server.AddWebSocketService&lt;NotificationsBehaviour&gt;("/notifications");
  
  _server.Start();
  </code></pre>
  
  <p>This is how the certificate is created with BouncyCastle</p>
  
  <pre><code>private static AsymmetricKeyParameter CreatePrivateKey(string subjectName = "CN=root")
  {
      const int keyStrength = 2048;
  
      // Generating Random Numbers
      var randomGenerator = new CryptoApiRandomGenerator();
      var random = new SecureRandom(randomGenerator);
  
      // The Certificate Generator
      var certificateGenerator = new X509V3CertificateGenerator();
  
      // Serial Number
      var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
      certificateGenerator.SetSerialNumber(serialNumber);
  
      // Issuer and Subject Name
      var subjectDn = new X509Name(subjectName);
      var issuerDn = subjectDn;
      certificateGenerator.SetIssuerDN(issuerDn);
      certificateGenerator.SetSubjectDN(subjectDn);
  
      // Valid For
      var notBefore = DateTime.UtcNow.Date;
      var notAfter = notBefore.AddYears(70);
  
      certificateGenerator.SetNotBefore(notBefore);
      certificateGenerator.SetNotAfter(notAfter);
  
      // Subject Public Key
      var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
      var keyPairGenerator = new RsaKeyPairGenerator();
      keyPairGenerator.Init(keyGenerationParameters);
      var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
  
      return subjectKeyPair.Private;
  }
  
  public static X509Certificate2 CreateSelfSignedCert(string subjectName = "CN=localhost", string issuerName = "CN=root")
  {
      const int keyStrength = 2048;
      var issuerPrivKey = CreatePrivateKey();
  
      // Generating Random Numbers
      var randomGenerator = new CryptoApiRandomGenerator();
      var random = new SecureRandom(randomGenerator);
      ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerPrivKey, random);
      // The Certificate Generator
      var certificateGenerator = new X509V3CertificateGenerator();
      certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames(new GeneralName[] { new GeneralName(GeneralName.DnsName, "localhost"), new GeneralName(GeneralName.DnsName, "127.0.0.1") }));
      certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage((new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") })));
  
      // Serial Number
      var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
      certificateGenerator.SetSerialNumber(serialNumber);
  
      // Signature Algorithm
      //const string signatureAlgorithm = "SHA512WITHRSA";
      //certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);
  
      // Issuer and Subject Name
      var subjectDn = new X509Name(subjectName);
      var issuerDn = new X509Name(issuerName);
      certificateGenerator.SetIssuerDN(issuerDn);
      certificateGenerator.SetSubjectDN(subjectDn);
  
      // Valid For
      var notBefore = DateTime.UtcNow.Date;
      var notAfter = notBefore.AddYears(70);
  
      certificateGenerator.SetNotBefore(notBefore);
      certificateGenerator.SetNotAfter(notAfter);
  
      // Subject Public Key
      var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
      var keyPairGenerator = new RsaKeyPairGenerator();
      keyPairGenerator.Init(keyGenerationParameters);
      var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
  
      certificateGenerator.SetPublicKey(subjectKeyPair.Public);
  
      // self sign certificate
      var certificate = certificateGenerator.Generate(signatureFactory);
  
      // corresponding private key
      var info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
  
  
      // merge into X509Certificate2
      var x509 = new X509Certificate2(certificate.GetEncoded());
  
      var seq = (Asn1Sequence)Asn1Object.FromByteArray(info.ParsePrivateKey().GetDerEncoded());
      if (seq.Count != 9)
      {
          throw new PemException("malformed sequence in RSA private key");
      }
  
      var rsa = RsaPrivateKeyStructure.GetInstance(seq); //new RsaPrivateKeyStructure(seq);
      var rsaparams = new RsaPrivateCrtKeyParameters(
          rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient);
  
      x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
      return x509;
  
  }
  </code></pre>
  
  <p>This behaviour is logical although it is strange as the cert check shouldn't be performed locally.
  Is there a possibility to bypass this problem? I already thought about installing the issuer certificate to the trusted certs but this is not an optimal solution.</p>