[C#] WebSocket安全連接自簽名證書


Answers

@Kdawg的答案是正確的。

您不希望客戶端瀏覽器只接受服務器端調整的不安全連接。 所有接受未簽名(或自簽名)證書的行為都在客戶端。

我想補充一下,在Kdawg的回答中,在Windows網絡上,私人組織最常見的做法是:

  1. 分配Windows服務器充當證書頒發機構

  2. 將證書頒發機構的公共根證書添加到Windows主機(通過GPO )或手動

  3. 用Windows CA服務器簽名自定義證書

這聽起來很痛苦,而且是。

如果我是你,我會去製定一個標準的公開簽名證書,並將SSL運行,直到完成。

看看我們為您的域名加密免費SSL證書。

Question

目標是一個Web應用程序,它與安裝在用戶電腦上的C#應用程序交換信息。 客戶端應用程序是websocket服務器,瀏覽器是websocket客戶端。

最後,用戶瀏覽器中的websocket客戶端通過Angular持久創建,應用程序在pc上運行,並執行一些操作。

所用的C#庫是WebSocket-Sharp 。 websocket客戶端是正常的JavaScript。

很明顯,這個連接只發生在本地,所以客戶端連接到本地主機。 由於網站是通過HTTPS安全的websocket必須得到保護。 為此,C#應用程序在啟動時會創建一個證書(這僅僅是為了實際的測試目的)。

連接不起作用,因為證書不受信任。 所有服務器檢查客戶端被禁用,但連接不會建立。

這是創建服務器的部分

_server = new WebSocketServer($"wss://localhost:4649")
{
    SslConfiguration =
    {
        ServerCertificate = Utils.Certificate.CreateSelfSignedCert(),
        ClientCertificateRequired = false,
        CheckCertificateRevocation = false,
        ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
    }
};
_server.AddWebSocketService<CommandsBehaviour>("/commands");
_server.AddWebSocketService<NotificationsBehaviour>("/notifications");

_server.Start();

這是如何使用BouncyCastle創建證書

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;

}

這種行為是合乎邏輯的,雖然這很奇怪,因為證書檢查不應該在本地執行。 有沒有可能繞過這個問題? 我已經考慮將發行者證書安裝到可信任的證書,但這不是一個最佳的解決方案。