惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
有赞技术团队
有赞技术团队
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
博客园 - 【当耐特】
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
The Cloudflare Blog
Microsoft Azure Blog
Microsoft Azure Blog
腾讯CDC
Vercel News
Vercel News
IT之家
IT之家
MyScale Blog
MyScale Blog
博客园_首页
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
罗磊的独立博客

WishMeLz - NodeJS

宝塔Node管理器安装node版本提示:文件下载失败,请手动安装! - WishMeLz Minio 之 Nodejs - WishMeLz nuxt项目本地启动,多开标签一直显示加载中 - WishMeLz nvm win10安装 - WishMeLz nodejs 对接邮箱服务 imap - WishMeLz PM2搭配nvm使用不同版本Node启动项目 - WishMeLz nodejs 生成网站sitemap.xml - WishMeLz EA Racenet API - WishMeLz SSE(Server-Sent Events) - WishMeLz
Electron 主进程起一个可用的 HTTPS 静态服务器 - WishMeLz
Wish · 2026-01-20 · via WishMeLz - NodeJS

1. 启动 HTTPS 服务

在 Electron 的主进程中,我们可以直接利用 Node.js 原生的 httphttps 模块来创建服务。区别在于 HTTPS 需要配置 key (私钥) 和 cert (证书)。

代码实现

import { createServer as createHttpServer } from "http";
import { createServer as createHttpsServer } from "https";
import { generateSiteCert } from "./cert-service"; // 下文会提到的证书服务

const startServer = async (config: ServerConfig) => {
  const requestHandler = (req, res) => {
    // 处理静态文件或 API 代理逻辑
    res.end("Hello Secure World!");
  };

  let server;
  
  if (config.https.enabled) {
    // 关键点:动态生成或读取站点证书
    // 返回的 key/cert 是 PEM 格式的字符串
    const { key, cert } = generateSiteCert(config.https.domain);
    
    server = createHttpsServer({ 
      key, 
      cert 
    }, requestHandler);
  } else {
    server = createHttpServer(requestHandler);
  }

  server.listen(config.port, () => {
    console.log(`Server running at ${config.https.enabled ? 'https' : 'http'}://localhost:${config.port}`);
  });
};

2. SSL 证书的自动化生成与认证

我们使用 node-forge 库来完成这些操作,它是一个纯 JavaScript 实现的加密库。

2.1 生成根证书 (Root CA)

CA 证书的有效期通常设置得很长(如 10 年),并且需要标记为 cA: true

import * as forge from "node-forge";

export const generateCA = () => {
  const keys = forge.pki.rsa.generateKeyPair(2048);
  const cert = forge.pki.createCertificate();
  cert.publicKey = keys.publicKey;
  cert.serialNumber = "01";
  
  // 设置有效期 10 年
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10);

  const attrs = [
    { name: "commonName", value: `My Local Dev CA - ${Date.now()}` }, // 加上时间戳避免重名冲突
    { name: "organizationName", value: "Local Dev" }
  ];
  cert.setSubject(attrs);
  cert.setIssuer(attrs); // 自签名:颁发者等于使用者

  cert.setExtensions([
    { name: "basicConstraints", cA: true }, // 关键:标识为 CA
    { name: "keyUsage", keyCertSign: true, digitalSignature: true },
    { name: "subjectKeyIdentifier" } 
  ]);

  // 使用私钥签名
  cert.sign(keys.privateKey, forge.md.sha256.create());

  return {
    key: forge.pki.privateKeyToPem(keys.privateKey),
    cert: forge.pki.certificateToPem(cert)
  };
};

2.2 签发站点证书 (Site Certificate)

站点证书必须包含 SAN (Subject Alternative Name)Server Auth (扩展密钥用法),否则现代浏览器(Chrome 58+)会拦截。

export const generateSiteCert = (domain: string, caKeyPem: string, caCertPem: string) => {
  const caKey = forge.pki.privateKeyFromPem(caKeyPem);
  const caCert = forge.pki.certificateFromPem(caCertPem);

  const keys = forge.pki.rsa.generateKeyPair(2048);
  const cert = forge.pki.createCertificate();
  cert.publicKey = keys.publicKey;
  cert.serialNumber = Date.now().toString(); // 唯一的序列号
  
  // 有效期 1 年
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

  cert.setSubject([{ name: "commonName", value: domain }]);
  cert.setIssuer(caCert.subject.attributes); // 颁发者是我们的 CA

  cert.setExtensions([
    { name: "basicConstraints", cA: false },
    { 
      name: "keyUsage", 
      digitalSignature: true, 
      keyEncipherment: true 
    },
    {
      name: "extKeyUsage", // 关键:必须包含服务器验证
      serverAuth: true,
      clientAuth: true
    },
    {
      name: "subjectAltName", // 关键:SAN 字段
      altNames: [
        { type: 2, value: domain }, // DNS: mysite.local
        { type: 7, ip: "127.0.0.1" } // IP: 127.0.0.1
      ]
    },
    {
       name: "authorityKeyIdentifier", // 建立信任链的关键
       keyIdentifier: caCert.generateSubjectKeyIdentifier().data
    }
  ]);

  // 使用 CA 的私钥进行签名
  cert.sign(caKey, forge.md.sha256.create());

  return {
    key: forge.pki.privateKeyToPem(keys.privateKey),
    cert: forge.pki.certificateToPem(cert)
  };
};

2.3 信任根证书 (Windows)

生成的证书默认是不受系统信任的。我们需要调用 Windows 的 certutil 工具将我们的 CA 导入到“受信任的根证书颁发机构”存储区。

这需要管理员权限,我们可以通过 PowerShell 的 Start-Process -Verb RunAs 来触发 UAC 提权弹窗。

import { exec } from "child_process";

export const installCertTrust = (caCertPath: string) => {
  // certutil -addstore -user Root "path/to/rootCA.pem"
  const psCmd = `Start-Process certutil -ArgumentList '-addstore','-user','Root','"${caCertPath}"' -Verb RunAs -Wait`;
  
  return new Promise((resolve, reject) => {
    exec(`powershell "${psCmd}"`, (err) => {
      if (err) reject(err);
      else resolve();
    });
  });
};

3. 修改系统 Hosts 文件

为了支持 mysite.local 这样的自定义域名指向本地,我们需要修改系统的 Hosts 文件。

Windows 的 Hosts 文件位于 C:\Windows\System32\drivers\etc\hosts。直接写入通常会因为权限不足而失败(即使是管理员权限的 Node 进程有时也会受限)。

稳健的解决方案

  1. 读取原 Hosts 内容。
  2. 在内存中修改(添加 127.0.0.1 mysite.local)。
  3. 写入到一个临时文件。
  4. 使用提权 PowerShell 将临时文件强制复制覆盖系统 Hosts 文件。
import * as fs from "fs";
import * as path from "path";
import { app } from "electron"; // 获取临时目录路径

const HOSTS_PATH = "C:\\Windows\\System32\\drivers\\etc\\hosts";

export const updateHosts = (domain: string) => {
  // 1. 读取并修改内容
  let content = fs.readFileSync(HOSTS_PATH, "utf8");
  // ... (省略具体的字符串处理逻辑,如去重、追加) ...
  const newContent = content + `\r\n127.0.0.1 ${domain}`;

  // 2. 写入临时文件
  const tempPath = path.join(app.getPath("userData"), `hosts_temp_${Date.now()}`);
  fs.writeFileSync(tempPath, newContent);

  // 3. 提权覆盖
  // Copy-Item "temp" "hosts" -Force
  const psCmd = `Start-Process powershell -ArgumentList '-Command "Copy-Item ''${tempPath}'' ''${HOSTS_PATH}'' -Force"' -Verb RunAs -Wait`;

  return new Promise((resolve, reject) => {
    exec(`powershell "${psCmd}"`, (err) => {
      // 清理临时文件
      fs.unlinkSync(tempPath);
      
      if (err) reject(err);
      else resolve();
    });
  });
};

4. 总结

  1. Node-Forge 负责构建合规的证书链,骗过浏览器的安全检查。
  2. Certutil + PowerShell 负责打通操作系统的证书信任。
  3. Hosts Hack 负责实现自定义域名的本地回环解析。