使用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容器,提取私钥:
- 安装依赖:
npm install node-forge
- 修改代码:
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存储):
- 安装依赖:
npm install @azure/identity @azure/keyvault-secrets
- 修改代码:
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
相关产品推荐
相关产品推荐

