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

SSL error when porting from node:tls with a self sign certificate (SSLV3_ALERT_HANDSHAKE_FAILURE or CertPathValidatorException) #190

Open
vricosti opened this issue May 17, 2024 · 5 comments
Labels
bug Something isn't working

Comments

@vricosti
Copy link
Contributor

vricosti commented May 17, 2024

Hi,

I am trying to port a library androidtv-remote that was originally designed to run in a node environment to be able to run inside a react-native app.
For the ssl part it was using node:tls and it works fine with it.

The android device I am connecting to is using a self signed certificate and the app is using a self signed certificate too.

Here is the server ssl characteristics:

openssl s_client -state -connect 192.168.1.102:6467
CONNECTED(00000003)
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
Can't use SSL_get_servername
SSL_connect:SSLv3/TLS read server hello
depth=0 dnQualifier = full_fbx6lcv2/fbx6lcv2/Freebox Player Mini v2, CN = atvremote/863600S191201703
verify error:num=18:self-signed certificate
verify return:1
depth=0 dnQualifier = full_fbx6lcv2/fbx6lcv2/Freebox Player Mini v2, CN = atvremote/863600S191201703
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:SSLv3/TLS read server key exchange
SSL_connect:SSLv3/TLS read server certificate request
SSL_connect:SSLv3/TLS read server done
SSL_connect:SSLv3/TLS write client certificate
SSL_connect:SSLv3/TLS write client key exchange
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
SSL3 alert read:fatal:handshake failure
SSL_connect:error in error
40277A58637E0000:error:0A000410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1584:SSL alert number 40
---
Certificate chain
 0 s:dnQualifier = full_fbx6lcv2/fbx6lcv2/Freebox Player Mini v2, CN = atvremote/863600S191201703
   i:dnQualifier = full_fbx6lcv2/fbx6lcv2/Freebox Player Mini v2, CN = atvremote/863600S191201703
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Jan 25 11:00:00 2021 GMT; NotAfter: Jan 19 03:14:07 2038 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIICNzCCAiCgAwIBAgIGAXc6px/tMA0GCSqGSIb3DQEBCwUAMF0xNjA0BgNVBC4M
LWZ1bGxfZmJ4NmxjdjIvZmJ4NmxjdjIvRnJlZWJveCBQbGF5ZXIgTWluaSB2MjEj
MCEGA1UEAxMaYXR2cmVtb3RlLzg2MzYwMFMxOTEyMDE3MDMwHhcNMjEwMTI1MTEw
MDAwWhcNMzgwMTE5MDMxNDA3WjBdMTYwNAYDVQQuDC1mdWxsX2ZieDZsY3YyL2Zi
eDZsY3YyL0ZyZWVib3ggUGxheWVyIE1pbmkgdjIxIzAhBgNVBAMTGmF0dnJlbW90
ZS84NjM2MDBTMTkxMjAxNzAzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA1hmo54HHzsk8i0+/hNax+zVtZR4WDP6F/3JXT1Y3YtYVP7HjW5THXSXvn+4i
VPgOwwFrg5fFd2N6hnk5As56Ks2h+r/r3UzuHvMiYnkkpbW89b0VoAhY740wShky
xY0TJx/Ipj1YlU65oB6AxWWM78MAHIAtNJuB+/DFvKTpZoGj32Eq2lHyEhfnHCYU
rkT27jkTp9TyWj6LIYkZFfcykgPKu90LDNqFtxrBCvA9d354TyrbZdBYbW3Ca4Vh
0jpfuxDPzOyRnSGWi4IwFZdIuv0vLu1TMOyp3aFzIbR4L+EBvvNKoSwDWFPnVRns
UAP+50dCjk6/zSNu4Fou4EwikwIDAQABMA0GCSqGSIb3DQEBCwUAAwIAAA==
-----END CERTIFICATE-----
subject=dnQualifier = full_fbx6lcv2/fbx6lcv2/Freebox Player Mini v2, CN = atvremote/863600S191201703
issuer=dnQualifier = full_fbx6lcv2/fbx6lcv2/Freebox Player Mini v2, CN = atvremote/863600S191201703
---
No client certificate CA names sent
Client Certificate Types: RSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA512:ECDSA+SHA512:RSA+SHA384:ECDSA+SHA384:RSA+SHA256:ECDSA+SHA256:RSA+SHA224:ECDSA+SHA224:RSA+SHA1:ECDSA+SHA1
Shared Requested Signature Algorithms: RSA+SHA512:ECDSA+SHA512:RSA+SHA384:ECDSA+SHA384:RSA+SHA256:ECDSA+SHA256:RSA+SHA224:ECDSA+SHA224
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, prime256v1, 256 bits
---
SSL handshake has read 1072 bytes and written 423 bytes
Verification error: self-signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-CHACHA20-POLY1305
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-CHACHA20-POLY1305
    Session-ID: E5935763D407F7CE037E1EE9F10B714E5347182F06929B4C07A17FCA936C37AB
    Session-ID-ctx: 
    Master-Key: 1F6A12558802A1F2DE12478C3E1146A6ECCB4F1A08CF657B14C196CA7FC9DDBD873C83EFC3DE1DBE0F72D138815E65F1
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1716205017
    Timeout   : 7200 (sec)
    Verify return code: 18 (self-signed certificate)
    Extended master secret: yes
---

The application I am using is available here: https://github.com/vricosti/TestAndroidTVRemoteApp

At the beginning a self signed certificate is generated inside packages/androidtv-remote/src/certificate/CertificateGenerator.js

this.cert = await CertificateGenerator.generateFull(
                this.service_name,
                'CNT',
                'ST',
                'LOC',
                'O',
                'OU'
            );

this.cert object holds 2 fields, key and cert holding private key and certificate in pem

and then this certificate was used to start the tls connection:

async start() {
        return new Promise((resolve, reject) => {
            let options = {
                port: this.port,
                host : this.host,
                key: this.certs.key,
                cert: this.certs.cert,
                rejectUnauthorized: false,
            };
            
            if (jsEnv.isNodeOrDeno) {
                console.debug('set options to use node:tls');

                this.client = tls.connect(options, () => {
                    console.debug(this.host + " Pairing connected");
                });

            } else if (jsEnv.isReactNative) {
                console.debug('set options to use react-native-tcp-socket');
                options.tls = true;
                options.tlsCheckValidity = false;
                options.cert = this.certs.cert;
                
                this.client = TcpSockets.connectTLS(options, () => {
                    console.debug(this.host + " Pairing connected");
                });
            }

            this.client.pairingManager = this;

            //const connectEventName = jsEnv.isNodeOrDeno ? "secureConnect" : "connect";
            this.client.on("secureConnect", () => {
                console.debug(this.host + " Pairing secure connected ");
                this.client.write(this.pairingMessageManager.createPairingRequest(this.service_name));
            });

            this.client.on('data', (data) => {
                let buffer = Buffer.from(data);
                this.chunks = Buffer.concat([this.chunks, buffer]);

                if(this.chunks.length > 0 && this.chunks.readInt8(0) === this.chunks.length - 1){

                    let message = this.pairingMessageManager.parse(this.chunks);

                    console.debug("Receive : " + Array.from(this.chunks));
                    console.debug("Receive : " + JSON.stringify(message.toJSON()));

                    if (message.status !== this.pairingMessageManager.Status.STATUS_OK){
                        this.client.destroy(new Error(message.status));
                    }
                    else {
                        if(message.pairingRequestAck){
                            this.client.write(this.pairingMessageManager.createPairingOption());
                        }
                        else if(message.pairingOption){
                            this.client.write(this.pairingMessageManager.createPairingConfiguration());
                        }
                        else if(message.pairingConfigurationAck){
                            this.emit('secret');
                        }
                        else if(message.pairingSecretAck){
                            console.debug(this.host + " Paired!");
                            this.client.destroy();
                        }
                        else {
                            console.debug(this.host + " What Else ?")
                        }
                    }
                    this.chunks = Buffer.from([]);
                }
            });

            this.client.on('close', (hasError) => {
                console.debug(this.host + " Pairing Connection closed", hasError);
                if(hasError){
                    reject(false);
                }
                else{
                    resolve(true);
                }
            });

            this.client.on('error', (error) => {
                console.error(error);
            });
        });
    }

I have the following error:

Entering useEffect()
 LOG  Before instantiating AndroidRemote
 LOG  AndroidRemote.constructor
 LOG  After instantiating AndroidRemote
 LOG  Before start()
 LOG  AndroidRemote.start
 LOG  before CertificateGenerator.generateFull
 LOG  Entering generateFull
 LOG  after generateKeyPair with keys:  {"privateKey": {"d": {"data": [Array], "s": 0, "t": 79}, "dP": {"data": [Array], "s": 0, "t": 40}, "dQ": {"data": [Array], "s": 0, "t": 40}, "decrypt": [Function anonymous], "e": {"data": [Array], "s": 0, "t": 1}, "n": {"data": [Array], "s": 0, "t": 79}, "p": {"data": [Array], "s": 0, "t": 40}, "q": {"data": [Array], "s": 0, "t": 40}, "qInv": {"data": [Array], "s": 0, "t": 40}, "sign": [Function anonymous]}, "publicKey": {"e": {"data": [Array], "s": 0, "t": 1}, "encrypt": [Function anonymous], "n": {"data": [Array], "s": 0, "t": 79}, "verify": [Function anonymous]}}
 LOG  after createCertificate with cert:  {"extensions": [], "generateSubjectKeyIdentifier": [Function anonymous], "getExtension": [Function anonymous], "isIssuer": [Function anonymous], "issued": [Function anonymous], "issuer": {"addField": [Function anonymous], "attributes": [], "getField": [Function anonymous], "hash": null}, "md": null, "publicKey": null, "serialNumber": "00", "setExtensions": [Function anonymous], "setIssuer": [Function anonymous], "setSubject": [Function anonymous], "siginfo": {"algorithmOid": null}, "sign": [Function anonymous], "signature": null, "signatureOid": null, "subject": {"addField": [Function anonymous], "attributes": [], "getField": [Function anonymous], "hash": null}, "validity": {"notAfter": 2024-05-17T08:13:05.387Z, "notBefore": 2024-05-17T08:13:05.387Z}, "verify": [Function anonymous], "verifySubjectKeyIdentifier": [Function anonymous], "version": 2}
 LOG  after CertificateGenerator.generateFull
 LOG  before new PairingManager
 LOG  PairingMessageManager.constructor
 LOG  after new PairingManager
 DEBUG  set options to use react-native-tcp-socket
 Read error: ssl=0x756ae993d8: Failure in SSL library, usually a protocol error
error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/tls_record.cc:592 0x751aef9020:0x00000003)

if use rejectUnauthorized: true

 ERROR  java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

So in both case it fails...

I tried to add the server certificate and pass it but same error:

let options = {
                port: this.port,
                host : this.host,
                ca: require('../../../../server-cert.pem'),
                key: this.certs.key,
                cert: this.certs.cert,
                //rejectUnauthorized: false,
            };

and I even check that inside the android code it receives the passed certificate...

thanks in advance

@vricosti vricosti added the bug Something isn't working label May 17, 2024
@vricosti vricosti changed the title How to solve: Trust anchor for certification path not found. error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE or CertPathValidatorException May 17, 2024
@vricosti vricosti changed the title error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE or CertPathValidatorException SSL error when porting from node:tls with a self sign certificate (SSLV3_ALERT_HANDSHAKE_FAILURE or CertPathValidatorException) May 17, 2024
@vricosti
Copy link
Contributor Author

Ok I found the fix and basically it implies to save the cert and the private key inside a keystore, I forked it.

@ManuelLatorre98
Copy link

Ok I found the fix and basically it implies to save the cert and the private key inside a keystore, I forked it.

you mean the private key of client or server?

@vricosti
Copy link
Contributor Author

vricosti commented May 23, 2024

Ok I found the fix and basically it implies to save the cert and the private key inside a keystore, I forked it.

you mean the private key of client or server?

private key of client
In my branch I have also allowed the possibility to load key/cert/ca as a string:
master...vricosti:react-native-tcp-socket:dev/more-node-tls-compliant

Now I will try to implement getCertificate and getPeerCertificate, on android it should be easy but no idea about ios.

@riteshNemade
Copy link

Hello @vricosti,
Is there a way to connect iOS devices with TLS certificate authority, certificate, key if we provide them as PEM strings? I tried passing the file path and the certificate as string but no luck.

@vricosti
Copy link
Contributor Author

Hello @vricosti, Is there a way to connect iOS devices with TLS certificate authority, certificate, key if we provide them as PEM strings? I tried passing the file path and the certificate as string but no luck.

I don't think so because I have only implemented on android, I should try it on iOS but don't know yet if it will be complicated or not...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants