Microsoft Entra ID の証明書認証で SMTP OAuth2 メール送信

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 の指定を疑うべし。 😄

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です