目录

SecurityConfiguration: Secure Transmission Customization

Use Cases

In software development, security is crucial. Remote Communication Kit provides SecurityConfiguration for customizing secure transmission for your app, ensuring robust security. Configuring and using SecurityConfiguration correctly can significantly reduce the risk of app attacks. The following will detail how to use certificate verification to enhance security.

Certificate Verification Mode Related Parameters
remoteValidation: The client verifies the server certificate.

'system': Use the system default certificate.

'skip': Skip certificate verification.

CertificateAuthority: The content parameter specifies the certificate content, the filePath parameter specifies the certificate path, and the folderPath parameter specifies the certificate directory.

ValidationCallback: custom certificate.

certificate: The server verifies the client certificate. ClientCertificate (client certificate verification): The content, filePath, type, key, and keyPassword parameters specify the certificate content, certificate path, certificate type, private key, and private key password, respectively.

Constraints

The secure transmission customization capability supports phones, 2-in-1 devices, tablets, and wearables. Starting from version 5.1.1(19), it is also supported on TVs.

Client Verifying the Server Certificate

[h2]Using the System Default Certificate

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Use 'system' in remoteValidation, indicating that the system's default CA is used.

    async function testSystemRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          remoteValidation: 'system',
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Skipping Certificate Verification

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Use 'skip' in remoteValidation to skip certificate verification.

    async function testSkipRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          remoteValidation: 'skip',
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Using a String to Specify the Certificate Content

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Set content in remoteValidation to define the certificate content of the string type.

    const PEM_CA_STRING = '-----BEGIN CERTIFICATE-----\n' +
      'MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV\n' +
      'UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD\n' +
      'VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw\n' +
      'MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k\n' +
      'aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C\n' +
      'AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O\n' +
      'YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP\n' +
      'BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp\n' +
      'Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y\n' +
      '3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34\n' +
      'VOKa5Vt8sycX\n' +
      '-----END CERTIFICATE-----';
    
    async function testStringRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          remoteValidation: {
            content: PEM_CA_STRING,
          }
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Using Binary Data to Specify the Certificate Content

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Set content in remoteValidation to define the certificate content of the ArrayBuffer type.

    const PEM_CA_BINARY = '-----BEGIN CERTIFICATE-----\n' +
      'MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV\n' +
      'UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD\n' +
      'VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw\n' +
      'MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k\n' +
      'aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C\n' +
      'AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O\n' +
      'YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP\n' +
      'BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp\n' +
      'Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y\n' +
      '3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34\n' +
      'VOKa5Vt8sycX\n' +
      '-----END CERTIFICATE-----';
    
    async function testBinaryRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      const buffer = new ArrayBuffer(PEM_CA_BINARY.length);
      util.TextEncoder.create('utf-8').encodeIntoUint8Array(PEM_CA_BINARY, new Uint8Array(buffer));
      request.configuration = {
        security: {
          remoteValidation: {
            content: buffer,
          }
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Using a File to Specify the Certificate Content

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Prepare the certificate file. In this example, assume that the certificate is example.pem. Then use the OpenSSL to generate the target file.

    openssl x509 -subject_hash -in ./example.pem

    The command output is as follows. The first line is the target file name, with .0 used as the file name extension. Other content is the file content. The final file will be output as dd8e9d41.0. If you are using Windows, you may need to press Enter for the OpenSSL to exit.

    dd8e9d41
    -----BEGIN CERTIFICATE-----
    MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
    CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
    ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
    Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
    EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
    IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
    K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
    fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
    Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
    BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
    AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
    oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
    sycX
    -----END CERTIFICATE-----
  3. Specify the certificate path by setting filePath in remoteValidation. For details about file path, please refer to Application Files.

    async function testFileRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          remoteValidation: {
            filePath: '/data/storage/el1/bundle/entry/resources/resfile/dd8e9d41.0', // Rename the generated certificate and save it to the /resources/resfile/ directory.
          }
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Using a File Directory to Specify the Certificate

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Set folderPath in remoteValidation to specify the sandbox directory where the certificate is located. For details about file path, please refer to Application Files. For the format and content of the certificate file in the directory, see step 2 in example 5.

    async function testDirRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          remoteValidation: {
            folderPath: '/data/storage/el1/bundle/entry/resources/resfile/', // Replace the file path with the actual sandbox directory where the certificate is located.
          }
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Customizing Certificate Verification

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Check whether the certificate date is valid.

    function makeItTwoChar(s: string): string {
      if (s.length === 1) {
        return '0' + s;
      }
      return s;
    }
    function getASNDateString(): string {
      const date = new Date();
      let dateStr = date.getFullYear().toString().slice(2);
      dateStr += makeItTwoChar((date.getMonth() + 1).toString());
      dateStr += makeItTwoChar(date.getDate().toString());
      dateStr += makeItTwoChar(date.getHours().toString());
      dateStr += makeItTwoChar(date.getMinutes().toString());
      dateStr += makeItTwoChar(date.getSeconds().toString());
      return dateStr + 'Z';
    }
  3. Customize the certificate verification logic.

    async function validationRemoteServer(context: rcp.ValidationContext): Promise<boolean> {
      // If no certificate is returned from the server, the verification fails.
      let length = context.pemCerts.length;
      if (length <= 0) {
        return Promise.reject();
      }
      // In this example, the last certificate is the root certificate. Change it based on your service needs.
      const firstCaBlob: networkSecurity.CertBlob = {
        type: networkSecurity.CertType.CERT_TYPE_PEM,
        data: context.pemCerts[length - 1],
      }
      // If the second parameter of certVerificationSync is not set, the default CA (/etc/security/certificates) is used.
      if (networkSecurity.certVerificationSync(firstCaBlob) !== 0) {
        return Promise.reject();
      }
      // Add verification logic, for example, logic for verifying the date.
      for (const x of context.x509Certs) {
        let dateStr = getASNDateString();
        try {
          x.checkValidityWithDate(dateStr);
        } catch (e) {
          return Promise.reject();
        }
      }
      // ...
      // Verification successful.
      return Promise.resolve(true);
    }
  4. Configure the defined custom certificate validator in the configuration and use FETCH to initiate a request.

    async function testCustomRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          remoteValidation: validationRemoteServer,
        }
      }
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

Server Verifying the Client Certificate

[h2]Verifying a PEM Certificate

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. In this example, a self-signed certificate is used to demonstrate the complete process of generating a local certificate. Run the following OpenSSL commands in sequence to generate a private key, create a certificate signing request (CSR) file, issue a self-signed certificate based on the CSR file, and merge the private key and certificate into a complete PEM certificate file.

    Note that in real production environments, a certificate formally issued by a trusted CA is usually used rather than the self-signed certificate in the demonstration.

    openssl genrsa -out cert.key 2048
    openssl req -new -key cert.key -out cert.csr
    openssl x509 -req -in cert.csr -out cert.crt -signkey cert.key -CAcreateserial -days 3650
    openssl x509 -in cert.crt -out cert.pem -outform PEM
  3. In certificate, set filePath to specify the certificate path (or set content to specify the certificate content), set type to PEM to specify the certificate type, and set key and keyPassword to specify the private key and private key password of the certificate. For details about file path, please refer to Application Files.

    async function testPemRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          certificate: {
            filePath: '/data/storage/el1/bundle/entry/resources/resfile/cert.pem', // Place the generated certificate in the /resources/resfile/ directory.
            type: 'PEM',
            key: '/data/storage/el1/bundle/entry/resources/resfile/cert.key', // Private key of the certificate. Place it in the /resources/resfile/ directory. This field is optional.
            keyPassword: 'keyPassword', // Password of the certificate private key. Replace it with the actual one. This field is optional. In this example, this field does not take effect.
          }
        }
      };
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Verifying a DER Certificate

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. In certificate, set filePath to specify the certificate path (or set content to specify the certificate content), set type to DER to specify the certificate type, and set key and keyPassword to specify the private key and private key password of the certificate. Run the openssl x509 -in cert.pem -outform der -out cert.der command to convert the PEM file in the preceding example to a DER file. For details about file path, please refer to Application Files.

    async function testDerRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          certificate: {
            filePath: '/data/storage/el1/bundle/entry/resources/resfile/cert.der', // Place the generated certificate in the /resources/resfile/ directory.
            type: 'DER',
            key: '/data/storage/el1/bundle/entry/resources/resfile/cert.key', // Private key of the certificate. Place it in the /resources/resfile/ directory. This field is optional.
            keyPassword: 'keyPassword', // Password of the certificate private key. Replace it with the actual one. This field is optional. In this example, this field does not take effect.
          }
        }
      };
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

[h2]Verifying a P12 Certificate

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. In certificate, set filePath to specify the certificate path (or set content to specify the certificate content), set type to P12 to specify the certificate type, and set keyPassword to specify the private key password of the certificate. Run the openssl pkcs12 -export -out cert.p12 -inkey cert.key -in cert.pem command to convert the PEM file in the preceding example to a P12 certificate and set the password to 1234. For details about file path, please refer to Application Files.

    async function testPRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          certificate: {
            filePath: '/data/storage/el1/bundle/entry/resources/resfile/cert.p12', // Place the generated certificate in the /resources/resfile/ directory.
            type: 'P12', // The P12 certificate already contains the private key. You do not need to use the key field to specify one.
            keyPassword: '1234', // Password of the certificate private key. Replace it with the actual one. This field is optional.
          }
        }
      };
      try {
        const response = await session.fetch(request);
        console.info(`response statusCode: ${JSON.stringify(response.statusCode)}`);
        // ...
        console.info(`response: ${JSON.stringify(response.toString())}`);
      } catch (err) {
        console.error(`response error code is ${err.code}, error data is ${err.data}`);
        // ...
      } finally {
        session.close();
      }
    }

Pinning a Certificate

Certificate pinning allows you to define the scope of trusted certificates. This is achieved by comparing the Base64-encoded SHA-256 hash of a certificate's public key, ensuring that only trusted certificates are accepted.

  1. Import the required modules.

    import { rcp } from '@kit.RemoteCommunicationKit';
  2. Configure the SHA-256 hash of the public key for trusted certificates in certificatePinning.

    const RIGHT_EXAMPLE_PUBLIC_KEY_SHA256_HASH = [
      'iMMpIJdSf5VlClHaxZReyhaLxLsmZMMNAiA2pMR8/M4=', // Replace the string with the actual one.
      'qBRjZmOmkSNJL0p70zek7odSIzqs/muR4Jk9xYyCP+E=', // Replace the string with the actual one.
      '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', // Replace the string with the actual one.
    ];
    
    async function testPinningRcp() {
      const session = rcp.createSession();
      const request = new rcp.Request('https://example.com'); // Replace the URL with the actual one.
      request.configuration = {
        security: {
          certificatePinning: [
            {
              kind: 'public-key',
              publicKeyHash: RIGHT_EXAMPLE_PUBLIC_KEY_SHA256_HASH[0],
              hashAlgorithm: 'SHA-256'
            },
            {
              kind: 'public-key',
              publicKeyHash: RIGHT_EXAMPLE_PUBLIC_KEY_SHA256_HASH[1],
              hashAlgorithm: 'SHA-256'
            },
          ]
        }
      };
      try {
        const response = await session.fetch(request);
        console.info('Test certificate pinning ' + response.statusCode);
        // ...
      } catch (e) {
        console.error('Test certificate pinning ' + JSON.stringify(e));
        // ...
      } finally {
        session.close();
      }
    }