c# - navegador - keytool certificado autofirmado
Certificado autofirmado de conexión segura WebSocket (2)
El objetivo es una aplicación web que intercambia información con una aplicación C # que está instalada en la PC del usuario. La aplicación cliente es el servidor websocket y el navegador es el cliente websocket.
Al final, el cliente de websocket en el navegador del usuario se crea persistentemente a través de Angular y la aplicación se ejecuta en la PC y hace algunas cosas.
La biblioteca C # utilizada es WebSocket-Sharp . El cliente de websocket es javascript normal.
Obviamente, esta conexión solo ocurre local, por lo que el cliente se conecta a localhost. Como el sitio web está protegido mediante HTTPS, también debe asegurarse el websocket. Para este propósito, la aplicación C # crea un certificado cuando se inicia (es solo para fines de prueba en realidad).
La conexión no funciona porque el certificado no es de confianza. Todas las comprobaciones del servidor para el cliente están deshabilitadas, pero la conexión no se establecerá.
Esta es la parte donde se crea el servidor
_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();
Así es como se crea el certificado con 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;
}
Este comportamiento es lógico, aunque es extraño, ya que la comprobación del certificado no debe realizarse localmente. ¿Existe la posibilidad de eludir este problema? Ya pensé en instalar el certificado de emisor en los certs confiables, pero esta no es una solución óptima.
¿Has probado alguna de las respuestas a esta pregunta ?
Para resumir, parece que hay algunas opciones que puedes probar:
Inicie Chrome con el argumento
--ignore-certificate-errors
especificado.Inicie un servidor HTTP en el mismo puerto que toma el mismo certificado autofirmado, navegue hasta él y acepte el certificado, después del cual podrá usar la conexión WebSocket.
Establezca la opción de configuración en Firefox
network.websocket.allowInsecureFromHTTPS
entrue
, luego usews://
lugar de la direcciónwss://
.
Si todo esto es para probar y tienes la posibilidad de controlar ese tipo de cosas, entonces creo que una o más de ellas deberían funcionar. Si necesita que su usuario final estándar pueda hacer esto, creo que necesitará una solución diferente. Como ha descubierto, no importa si configura el servidor para que no se preocupe por el certificado, el cliente tiene que decidir finalmente si desea aceptar el certificado o no aceptará la conexión.
Las respuestas de @Kdawg son correctas.
No tiene esperanzas de que los navegadores de los clientes acepten una conexión insegura solo con ajuste del lado del servidor. Todo el comportamiento para aceptar el certificado sin firmar (o autofirmado) está en el lado del cliente.
Me gustaría añadir, además de la respuesta de @Kdawg, que en las redes de Windows, la práctica más común para las organizaciones privadas es:
Asignar un servidor de Windows para actuar como autoridad de certificación
Agregue el certificado raíz público de la Autoridad de certificación a los hosts de Windows (ya sea por GPO ) o manualmente
Firme el certificado personalizado con el servidor de CA de Windows
Suena doloroso, y lo es.
Si yo fuera usted, me gustaría obtener un certificado estándar firmado públicamente y ejecutar SSL hasta que termine.
Mire Let''s Encrypt para obtener certificados SSL gratuitos para su dominio.