Uso de credenciales de certificado con el nodo MSAL

Puede crear aplicaciones cliente confidenciales con MSAL Node (aplicaciones web, aplicaciones de servicio, etc.). Una credencial de cliente es obligatoria para los clientes confidenciales.

Prerequisites

Puede crear aplicaciones cliente de tipo confidencial con MSAL Node (aplicaciones web, aplicaciones daemon, etc.). Una credencial de cliente es obligatoria para los clientes confidenciales. Las credenciales de cliente pueden ser:

  • managed identity: se trata de un escenario sin certificados, donde la confianza se establece a través de la infraestructura de Azure. No se requiere ninguna administración de secretos o certificados. MSAL aún no implementa esta característica, pero puede usar Azure SDK de identidad en su lugar. Consulte la documentación sobre identidades administradas para recursos de Azure.
  • clientSecret: una cadena secreta generada durante el registro de la aplicación o el registro posterior actualizado para una aplicación existente. Esto no se recomienda para producción.
  • clientCertificate: un certificado establecido durante el registro de la aplicación. El certificado debe tener la clave privada, ya que se usa para firmar una aserción que MSAL genera. thumbprintSha256 es una huella digital X.509 SHA-256 del certificado y privateKey es la clave privada codificada en PEM.
  • clientAssertion: en lugar de permitir que MSAL cree una aserción, el desarrollador de aplicaciones toma el control. Resulta útil para agregar notificaciones adicionales a la aserción o para usar KeyVault para firmar, en lugar de un certificado local. El certificado usado para firmar la aserción todavía debe establecerse durante el registro de la aplicación.

Nota: Puede que las apps 1P también deban enviar x5c. Esta es la cadena de certificados X.509 utilizada en escenarios de autenticación por nombre del sujeto o emisor.

Uso de secretos y certificados de forma segura

Los secretos nunca se deben codificar de forma dura. El paquete dotenv npm se puede usar para almacenar secretos o certificados en un archivo .env (ubicado en el directorio raíz del proyecto) que se debe incluir en .gitignore para evitar cargas accidentales de los secretos.

Los certificados también se pueden leer desde archivos a través del módulo fs de NodeJS. Sin embargo, nunca deben almacenarse en el directorio del proyecto. Las aplicaciones de producción deben capturar certificados de Azure KeyVault u otros almacenes de claves seguros.

Consulte certificados y secretos para obtener más información.

Consulte el ejemplo de MSAL: auth-code-with-certs

Registro de certificados

Si no tiene un certificado, puede crear un certificado autofirmado mediante PowerShell o mediante Azure KeyVault.

Debe cargar el certificado en Microsoft Entra ID.

  1. Vaya a Azure portal y seleccione el registro de la aplicación Microsoft Entra.
  2. Seleccione la hoja Certificados y secretos de la izquierda.
  3. Haga clic en Cargar certificado y seleccione el archivo de certificado que se va a cargar (por ejemplo, example.crt).
  4. Haga clic en Agregar. Una vez cargado el certificado, se muestran la huella digital (SHA-256),la fecha de inicio y los valores de expiración .

Para obtener más información, consulte: Registro del certificado con Plataforma de identidad de Microsoft

Inicialización del nodo MSAL con certificados

const msal = require('@azure/msal-node');
require('dotenv').config(); // process.env now has the values defined in a .env file

const config = {
    auth: {
        clientId: "YOUR_CLIENT_ID",
        authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
        clientCertificate: {
            thumbprintSha256: process.env.thumbprint,
            privateKey: process.env.privateKey,
        }
    }
};

// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);

Se espera que tanto thumbprintSha256 como privateKey sean cadenas. privateKey se espera que tenga el formato siguiente (PKCS#8):

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDkpKPrsfpIijS3
z2HCpDsa7dxOsKIrm7F1AtGBjyB0yVDjlh/FA7jT5sd2ypBh3FVsZGJudQsLRKfE
// ...
-----END ENCRYPTED PRIVATE KEY-----

Note

Como alternativa, la clave privada puede comenzar por -----BEGIN PRIVATE KEY----- ( PKCS#8 sin cifrar) o -----BEGIN RSA PRIVATE KEY----- (PKCS#1). Estos formatos también están permitidos. Se puede usar lo siguiente para convertir cualquier clave compatible al tipo de clave PKCS#8:

openssl pkcs8 -topk8 -inform PEM -outform PEM -in example.key -out example.key

Si ha cifrado la clave privada (o si la clave privada ya está cifrada) con una frase de contraseña, deberá descifrarla antes de pasarla al nodo MSAL.

Importante: Nunca codifique las contraseñas de forma rígida en el código fuente. Tanto la clave privada del certificado como la contraseña de descifrado opcional deben capturarse desde una ubicación segura (por ejemplo, Azure KeyVault) e implementarse de forma segura con la API web.

Esto se puede hacer mediante el módulo criptográfico de Node. Use el createPrivateKey() método para analizar y exportar la clave:

const fs = require('fs');
const crypto = require('crypto');

const privateKeySource = fs.readFileSync('<path_to_key>/example.key')

const privateKeyObject = crypto.createPrivateKey({
    key: privateKeySource,
    passphrase: process.env.YOUR_PASSPHRASE,
    format: 'pem'
});

const privateKey = privateKeyObject.export({
    format: 'pem',
    type: 'pkcs8'
});

(Opcional) Conversión de pfx a pem

OpenSSL se puede usar para convertir archivos de certificado codificados pfx en pem:

    openssl pkcs12 -in certificate.pfx -out certificate.pem

Si la conversión debe producirse mediante programación, es posible que tenga que confiar en un paquete de terceros, ya que Node.js no ofrece ningún método nativo para esto. Por ejemplo, con una implementación de TLS popular como node-forge, puede hacer lo siguiente:

const forge = require('node-forge');

/**
 * @param {string} pfx: certificate + private key combination in pfx format
 * @param {string} passphrase: passphrase used to encrypt pfx file
 * @returns {Object}
 */
function convertPFX(pfx, passphrase = null) {

    const asn = forge.asn1.fromDer(forge.util.decode64(pfx));
    const p12 = forge.pkcs12.pkcs12FromAsn1(asn, true, passphrase);

    // Retrieve key data
    const keyData = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag })[forge.pki.oids.pkcs8ShroudedKeyBag]
        .concat(p12.getBags({ bagType: forge.pki.oids.keyBag })[forge.pki.oids.keyBag]);

    // Retrieve certificate data
    const certBags = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag];
    const certificate = forge.pki.certificateToPem(certBags[0].cert)

    // Convert a Forge private key to an ASN.1 RSAPrivateKey
    const rsaPrivateKey = forge.pki.privateKeyToAsn1(keyData[0].key);

    // Wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
    const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);

    // Convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
    const privateKey = forge.pki.privateKeyInfoToPem(privateKeyInfo);

    console.log("Converted certificate: \n", certificate);
    console.log("Converted key: \n", privateKey);

    return {
        certificate: certificate,
        key: privateKey
    };
}

(Opcional) Creación de un servidor HTTPS

El protocolo OAuth 2.0 recomienda usar una conexión HTTPS siempre que sea posible. La mayoría de los servicios en la nube, como Azure App Service, proporcionarán conexión HTTPS de forma predeterminada a través del proxy. Si con fines de prueba desea configurar su propio servidor HTTPS, consulte la documentación de Node.js para obtener instrucciones sobre cómo crear un servidor HTTPS.

También deberá agregar los certificados autofirmados a lacadena de claves del / de credenciales del sistema operativo para omitir la directiva de seguridad del explorador. Es posible que todavía vea una advertencia en el explorador después (por ejemplo, Chrome).

Warning

Es posible que necesite privilegios de administrador para ejecutar los comandos anteriores.

Problemas comunes

En algunos casos, puede recibir un error de Microsoft Entra ID al intentar autenticarse mediante certificados, como el AADSTS700027: Client assertion contains an invalid signature error , que indica que los certificados o claves privadas que se usan para inicializar el nodo MSAL tienen un formato incorrecto. Un motivo común es que la cadena de clave privada o certificado que se proporciona al nodo MSAL contiene caracteres inesperados, como carros de retorno (\r) o nuevas líneas (\n):

-----BEGIN CERTIFICATE-----\nMIIDDzCCAfegAwIBAgIJAMkyzQVK88NHMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMQ4wDAYDVQQHEwVLaXN0YTEQMA4G0fbkqbKulrchGbNgkankZtEVg4PGjobZq7B+njvcVa7SsWF/WLq5AUbw==\r\n-----END CERTIFICATE-----

Alternativamente, su archivo de certificado / clave puede contener atributos de bolsa:

Bag Attributes
    localKeyID: 28 B5 8E 16 11 88 E9 00 58 D5 76 30 12 B9 59 B8 E4 CE 7C AA
subject=/C=UK/ST=Suffolk/L=Ipswich/O=Example plc/CN=alice
issuer=/C=UK/ST=Suffolk/L=Ipswich/O=Example plc/CN=Certificate Authority/emailAddress=ca@example.com\n
-----BEGIN CERTIFICATE-----
MIIDDzCCAfegAwIBAgIJAMkyzQVK88NHMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD
VQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMQ4wDAYDVQQHEwVLaXN0YTEQMA4G
0fbkqbKulrchGbNgkankZtEVg4PGjo+Y8MdMjtfSZB29hwYvfMX09jzJ68ZqmpYQ
njvcVtLbEZN5OGCkaslb/f2OxLbsUNgIbws538WnaaufDvKmQe2kUdWmpl9Wn9Bf
bZq7B+njvcVa7SsWF/WLq5AUbw==
-----END CERTIFICATE-----

En tales casos, usted es responsable de limpiar la cadena antes de pasarla a la configuración de MSAL Node. Por ejemplo:

const msal = require('@azure/msal-node');
const fs = require('fs');

const privateKeySource = fs.readFileSync('<path_to_key>/certs/example.key');
const privateKey = Buffer.from(privateKeySource, 'base64').toString().replace(/\r/g, "").replace(/\n/g, "");

const config = {
    auth: {
        clientId: "YOUR_CLIENT_ID",
        authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
        clientCertificate: {
            thumbprintSha256: process.env.thumbprint,
            privateKey: privateKey,
        }
    }
};

// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);

Consulte también