Microsoft Entra ID の証明書認証で SMTP OAuth2 メール送信
概要:これまで Client Secret を使用していた Node.js のメール送信バッチを、Microsoft Entra ID の証明書認証へ変更した際のメモです。
環境:
Windows 11
Node.js
nodemailer
@azure/msal-node 5.4.3
Exchange Online SMTP AUTH (OAuth2)
自己署名証明書の作成
PowerShell で作成。
$cert = New-SelfSignedCertificate -Subject "CN=MailBatchCert"
-CertStoreLocation “Cert:\CurrentUser\My” -KeyAlgorithm RSA
-KeyLength 2048 -KeyExportPolicy Exportable
-NotAfter (Get-Date).AddYears(5)
公開証明書(.cer)出力
Export-Certificate -Cert $cert
-FilePath C:\temp\MailBatchCert.cer
秘密鍵付き証明書(.pfx)出力
$password = ConvertTo-SecureString "任意のパスワード"
-AsPlainText `
-Force
Export-PfxCertificate -Cert $cert
-FilePath C:\temp\MailBatchCert.pfx `
-Password $password
Entra ID 側設定
アプリ登録の画面で、該当のアプリに公開証明書(.cer)をアップロード。(秘密鍵付きの PFX は登録しない)
PFXから秘密鍵を取得
const forge = require(“node-forge”);
const crypto = require(“crypto”);
function getThumbprint(cert) {
const der = forge.asn1.toDer(
forge.pki.certificateToAsn1(cert)
);
return crypto
.createHash(“sha1”)
.update(Buffer.from(der.getBytes(), “binary”))
.digest(“hex”)
.toUpperCase();
}
return {
privateKey: pkcs8PrivateKey,
thumbprint: getThumbprint(cert)
};
MSAL設定
const cca =
new msal.ConfidentialClientApplication({
auth: {
clientId,
authority: https://login.microsoftonline.com/${tenantId},
clientCertificate: {
thumbprint : certInfo.thumbprint,
privateKey:certInfo.privateKey
}}});
ハマったポイント
× thumbprintSha256 を使っていた
最初はthumbprintSha256を自前で作成していたが、
認証エラー
AADSTS700027
No certificate SHA-1 thumbprint,
certificate SHA-256 thumbprint,
nor keyId specified in token header
が発生。
○ thumbprint を使用
MSAL Node では
clientCertificate: {
thumbprint: “証明書のSHA1 Thumbprint”,
privateKey: “秘密鍵”
}
を指定する。
PowerShellで確認可能。
Get-PfxCertificate .\MailBatchCert.pfx |
fl Subject,Thumbprint
例:Thumbprint : 1D663C16FAF9BCA8E8D03BAD0B55B2E5D91E2EC7
× pkcs8PrivateKey
以下は認識されない。
clientCertificate: {
pkcs8PrivateKey: …
}
○ privateKey
正しくは
clientCertificate: {
privateKey: …
}
認証の仕組み
Client Secret 認証
Client ID+Client Secret→Entra ID
秘密情報を送信する方式。
証明書認証
秘密鍵(PFX)→JWTへ電子署名→Entra ID→公開鍵(CER)
で検証
秘密鍵そのものは送信されない。
そのため Secret より安全性が高い。
まとめ
Entra IDには .cer を登録
サーバーには .pfx を配置
node-forgeでPFXから秘密鍵取得
MSALは thumbprint と privateKey を指定
SMTP送信処理(nodemailer)は変更不要
Client Secretから証明書認証への移行は、MSAL部分の修正だけで実現できました。 🎉
今回の教訓
thumbprintSha256 と thumbprint(SHA1) は別物です。
AADSTS700027 が出たら、まず clientCertificate の thumbprint と privateKey の指定を疑うべし。 😄