Explorar o código

fix: store dkim dns public keys in rsa format

Codex hai 1 mes
pai
achega
76fdd3056a
Modificáronse 3 ficheiros con 29 adicións e 5 borrados
  1. 17 0
      src/db.js
  2. 10 3
      src/dkim.js
  3. 2 2
      test/dkim.test.js

+ 17 - 0
src/db.js

@@ -2,6 +2,7 @@ import { mkdirSync } from 'node:fs';
 import crypto from 'node:crypto';
 import path from 'node:path';
 import { DatabaseSync } from 'node:sqlite';
+import { dkimPublicFromPrivateKey } from './dkim.js';
 
 let db;
 let secretKey = '';
@@ -108,6 +109,7 @@ export function initDatabase(dataDir, secret = '') {
     CREATE INDEX IF NOT EXISTS idx_domains_user_id ON domains(user_id);
     CREATE INDEX IF NOT EXISTS idx_events_user_id ON send_events(user_id);
   `);
+  normalizeDkimPublicKeys();
   return db;
 }
 
@@ -590,6 +592,21 @@ function ensureColumn(table, column, definition) {
   if (!columnExists(table, column)) requireDb().exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
 }
 
+function normalizeDkimPublicKeys() {
+  if (!tableExists('domains') || !columnExists('domains', 'dkim_private') || !columnExists('domains', 'dkim_public')) return;
+  const rows = requireDb().prepare('SELECT id, dkim_public, dkim_private FROM domains').all();
+  const update = requireDb().prepare('UPDATE domains SET dkim_public = ?, updated_at = ? WHERE id = ?');
+  for (const row of rows) {
+    if (!row.dkim_private) continue;
+    try {
+      const publicKey = dkimPublicFromPrivateKey(row.dkim_private);
+      if (publicKey && publicKey !== row.dkim_public) update.run(publicKey, now(), row.id);
+    } catch {
+      // Leave legacy or malformed rows untouched; rotating DKIM from the UI can repair them.
+    }
+  }
+}
+
 function publicUser(row) {
   if (!row) return null;
   return {

+ 10 - 3
src/dkim.js

@@ -4,7 +4,7 @@ export function createDkimKeyPair() {
   const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
     modulusLength: 2048,
     publicKeyEncoding: {
-      type: 'spki',
+      type: 'pkcs1',
       format: 'pem'
     },
     privateKeyEncoding: {
@@ -20,11 +20,18 @@ export function createDkimKeyPair() {
 
 export function pemToDkimPublic(pem) {
   return pem
-    .replace(/-----BEGIN PUBLIC KEY-----/g, '')
-    .replace(/-----END PUBLIC KEY-----/g, '')
+    .replace(/-----BEGIN (?:RSA )?PUBLIC KEY-----/g, '')
+    .replace(/-----END (?:RSA )?PUBLIC KEY-----/g, '')
     .replace(/\s+/g, '');
 }
 
+export function dkimPublicFromPrivateKey(privateKey) {
+  const publicKey = crypto
+    .createPublicKey(privateKey)
+    .export({ type: 'pkcs1', format: 'pem' });
+  return pemToDkimPublic(publicKey);
+}
+
 export function buildDkimRecord(publicKey) {
   return `v=DKIM1; k=rsa; p=${publicKey}`;
 }

+ 2 - 2
test/dkim.test.js

@@ -98,9 +98,9 @@ function relaxedBody(body) {
 
 function dkimPublicPem(publicKey) {
   return [
-    '-----BEGIN PUBLIC KEY-----',
+    '-----BEGIN RSA PUBLIC KEY-----',
     publicKey.match(/.{1,64}/g).join('\n'),
-    '-----END PUBLIC KEY-----',
+    '-----END RSA PUBLIC KEY-----',
     ''
   ].join('\n');
 }