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

Node.js读取证书文件夹后,如何提取证书对应的域名?

从X509证书中提取域名的Node.js实现

核心结论

可以直接从X509证书中提取关联域名,Node.js的crypto.X509Certificate对象提供了相关属性来获取证书的域名信息,包括传统的通用名称(CN)和现代证书常用的主题备用名称(SAN)。

需要修正的代码问题

你的代码存在几个影响功能的问题:

  • 未引入path模块,代码中使用了path.extname等方法但未声明const path = require('path');
  • 证书目录中的文件后缀是.crt,但代码中过滤的是.cert,会导致无法匹配到证书文件
  • 未引入X509Certificate,需要从crypto模块导入:const { X509Certificate } = require('crypto');
  • 回调嵌套过深,容易引发逻辑混乱,建议改用同步读取或Promise化的文件操作方法

提取域名的具体实现

证书的域名信息主要来自两个字段:

  1. Subject Alternative Names(SAN):现代SSL证书的标准字段,支持绑定多个域名
  2. Common Name(CN):旧版证书使用的单域名字段

以下是修改后的完整代码,实现从证书中提取域名并构建secureContext:

const fs = require('fs');
const path = require('path');
const { exit } = require('process');
const { X509Certificate } = require('crypto');
const tls = require('node:tls');

let https;
try {
  https = require('node:https');
} catch (err) {
  console.log('https support is disabled!');
  exit(-1);
}

const cert_dir = '/etc/myapp/certs';
const secureContext = {};

// 检查证书目录是否存在
try {
  fs.statSync(cert_dir);
} catch (err) {
  console.log(err);
  exit(-1);
}

// 读取目录下的所有文件
const files = fs.readdirSync(cert_dir);
// 过滤出.crt后缀的证书文件
const certFiles = files.filter(item => path.extname(item) === '.crt');

certFiles.forEach(crtFile => {
  const certPath = path.join(cert_dir, crtFile);
  // 对应密钥文件后缀为.pem,替换后缀获取路径
  const keyPath = path.join(cert_dir, crtFile.replace('.crt', '.pem'));

  try {
    // 验证密钥文件存在
    fs.statSync(keyPath);
    const certBuffer = fs.readFileSync(certPath);
    const x509 = new X509Certificate(certBuffer);

    let domains = [];

    // 优先提取SAN字段的域名
    const san = x509.subjectAltName;
    if (san) {
      domains = san.split(', ')
        .filter(item => item.startsWith('DNS:'))
        .map(item => item.slice(4));
    }

    // 无SAN时提取CN字段
    if (domains.length === 0) {
      const cnMatch = x509.subject.match(/CN=([^,]+)/);
      if (cnMatch) {
        domains.push(cnMatch[1]);
      }
    }

    // 为每个域名创建安全上下文
    if (domains.length > 0) {
      const context = tls.createSecureContext({
        key: fs.readFileSync(keyPath),
        cert: certBuffer
      });
      domains.forEach(domain => {
        secureContext[domain] = context;
        console.log(`绑定域名 ${domain} 到证书 ${crtFile}`);
      });
    } else {
      console.log(`证书 ${crtFile} 中未找到有效域名`);
    }
  } catch (err) {
    console.log(`处理证书 ${crtFile} 失败: ${err.message}`);
  }
});

// 服务器配置
const options = {
  SNICallback: function (domain, cb) {
    if (secureContext[domain]) {
      cb ? cb(null, secureContext[domain]) : secureContext[domain];
    } else {
      cb ? cb(new Error('No keys/certificates for domain requested')) : 
          throw new Error('No keys/certificates for domain requested');
    }
  },
  // 默认证书(确保路径有效)
  key: fs.readFileSync('../path_to_key.pem'),
  cert: fs.readFileSync('../path_to_cert.crt'),
};

// 请求处理函数
function handle(req, res) {
  res.writeHead(200);
  res.end('Hello Secure World!\n');
}

const server = https.createServer(options, handle);
server.listen(449, () => {
  console.log('HTTPS server listening on port 449');
});

关键说明

  • SAN字段解析:subjectAltName返回的字符串格式为"DNS:example.com, DNS:www.example.com",需拆分后过滤出DNS类型的域名
  • CN字段降级:通过正则匹配subject字段中的CN=xxx部分,作为SAN字段的备选方案
  • 安全上下文:使用tls.createSecureContext生成对应域名的安全上下文,存入secureContext供SNI回调使用
  • 错误处理:单个证书处理失败不会中断整个流程,避免服务器启动异常

内容的提问来源于stack exchange,提问作者Dimitrios Desyllas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 23:40:58