You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PFX证书获取Microsoft Entra令牌报错:RS256需非对称密钥

解决PFX证书获取Azure AD令牌时的RS256密钥错误

错误原因

Error: secretOrPrivateKey must be an asymmetric key when using RS256 本质是因为RS256算法要求使用非对称加密的私钥签名,而直接传入的PFX文件是一个容器格式,包含了证书链和私钥的集合,并非纯私钥,jsonwebtoken库无法直接识别。

解决方案

方案1:用OpenSSL提前将PFX转成PEM格式私钥

先通过OpenSSL命令从PFX中提取纯私钥:

# 从PFX提取私钥到PEM文件,若PFX有密码,执行后按提示输入
openssl pkcs12 -in certificate.pfx -nocerts -out private.key -nodes

然后修改代码,读取PEM私钥文件传入jwt.sign:

const axios = require('axios');
const fs = require('fs');
const path = require('path');
const qs = require('querystring');
const jwt = require('jsonwebtoken'); 

// 读取PEM格式私钥
const privateKeyPath = path.join(__dirname, 'private.key');
const privateKeyPem = fs.readFileSync(privateKeyPath, 'utf8');

const tokenEndpoint = 'https://login.microsoftonline.com/my-tenant-id/oauth2/token';
const clientId = 'my-app-id';

const tokenPayload = {
    aud: tokenEndpoint,
    iss: clientId,
    sub: clientId,
    jti: Math.random().toString(36).substring(7),
    nbf: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600,
};

// 使用PEM私钥签名
const clientAssertion = jwt.sign(tokenPayload, privateKeyPem, { algorithm: 'RS256' });
console.log('Client Assertion:', clientAssertion);

const postData = {
    grant_type: 'client_credentials',
    client_id: clientId,
    resource: 'resource', // 替换为实际资源ID,如https://graph.microsoft.com
    client_assertion: clientAssertion,
    client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
};

// 无需配置httpsAgent的pfx,client assertion已完成认证
const axiosConfig = {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
};

async function getToken() {
    try {
        const response = await axios.post(tokenEndpoint, qs.stringify(postData), axiosConfig);
        const token = response.data.access_token;
        console.log('Token:', token);
        return token;
    } catch (error) {
        console.error('Error:', error.message);
        if (error.response) {
            console.error('Azure AD错误响应:', error.response.data);
        }
        throw error;
    }
}

getToken().then(token => {
    console.log('Token:', token);
}).catch(err => {
    console.error('获取令牌失败:', err);
});

方案2:在代码中直接解析PFX提取私钥

使用node-forge库在代码中解析PFX容器,提取私钥:

  1. 安装依赖:
npm install node-forge
  1. 修改代码:
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const qs = require('querystring');
const jwt = require('jsonwebtoken');
const forge = require('node-forge');

const certPath = path.join(__dirname, 'certificate.pfx');
const certBuffer = fs.readFileSync(certPath);
const pfxPassword = ''; // 若PFX有密码,填写此处

// 解析PFX并提取私钥
const p12Asn1 = forge.asn1.fromDer(certBuffer.toString('binary'));
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, pfxPassword);
const privateKeyEntry = p12.getBags({bagType: forge.pki.oids.pkcs8ShroudedKeyBag})[forge.pki.oids.pkcs8ShroudedKeyBag][0];
const privateKeyPem = forge.pki.privateKeyToPem(privateKeyEntry.key);

const tokenEndpoint = 'https://login.microsoftonline.com/my-tenant-id/oauth2/token';
const clientId = 'my-app-id';

const tokenPayload = {
    aud: tokenEndpoint,
    iss: clientId,
    sub: clientId,
    jti: Math.random().toString(36).substring(7),
    nbf: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600,
};

const clientAssertion = jwt.sign(tokenPayload, privateKeyPem, { algorithm: 'RS256' });
console.log('Client Assertion:', clientAssertion);

const postData = {
    grant_type: 'client_credentials',
    client_id: clientId,
    resource: 'resource',
    client_assertion: clientAssertion,
    client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
};

const axiosConfig = {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
};

async function getToken() {
    try {
        const response = await axios.post(tokenEndpoint, qs.stringify(postData), axiosConfig);
        const token = response.data.access_token;
        console.log('Token:', token);
        return token;
    } catch (error) {
        console.error('Error:', error.message);
        if (error.response) {
            console.error('Azure AD错误响应:', error.response.data);
        }
        throw error;
    }
}

getToken().then(token => {
    console.log('Token:', token);
}).catch(err => {
    console.error('获取令牌失败:', err);
});

方案3:从Azure Key Vault获取私钥

若证书存储在Key Vault,需通过SecretClient获取私钥(Key Vault中证书的私钥以{证书名}-private为名的secret存储):

  1. 安装依赖:
npm install @azure/identity @azure/keyvault-secrets
  1. 修改代码:
const { DefaultAzureCredential } = require('@azure/identity');
const { SecretClient } = require('@azure/keyvault-secrets');
const jwt = require('jsonwebtoken');
const axios = require('axios');
const qs = require('querystring');

// 初始化Key Vault客户端
const credential = new DefaultAzureCredential();
const secretClient = new SecretClient('https://你的密钥保管库名称.vault.azure.net/', credential);

async function getPrivateKeyFromKeyVault(certName) {
    // 获取证书对应的私钥secret
    const secret = await secretClient.getSecret(`${certName}-private`);
    return secret.value; // 返回PEM格式私钥
}

async function getToken() {
    const privateKeyPem = await getPrivateKeyFromKeyVault('你的证书名称');
    const tokenEndpoint = 'https://login.microsoftonline.com/my-tenant-id/oauth2/token';
    const clientId = 'my-app-id';

    const tokenPayload = {
        aud: tokenEndpoint,
        iss: clientId,
        sub: clientId,
        jti: Math.random().toString(36).substring(7),
        nbf: Math.floor(Date.now() / 1000),
        exp: Math.floor(Date.now() / 1000) + 3600,
    };

    const clientAssertion = jwt.sign(tokenPayload, privateKeyPem, { algorithm: 'RS256' });

    const postData = {
        grant_type: 'client_credentials',
        client_id: clientId,
        resource: 'resource',
        client_assertion: clientAssertion,
        client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    };

    try {
        const response = await axios.post(tokenEndpoint, qs.stringify(postData), {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
        });
        console.log('Token:', response.data.access_token);
        return response.data.access_token;
    } catch (error) {
        console.error('Error:', error.message);
        if (error.response) {
            console.error('Azure AD错误响应:', error.response.data);
        }
        throw error;
    }
}

getToken().catch(err => console.error('获取令牌失败:', err));

关键注意点

  • 不要在axios的httpsAgent中配置PFX,这是客户端证书认证的方式,和client assertion认证无关,反而可能引发混淆。
  • 确保PFX文件的密码正确,若PFX无密码则留空字符串。
  • resource字段需替换为实际的目标资源ID,比如调用Graph API时填https://graph.microsoft.com。

内容的提问来源于stack exchange,提问作者Cristobal BL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 05:14:54