ECDSA (Elliptic Curve Digital Signature Algorithm) gir kryptografisk sterk autentisering med nøkler som er 10 ganger kortere enn RSA. En P-256-nøkkel på 256 bit gir den samme 128-bits sikkerheten som en RSA-nøkkel på 3 072 bit, og selve signeringsoperasjonen tar under 1 millisekund på moderne maskinvare. Denne guiden viser deg hvordan du implementerer ECDSA digitale signaturer i Node.js fra starten av, steg for steg, med fullstendige kodeeksempler og et komplett arbeidsprosjekt du kan ta rett i bruk.
Hva er ECDSA og hvorfor bruke det i Node.js?
ECDSA er en digital signeringsalgoritme basert på elliptisk kurve-kryptografi (ECC). Den bruker det matematiske problemet med å beregne diskrete logaritmer på en elliptisk kurve for å sikre at bare den som eier den private nøkkelen kan produsere en gyldig signatur, mens hvem som helst med den offentlige nøkkelen kan verifisere den.
Algoritmen er definert i NIST FIPS 186-5 og brukes i en rekke kritiske protokoller: TLS 1.3-sertifikater, JWT ES256/ES384, kode-signering i npm, Bitcoin-transaksjoner og SSH-autentisering. Hvis du har verifisert en HTTPS-tilkobling i nettleseren din de siste fem minuttene, er det stor sjanse for at ECDSA var involvert i håndtrykket.
Sammenlignet med RSA har ECDSA tre konkrete fordeler for Node.js-utviklere:
- Kortere nøkler og signaturer: P-256 gir 128-bits sikkerhet med 256-bit nøkler og typisk 71-byte signaturer, mot RSA-3072 sine 3 072-bit nøkler og 384-byte signaturer. JWT-tokens er 20-30% kortere med ES256 enn RS256.
- Raskere signering: ECDSA-signering på P-256 tar typisk 0,31 ms mot RSA-2048-signering på 1,21 ms, en 4x ytelsesforbedring. For API-er som signerer mange tokens per sekund, er dette målbart.
- Innebygd Node.js-støtte:
crypto-modulen i Node.js støtter ECDSA uten eksterne avhengigheter. Du trenger ingen ekstra pakker for grunnleggende nøkkelgenerering, signering og verifisering.
En vanlig misforståelse er at “kortere nøkkel betyr svakere sikkerhet”. Det stemmer ikke for elliptisk kurve-kryptografi. Det diskrete logaritmeproblemet på elliptiske kurver er matematisk hardere enn faktoriseringsproblemet RSA baserer seg på, noe som gjør kortere nøkler tilstrekkelig for ekvivalent sikkerhet.
Forutsetninger og versjoner
Kontroller at du har følgende installert og konfigurert før du begynner:
| Programvare | Minimum versjon | Anbefalt versjon | Sjekk kommando |
|---|---|---|---|
| Node.js | 18.0.0 | 22.x LTS | node --version |
| npm | 9.0.0 | 10.x | npm --version |
| OpenSSL | 3.0 | 3.3+ | openssl version |
| OS | Linux, macOS, Windows | Linux eller macOS | uname -a |
Du bør ha grunnleggende kjennskap til JavaScript og asynkron programmering i Node.js. Forståelse av kryptografiske begreper som offentlig/privat nøkkel, hash-funksjoner og digitale signaturer er nyttig, men ikke obligatorisk, ettersom vi forklarer hvert konsept underveis.
For JWT-delen (Steg 8) trenger du jsonwebtoken-pakken. Alle andre kodeksempler bruker kun Nodes innebygde crypto-modul. Node.js 20+ anbefales for best ytelse med asynkron nøkkelgenerering og WebCrypto API-integrasjonen i Steg 10.
Steg 1: Sett opp prosjektet
Opprett en ny prosjektmappe og initialiser npm. Vi bruker kun innebygde Node.js-moduler for de grunnleggende operasjonene, så ingen ekstra pakkeinstallasjon er nødvendig til å begynne med.
mkdir ecdsa-demo
cd ecdsa-demo
npm init -y
mkdir keys output
Opprett hovedfilen index.js med nødvendige importer fra Node.js crypto-modulen:
'use strict';
const {
generateKeyPairSync,
generateKeyPair,
createSign,
createVerify,
createPrivateKey,
createPublicKey
} = require('crypto');
const fs = require('fs');
const path = require('path');
const KEYS_DIR = path.join(__dirname, 'keys');
const OUTPUT_DIR = path.join(__dirname, 'output');
[KEYS_DIR, OUTPUT_DIR].forEach(dir => {
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
});
Prosjektstrukturen vil se slik ut etter at alle steg er fullført:
ecdsa-demo/
├── index.js # Hovedskript
├── keys.js # Nøkkelgenerering
├── signer.js # Signering
├── verifiser.js # Verifisering
├── fil-signer.js # Filsignering via streams
├── jwt-ecdsa.js # JWT med ES256
├── webcrypto-ecdsa.js # WebCrypto API-eksempel
├── ecdsa.test.js # Enhetstester
├── app.js # Komplett Express API
├── keys/
│ ├── private.pem # PKCS#8 privat nøkkel (hold hemmelig)
│ └── public.pem # SubjectPublicKeyInfo offentlig nøkkel
└── package.json
Steg 2: Velg riktig elliptisk kurve
Valget av kurve er en av de viktigste avgjørelsene du tar i et ECDSA-oppsett. Ulike kurver tilbyr forskjellige sikkerhetsnivåer og ytelsesprofiler. Node.js støtter alle NIST-kurvene og secp256k1 (Bitcoin/Ethereum-kurven).
| Kurve | Nøkkelstørrelse | Sikkerhetsbit | RSA-ekvivalent | Typisk bruk |
|---|---|---|---|---|
| P-256 (secp256r1) | 256 bit | 128 | RSA-3072 | TLS, JWT ES256, kode-signering |
| P-384 (secp384r1) | 384 bit | 192 | RSA-7680 | Statlige og finansielle systemer |
| P-521 (secp521r1) | 521 bit | 260 | RSA-15360+ | Svært høy sikkerhetsklassifisering |
| secp256k1 | 256 bit | 128 | RSA-3072 | Bitcoin, Ethereum, blockchain |
| Ed25519 (EdDSA)* | 256 bit | 128 | RSA-3072 | SSH, generell bruk (ikke ECDSA) |
For de fleste Node.js-applikasjoner er P-256 det riktige valget. Den er allment støttet av nettlesere, TLS-biblioteker og JWT-implementasjoner, rask, og gir tilstrekkelig sikkerhet til minst 2030 ifølge NIST. P-384 er riktig for systemer med strengere regulatoriske krav, for eksempel bankinfrastruktur under PCI DSS eller norske offentlige systemer der NSM-retningslinjene tilsier høyere sikkerhetsklassifisering.
En viktig merknad om secp256k1: Selv om Bitcoin bruker secp256k1, er denne kurven ikke anbefalt for generell bruk utenfor blockchain-kontekster. Den mangler offisiell NIST-verifikasjon og støttes ikke like bredt i TLS-biblioteker. Velg P-256 til web-applikasjoner.
Ed25519 er teknisk sett en annen algoritme (EdDSA med Curve25519, ikke ECDSA), men er et fremragende alternativ for SSH-nøkler og generell signering. Se sammenligning i artikkelen om Ed25519 vs RSA: 33x raskere signaturer.
Steg 3: Generer et EC-nøkkelpar
Nøkkelgenerering er første steg i ethvert kryptografisk system. Vi bruker P-256-kurven som standard. Nøkkelen genereres med kryptografisk sterk tilfeldighet fra operativsystemets CSPRNG (Cryptographically Secure Pseudo-Random Number Generator), håndtert automatisk av OpenSSL via Node.js.
// keys.js - Nøkkelgenerering for ECDSA
'use strict';
const { generateKeyPairSync, generateKeyPair } = require('crypto');
const fs = require('fs');
const path = require('path');
// Synkron generering (for scripts og CLI-verktøy)
function genererNokkelpar(kurve = 'P-256') {
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: kurve,
publicKeyEncoding: {
type: 'spki', // SubjectPublicKeyInfo - standard for offentlige nøkler
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8', // PKCS#8 - standard for private nøkler
format: 'pem'
}
});
return { privateKey, publicKey };
}
// Asynkron generering (anbefalt for produksjon - blokkerer ikke event loop)
async function genererNokkelparAsync(kurve = 'P-256') {
return new Promise((resolve, reject) => {
generateKeyPair('ec', {
namedCurve: kurve,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
}, (err, publicKey, privateKey) => {
if (err) return reject(err);
resolve({ privateKey, publicKey });
});
});
}
// Lagre nøkler til disk med riktige filrettigheter
async function lagreNokler(kurve = 'P-256') {
const { privateKey, publicKey } = await genererNokkelparAsync(kurve);
const privPath = path.join('keys', 'private.pem');
const pubPath = path.join('keys', 'public.pem');
// mode 0o600: kun eier kan lese og skrive (ingen andre brukere)
fs.writeFileSync(privPath, privateKey, { mode: 0o600 });
fs.writeFileSync(pubPath, publicKey);
console.log(`EC-nøkkelpar (${kurve}) generert og lagret.`);
console.log(`Privat nokkel: ${privPath}`);
console.log(`Offentlig nokkel: ${pubPath}`);
return { privPath, pubPath };
}
lagreNokler('P-256').catch(console.error);
Kjør skriptet og inspisér de genererte nøklene:
# Kjor nøkkelgenerering
$ node keys.js
EC-noekkelpar (P-256) generert og lagret.
Privat nokkel: keys/private.pem
Offentlig nokkel: keys/public.pem
# Inspisér privat nøkkel med OpenSSL
$ openssl pkey -in keys/private.pem -text -noout
Private-Key: (256 bit)
priv:
00:c9:f3:4a:...
pub:
04:7b:e4:1d:...
ASN1 OID: prime256v1
NIST CURVE: P-256
# Inspisér offentlig nøkkel
$ openssl pkey -pubin -in keys/public.pem -text -noout
Public-Key: (256 bit)
pub:
04:7b:e4:1d:...
ASN1 OID: prime256v1
NIST CURVE: P-256
Den genererte private nøkkelen i PEM-format ser slik ut (PKCS#8-format, ukodet):
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8PM7zH3CuSf9Bkx4
3d5nQ7...dinFI4vRdW4wSGbz6hRANCAAR75B0...
-----END PRIVATE KEY-----
Steg 4: Signer data med ECDSA
Signeringsoperasjonen bruker den private nøkkelen til å produsere en kryptografisk signatur for et datasett. Under panseret hasher Node.js meldingen med SHA-256 og signerer hashen med ECDSA via OpenSSL. Signaturen beviser to ting: at avsenderen kontrollerer den private nøkkelen, og at meldingen ikke er endret siden signeringen.
// signer.js - Signering med ECDSA
'use strict';
const { createSign } = require('crypto');
const fs = require('fs');
function signerMelding(melding, privatNokkelPem) {
// createSign tar algoritmespesifikasjonen som argument
// 'SHA256' betyr SHA-256 hash + ECDSA-signering
const sign = createSign('SHA256');
sign.update(melding, 'utf8'); // Legg til data som skal signeres
sign.end(); // Ferdigstill datastrommen
// sign.sign() returnerer signaturen som en Buffer (DER-kodet)
const signaturBuf = sign.sign(privatNokkelPem);
return {
hex: signaturBuf.toString('hex'),
base64: signaturBuf.toString('base64'),
buffer: signaturBuf
};
}
// Praktisk eksempel: signer en finansiell transaksjon
const privatNokkel = fs.readFileSync('keys/private.pem', 'utf8');
// Inkluder alltid et tidsstempel for å forhindre replay-angrep
const transaksjon = JSON.stringify({
betalingsreferanse: 'INV-2026-001',
belop: 10000,
mottaker: '1234.56.78901',
timestamp: '2026-06-20T10:30:00Z',
nonce: 'a3f7b2c1d4e5'
});
const signatur = signerMelding(transaksjon, privatNokkel);
console.log('Melding:', transaksjon);
console.log('Signatur (base64):', signatur.base64);
console.log('Signatur lengde:', signatur.buffer.length, 'bytes');
// Lagre signatur til fil for senere bruk
fs.writeFileSync('output/signatur.b64', signatur.base64);
fs.writeFileSync('output/melding.json', transaksjon);
Eksempel pa utdata:
Melding: {"betalingsreferanse":"INV-2026-001","belop":10000,...}
Signatur (base64): MEYCIQDt3M7v9K2...8kL2nAIhAL+3wQ==
Signatur lengde: 71 bytes
P-256-signaturer er typisk 70-72 bytes i DER-format (to 32-byte tall r og s med DER-koding). Til sammenligning er en RSA-2048-signatur alltid noyaktig 256 bytes, og en RSA-4096-signatur alltid 512 bytes. Denne kompaktheten gjor ECDSA spesielt effektivt i protokoller der storrelse teller, som JWT-tokens sendt i HTTP-headere.
Steg 5: Verifiser en ECDSA-signatur
Verifisering bruker den offentlige nøkkelen og bekrefter to ting: at signaturen ble produsert av den tilsvarende private nøkkelen, og at meldingen ikke er endret siden signeringen. Verifiseringsoperasjonen kan utfores av hvem som helst med tilgang til den offentlige nøkkelen. Det er viktig a note at createVerify().verify() returnerer false pa ugyldig signatur i stedet for a kaste et unntak, slik at du alltid far et tydelig svar.
// verifiser.js - Verifisering av ECDSA-signatur
'use strict';
const { createVerify } = require('crypto');
const fs = require('fs');
function verifiserMelding(melding, offentligNokkelPem, signaturInput, kodingFormat = 'base64') {
const verify = createVerify('SHA256');
verify.update(melding, 'utf8');
verify.end();
// Konverter signatur til Buffer om nodvendig
const signaturBuf = Buffer.isBuffer(signaturInput)
? signaturInput
: Buffer.from(signaturInput, kodingFormat);
try {
return verify.verify(offentligNokkelPem, signaturBuf);
} catch (err) {
// Fanger bl.a. malformaterte signaturer og feil nøkkeltype
console.error('Verifiseringsfeil:', err.message);
return false;
}
}
// Les inn data og signatur fra fil
const offentligNokkel = fs.readFileSync('keys/public.pem', 'utf8');
const melding = fs.readFileSync('output/melding.json', 'utf8');
const signaturB64 = fs.readFileSync('output/signatur.b64', 'utf8').trim();
// Test 1: Gyldig signatur
const erGyldig = verifiserMelding(melding, offentligNokkel, signaturB64, 'base64');
console.log('Gyldig signatur:', erGyldig); // true
// Test 2: Manipulert melding
const manipulert = melding.replace('10000', '99000');
const erManipulertGyldig = verifiserMelding(manipulert, offentligNokkel, signaturB64, 'base64');
console.log('Manipulert melding gyldig:', erManipulertGyldig); // false
// Test 3: Manipulert signatur (en enkelt byte endret)
const signaturBuf = Buffer.from(signaturB64, 'base64');
signaturBuf[10] ^= 0x01; // XOR med 1 for a endre en bit
const erKorruptGyldig = verifiserMelding(melding, offentligNokkel, signaturBuf);
console.log('Korrupt signatur gyldig:', erKorruptGyldig); // false
Forventet utdata:
Gyldig signatur: true
Manipulert melding gyldig: false
Korrupt signatur gyldig: false
Legg merke til at selv en enkelt endret bit i meldingen eller signaturen gjor verifikasjonen ugyldig. Dette er den fundamentale egenskapen til kryptografiske signaturer: signaturen er matematisk bundet til noyaktig de dataene som ble signert, via SHA-256-hashen.
Steg 6: Signer og verifiser filer via streams
For storre filer som binærfiler, programpakker, logger eller dokumenter bor du lese filen som en datastrøm (stream) for a unnga a laste hele innholdet i minnet pa en gang. Node.js createSign er en Writable stream og kan motta data via pipe().
// fil-signer.js - Stream-basert fil-signering
'use strict';
const { createSign, createVerify } = require('crypto');
const fs = require('fs');
const path = require('path');
async function signerFil(filsti, privatNokkelPem) {
return new Promise((resolve, reject) => {
const sign = createSign('SHA256');
const stream = fs.createReadStream(filsti);
stream.on('error', reject);
sign.on('error', reject);
stream.pipe(sign);
stream.on('end', () => {
sign.end();
const signatur = sign.sign(privatNokkelPem);
resolve(signatur);
});
});
}
async function verifiserFil(filsti, offentligNokkelPem, signatur) {
return new Promise((resolve, reject) => {
const verify = createVerify('SHA256');
const stream = fs.createReadStream(filsti);
stream.on('error', reject);
stream.pipe(verify);
stream.on('end', () => {
verify.end();
try {
resolve(verify.verify(offentligNokkelPem, signatur));
} catch (err) {
reject(err);
}
});
});
}
// Eksempel: signer en Node.js-pakke
async function main() {
const privatNokkel = fs.readFileSync('keys/private.pem', 'utf8');
const offentligNokkel = fs.readFileSync('keys/public.pem', 'utf8');
const filsti = 'package.json';
console.log(`Signerer ${filsti}...`);
const signatur = await signerFil(filsti, privatNokkel);
const signatursti = path.join('output', `${path.basename(filsti)}.sig`);
fs.writeFileSync(signatursti, signatur);
console.log(`Signatur lagret: ${signatursti} (${signatur.length} bytes)`);
const erGyldig = await verifiserFil(filsti, offentligNokkel, signatur);
console.log(`Signaturverifisering: ${erGyldig ? 'OK' : 'FEIL'}`);
}
main().catch(console.error);
Stream-basert signering er kritisk for filer over noen megabytes. En Node.js-prosess med standard minnegrense pa 512 MB vil mislykkes hvis den forsøker a lese en 600 MB programpakke som en enkelt buffer. Stream-tilnærmingen bruker konstant minne uavhengig av filstorrelse.
Steg 7: Arbeid med PEM- og JWK-format
Nøkler finnes i ulike formater, og du vil ofte trenge a konvertere mellom dem. Node.js støtter import og eksport av nøkler i PEM, DER og JWK (JSON Web Key)-format. JWK er standarden for a publisere offentlige nøkler via et JWKS-endepunkt.
// nokkel-konvertering.js - Konvertering mellom nøkkelformater
'use strict';
const { createPrivateKey, createPublicKey, generateKeyPairSync } = require('crypto');
const fs = require('fs');
const { privateKey: privPem, publicKey: pubPem } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// Konverter PEM til JWK - brukes i JWKS-endepunkt
const pubKeyObj = createPublicKey(pubPem);
const pubJwk = pubKeyObj.export({ format: 'jwk' });
console.log('Offentlig noekkel som JWK:');
console.log(JSON.stringify(pubJwk, null, 2));
// Output:
// {
// "kty": "EC",
// "crv": "P-256",
// "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
// "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
// }
// Konverter JWK tilbake til PEM
const gjenopprettetPub = createPublicKey({ key: pubJwk, format: 'jwk' });
const gjenopprettetPem = gjenopprettetPub.export({ type: 'spki', format: 'pem' });
console.log('PEM-er identiske:', pubPem === gjenopprettetPem); // true
// Passordbeskytt privat nøkkel for lagring
const kryptertPriv = createPrivateKey(privPem).export({
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: process.env.KEY_PASSPHRASE || 'endre-dette-i-produksjon'
});
fs.writeFileSync('keys/private-encrypted.pem', kryptertPriv, { mode: 0o600 });
console.log('Kryptert privat noekkel lagret.');
// Les kryptert nøkkel tilbake
const lastetPriv = createPrivateKey({
key: fs.readFileSync('keys/private-encrypted.pem'),
format: 'pem',
passphrase: process.env.KEY_PASSPHRASE || 'endre-dette-i-produksjon'
});
console.log('Kryptert noekkel lastet OK, type:', lastetPriv.asymmetricKeyType); // ec
Et komplett JWKS-endepunkt for a la klienter hente offentlige nøkler:
// JWKS-endepunkt i Express - lar klienter coche offentlige nokler
app.get('/.well-known/jwks.json', (req, res) => {
const pubKeyObj = createPublicKey(offentligNokkel);
const jwk = pubKeyObj.export({ format: 'jwk' });
res.json({
keys: [{
...jwk,
kid: 'v1-2026-06', // Key ID for nøkkelrotasjon
use: 'sig', // Bruk: signering
alg: 'ES256' // Algoritme
}]
});
});
Steg 8: ECDSA i JWT (ES256, ES384, ES512)
JWT (JSON Web Tokens) med ECDSA-signaturer er standarden for sikker token-basert autentisering i moderne API-er. ES256 (P-256 + SHA-256), ES384 (P-384 + SHA-384) og ES512 (P-521 + SHA-512) er definert i RFC 7518 (JWA). Den asymmetriske karakteren til ECDSA gir en viktig fordel over HMAC-baserte tokens (HS256): kun signeringstjenesten trenger den private nøkkelen, mens alle tjenester som verifiserer tokens bare trenger den offentlige nøkkelen.
# Installer JWT-biblioteket
npm install jsonwebtoken
// jwt-ecdsa.js - JWT med ECDSA ES256
'use strict';
const jwt = require('jsonwebtoken');
const { generateKeyPairSync } = require('crypto');
const fs = require('fs');
// Generer nøkkelpar (i produksjon: bruk lagrede nøkler)
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// Signer en JWT med ES256
function lagToken(brukerdata) {
return jwt.sign(
{
sub: brukerdata.id.toString(),
name: brukerdata.navn,
roles: brukerdata.roller,
jti: Math.random().toString(36).slice(2) // JWT ID mot replay-angrep
},
privateKey,
{
algorithm: 'ES256',
expiresIn: '1h',
issuer: 'api.eksempel.no',
audience: 'app.eksempel.no'
}
);
}
// Verifiser og dekod JWT
function verifiserToken(token) {
try {
const decoded = jwt.verify(token, publicKey, {
algorithms: ['ES256'], // Eksplisitt algoritmeliste forhindrer algorithm confusion-angrep
issuer: 'api.eksempel.no',
audience: 'app.eksempel.no'
});
return { gyldig: true, data: decoded };
} catch (err) {
return { gyldig: false, feil: err.message };
}
}
// Test
const bruker = { id: 42, navn: 'Ola Nordmann', roller: ['admin', 'bruker'] };
const token = lagToken(bruker);
console.log('JWT-token (forkortet):', token.slice(0, 60) + '...');
console.log('Token lengde:', token.length, 'tegn');
const resultat = verifiserToken(token);
console.log('Gyldig:', resultat.gyldig); // true
console.log('Bruker-ID:', resultat.data?.sub); // 42
console.log('Roller:', resultat.data?.roles); // ['admin', 'bruker']
// Test med ugyldig token
const ugyldig = verifiserToken(token.slice(0, -5) + 'xxxxx');
console.log('Ugyldig token:', ugyldig.gyldig, '-', ugyldig.feil);
Et ES256-JWT-token har tre Base64URL-kodede deler separert av punktum. Headerdelen avkoder til {"alg":"ES256","typ":"JWT"}. Et typisk ES256-token er 271-320 tegn langt, mot 380-420 tegn for RS256. For autentisering i full OAuth 2.0-flyt med PKCE, se guiden om OAuth 2.0 i Node.js.
Steg 9: Lagre nøkler sikkert i produksjon
Sikker nøkkellagring er det svakeste leddet i de fleste ECDSA-implementasjoner. En kompromittert privat nøkkel ugyldiggjor alle signaturer og krever øyeblikkelig nøkkelrotasjon med varsling av alle parter som stolte pa de signerte dataene.
| Lagringsmetode | Sikkerhetsnivaa | Egnet for | Implementasjon |
|---|---|---|---|
| Hardkodet i kildekode | Ingen (aldri bruk) | Aldri | Uakseptabelt |
| .env-fil pa server | Lav | Kun lokal utvikling | dotenv-pakken |
| Filsystem med chmod 600 | Middels | Enkle serveroppsett | Innebygd i OS |
| Docker Secrets / K8s Secrets | Middels-høy | Container-orkestrasjon | docker secret create |
| AWS Secrets Manager / Azure Key Vault | Høy | Skybasert produksjon | SDK-integrasjon |
| HSM (Hardware Security Module) | Svært høy | Finansielle systemer, CA | PKCS#11-bibliotek |
For de fleste Node.js-applikasjoner i produksjon er en skybasert hemmelighetslagring (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault) det riktige valget. Slik laster du nøkkelen sikkert ved oppstart:
// nokkel-laster.js - Sikker nøkkelinnlasting fra miljøvariabler
'use strict';
let cachetPrivatNokkel = null;
let cachetOffentligNokkel = null;
// Cache for a unnga gjentatte fillesninger eller API-kall
function hentPrivatNokkel() {
if (cachetPrivatNokkel) return cachetPrivatNokkel;
const nokkPem = process.env.ECDSA_PRIVATE_KEY;
if (!nokkPem) throw new Error('ECDSA_PRIVATE_KEY miljøvariabel ikke satt');
// Miljøvariabler serialiserer ofte \n som bokstavelig \\n
cachetPrivatNokkel = nokkPem.replace(/\\n/g, '\n');
return cachetPrivatNokkel;
}
function hentOffentligNokkel() {
if (cachetOffentligNokkel) return cachetOffentligNokkel;
const nokkPem = process.env.ECDSA_PUBLIC_KEY;
if (!nokkPem) throw new Error('ECDSA_PUBLIC_KEY miljøvariabel ikke satt');
cachetOffentligNokkel = nokkPem.replace(/\\n/g, '\n');
return cachetOffentligNokkel;
}
// ALDRI gjor dette:
// console.log(hentPrivatNokkel()); // Logger privat nøkkel til stdout
module.exports = { hentPrivatNokkel, hentOffentligNokkel };
Legg alltid til disse linjene i .gitignore for a forhindre utilsiktet commit av private nøkler:
# .gitignore
keys/private*.pem
keys/*-encrypted.pem
*.key
.env
.env.local
.env.production
secrets/
For nøkkelrotasjon: oppretthold alltid to aktive nøkler i en overgangsperiode. Signer nye data med den nye nøkkelen, men aksepter signaturer fra begge i minst 24-48 timer. Bruk kid-feltet i JWK og JWT-headere for a identifisere hvilken nøkkel som ble brukt til en signatur. For full PKI-infrastruktur med sertifikatutstedelse og tilbakekalling, se guiden om Privat Certificate Authority med OpenSSL.
Steg 10: ECDSA med WebCrypto API
WebCrypto API er tilgjengelig i alle moderne nettlesere og i Node.js 20+ via globalThis.crypto.subtle. Det gir asynkron kryptografi med Promises og er ideelt for isomorfisk kode som kjorer i bade nettleser og Node.js.
// webcrypto-ecdsa.js - WebCrypto API i Node.js og nettlesere
'use strict';
// Node.js 20+: globalThis.crypto.subtle er tilgjengelig
// Node.js 18-19: bruk require('crypto').webcrypto.subtle
const subtle = globalThis.crypto?.subtle ?? require('crypto').webcrypto.subtle;
async function webcryptoDemo() {
// 1. Generer nøkkelpar
const nokkelpar = await subtle.generateKey(
{ name: 'ECDSA', namedCurve: 'P-256' },
true, // exportable (sett false i produksjon om mulig)
['sign', 'verify']
);
// 2. Signer data
const encoder = new TextEncoder();
const data = encoder.encode('Melding som skal signeres, 2026-06-20');
const signatur = await subtle.sign(
{ name: 'ECDSA', hash: 'SHA-256' },
nokkelpar.privateKey,
data
);
console.log('Signatur lengde:', signatur.byteLength, 'bytes'); // 64 bytes (P1363-format!)
// 3. Verifiser
const erGyldig = await subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
nokkelpar.publicKey,
signatur,
data
);
console.log('Signatur gyldig:', erGyldig); // true
// 4. Eksporter offentlig nøkkel som JWK
const jwk = await subtle.exportKey('jwk', nokkelpar.publicKey);
console.log('JWK:', JSON.stringify(jwk));
// 5. Eksporter til PEM-kompatibelt format (SPKI for offentlig, PKCS8 for privat)
const pubRaw = await subtle.exportKey('spki', nokkelpar.publicKey);
const privRaw = await subtle.exportKey('pkcs8', nokkelpar.privateKey);
console.log('SPKI bytes:', pubRaw.byteLength); // 91 bytes for P-256
console.log('PKCS8 bytes:', privRaw.byteLength); // 138 bytes for P-256
}
webcryptoDemo().catch(console.error);
Kritisk forskjell mellom Node.js crypto og WebCrypto: WebCrypto returnerer signaturen i P1363-format (to concatenerte 32-byte tall for P-256, totalt 64 bytes), mens Node.js createSign bruker DER-format (70-72 bytes med ASN.1-koding). Du kan IKKE direkte bytte signaturer mellom de to API-ene uten konvertering. Bruk et bibliotek som @noble/curves for konvertering mellom formatene om nodvendig.
Steg 11: Skriv tester for ECDSA-kode
Kryptografisk kode krever grundig testing. Test alltid bade positive tilfeller (gyldig signatur verifiserer) og negative tilfeller (ugyldig signatur avvises, manipulert melding avvises, feil nøkkel avvises). Bruk Nodes innebygde node:test-modul, tilgjengelig fra Node.js 18+.
// ecdsa.test.js - Enhetstester for ECDSA-implementasjon
'use strict';
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { generateKeyPairSync, createSign, createVerify } = require('crypto');
function lagTestnokler(kurve = 'P-256') {
return generateKeyPairSync('ec', {
namedCurve: kurve,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
}
function signer(melding, privKey) {
const s = createSign('SHA256');
s.update(melding, 'utf8');
s.end();
return s.sign(privKey);
}
function verifiser(melding, pubKey, sig) {
const v = createVerify('SHA256');
v.update(melding, 'utf8');
v.end();
return v.verify(pubKey, sig);
}
describe('ECDSA P-256', () => {
it('verifiserer gyldig signatur', () => {
const { privateKey, publicKey } = lagTestnokler('P-256');
const sig = signer('test', privateKey);
assert.equal(verifiser('test', publicKey, sig), true);
});
it('avviser manipulert melding', () => {
const { privateKey, publicKey } = lagTestnokler('P-256');
const sig = signer('original', privateKey);
assert.equal(verifiser('manipulert', publicKey, sig), false);
});
it('avviser feil offentlig nokkel', () => {
const nokler1 = lagTestnokler('P-256');
const nokler2 = lagTestnokler('P-256');
const sig = signer('test', nokler1.privateKey);
assert.equal(verifiser('test', nokler2.publicKey, sig), false);
});
it('avviser korrupt signatur', () => {
const { privateKey, publicKey } = lagTestnokler('P-256');
const sig = signer('test', privateKey);
sig[10] ^= 0xFF; // Korrumper et byte
assert.equal(verifiser('test', publicKey, sig), false);
});
});
describe('ECDSA P-384', () => {
it('verifiserer gyldig signatur', () => {
const { privateKey, publicKey } = lagTestnokler('P-384');
const s = createSign('SHA384');
s.update('p384-test', 'utf8');
s.end();
const sig = s.sign(privateKey);
const v = createVerify('SHA384');
v.update('p384-test', 'utf8');
v.end();
assert.equal(v.verify(publicKey, sig), true);
});
});
// Kjor med: node --test ecdsa.test.js
$ node --test ecdsa.test.js
▶ ECDSA P-256
✔ verifiserer gyldig signatur (3.12ms)
✔ avviser manipulert melding (0.87ms)
✔ avviser feil offentlig nokkel (0.45ms)
✔ avviser korrupt signatur (0.32ms)
▶ ECDSA P-256 (4.76ms)
▶ ECDSA P-384
✔ verifiserer gyldig signatur (0.94ms)
▶ ECDSA P-384 (1.02ms)
ℹ tests 5
ℹ pass 5
ℹ fail 0
ℹ duration_ms 52.4
Steg 12: Bygg et komplett signeringsprosjekt
La oss sette alt sammen i et komplett Express.js-API som tilbyr ECDSA-signeringstjenester via HTTP-endepunkter. Dette er et realistisk mønster for en mikrotjeneste som tilbyr kryptografisk autentisering til andre tjenester i et distribuert system.
# Installer Express
npm install express
// app.js - Komplett ECDSA Signing API med Express
'use strict';
const express = require('express');
const { generateKeyPairSync, createSign, createVerify, createPublicKey } = require('crypto');
const app = express();
app.use(express.json({ limit: '1mb' }));
// Nøkkelpar generert ved oppstart (i produksjon: last fra Secrets Manager)
const { privateKey, publicKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// Preberegn JWK for JWKS-endepunkt
const publicJwk = createPublicKey(publicKey).export({ format: 'jwk' });
// GET /.well-known/jwks.json - JWKS-endepunkt
app.get('/.well-known/jwks.json', (req, res) => {
res.json({ keys: [{ ...publicJwk, kid: 'v1', use: 'sig', alg: 'ES256' }] });
});
// GET /public-key - Hent offentlig nøkkel som PEM
app.get('/public-key', (req, res) => {
res.json({ publicKey, kurve: 'P-256', algoritme: 'ECDSA-SHA256' });
});
// POST /sign - Signer en melding
app.post('/sign', (req, res) => {
const { melding } = req.body;
if (!melding || typeof melding !== 'string') {
return res.status(400).json({ feil: 'Feltet "melding" (string) er paakrevd' });
}
if (melding.length > 10000) {
return res.status(400).json({ feil: 'Melding for lang (maks 10000 tegn)' });
}
try {
const sign = createSign('SHA256');
sign.update(melding, 'utf8');
sign.end();
const signatur = sign.sign(privateKey).toString('base64');
res.json({
melding,
signatur,
algoritme: 'ECDSA-P256-SHA256',
timestamp: new Date().toISOString()
});
} catch (err) {
res.status(500).json({ feil: 'Intern signeringsfeil' }); // Ikke eksponer intern feil
}
});
// POST /verify - Verifiser en signatur
app.post('/verify', (req, res) => {
const { melding, signatur } = req.body;
if (!melding || !signatur) {
return res.status(400).json({ feil: 'Feltene "melding" og "signatur" er paakrevde' });
}
try {
const verify = createVerify('SHA256');
verify.update(melding, 'utf8');
verify.end();
const erGyldig = verify.verify(publicKey, Buffer.from(signatur, 'base64'));
res.json({ gyldig: erGyldig, melding });
} catch {
// Fanges: malformatert base64, feil signaturstruktur
res.status(400).json({ gyldig: false, feil: 'Ugyldig signaturformat' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`ECDSA Signing API kjorer pa port ${PORT}`);
console.log('Endepunkter:');
console.log(' GET /.well-known/jwks.json');
console.log(' GET /public-key');
console.log(' POST /sign { melding: "..." }');
console.log(' POST /verify { melding: "...", signatur: "..." }');
});
Test API-et med curl:
# Start serveren
$ node app.js
ECDSA Signing API kjorer pa port 3000
# Signer en melding
$ curl -s -X POST http://localhost:3000/sign \
-H 'Content-Type: application/json' \
-d '{"melding":"Faktura #1234, belop 5000 kr"}' | tee /tmp/signert.json
{
"melding": "Faktura #1234, belop 5000 kr",
"signatur": "MEYCIQDpX2...==",
"algoritme": "ECDSA-P256-SHA256",
"timestamp": "2026-06-20T10:30:00.000Z"
}
# Verifiser signaturen
$ SIGNATUR=$(cat /tmp/signert.json | python3 -c "import json,sys; print(json.load(sys.stdin)['signatur'])")
$ curl -s -X POST http://localhost:3000/verify \
-H 'Content-Type: application/json' \
-d "{\"melding\":\"Faktura #1234, belop 5000 kr\",\"signatur\":\"$SIGNATUR\"}"
{"gyldig":true,"melding":"Faktura #1234, belop 5000 kr"}
# Hent JWKS
$ curl -s http://localhost:3000/.well-known/jwks.json | python3 -m json.tool
Vanlige fallgruver ved ECDSA i Node.js
Disse feilene gar igjen i ECDSA-implementasjoner og kan undergrave sikkerheten fullstendig eller fore til subtile feil som er vanskelige a debugge.
Fallgruve 1: Gjenbruk av k-nonce. ECDSA krever en unik, tilfeldig verdi k for hver signatur. Hvis samme k brukes to ganger for ulike meldinger, kan en angriper beregne den private nøkkelen algebraisk. Sony PlayStation 3 mistet sin kodesigneringsnøkkel i 2010 pa grunn av nettopp dette. Node.js OpenSSL bruker deterministisk k-generering i henhold til RFC 6979. Bruk ALDRI manuelle ECDSA-implementasjoner uten RFC 6979.
Fallgruve 2: Blanding av DER og P1363-format. Node.js crypto-modulen produserer DER-kodede signaturer (70-72 bytes for P-256). WebCrypto API bruker P1363-format (alltid 64 bytes for P-256). JWT-biblioteker forventer P1363. A blande disse formatene gir signaturer som alltid feiler ved verifikasjon, uten tydelig feilmelding om arsaken.
Fallgruve 3: Bruk av SHA-1 med ECDSA. SHA-1 er kryptografisk kompromittert siden kollisjonsdemonstrasjonen i 2017 (SHAttered-angrepet, dokumentert pa shattered.io-bloggen). Bruk SHA-256 med P-256, SHA-384 med P-384, SHA-512 med P-521. Spesifiser aldri bare 'SHA1' i createSign('SHA1').
Fallgruve 4: Logging av privat nøkkel. En privat nøkkel logget til console.log, en loggfil, eller en feilsporingstjeneste som Sentry eller Datadog er kompromittert. Behandle den private nøkkelen som et passord: log aldri innholdet, send den aldri over nettverket, og inkluder den aldri i feilmeldinger til klienten.
Fallgruve 5: Manglende tidsstempler i signerte data. En gyldig ECDSA-signatur forblir gyldig for alltid. Uten tidsstempel kan en angriper ta en gammel, gyldig signatur og gjenbruke den (replay-angrep). Inkluder alltid et ISO-8601-tidsstempel og en engangsverdi (nonce) i de signerte dataene, og verifiser at tidsstemplet er innenfor et akseptabelt vindu (f.eks. 5 minutter).
Fallgruve 6: Bruke secp256k1 til TLS. secp256k1 er ikke en del av TLS 1.3-kurvelisten (RFC 8446). Nettlesere og TLS-biblioteker støtter den ikke for HTTPS-tilkoblinger. Bruk P-256 for webkryptering og reserver secp256k1 for blockchain-applikasjoner.
Fallgruve 7: Bruke samme nøkkel for signering og kryptering. EC-nøkler for ECDSA (signering) og ECDH (nøkkelutveksling/kryptering) bor holdes atskilt. Gjenbruk av nøkler pa tvers av algoritmer kan fore til kryssprotokoll-angrep. Generer separate nøkkelpar for hvert formal.
8 Feilsøkingstips
1. ERR_OSSL_PEM_NO_START_LINE. PEM-filen er ødelagt eller har feil linjeskift. Kontroller at filen starter med -----BEGIN PRIVATE KEY-----. Miljøvariabler serialiserer ofte \n som \\n. Løsning: key.replace(/\\n/g, '\n').
2. ERR_INVALID_ARG_TYPE: The “key” argument must be of type string. Du sender en Buffer der en string forventes. Sikre at du laster nøkkelen som UTF-8-streng: fs.readFileSync('private.pem', 'utf8'), ikke fs.readFileSync('private.pem').
3. verify() returnerer alltid false uten feilmelding. Fire vanlige arsaker: (a) feil signaturkoding (prøv hex vs base64 vs buffer), (b) meldingen ble kodet annerledes (UTF-8 vs binary), (c) feil offentlig nøkkel brukes, (d) du blander DER og P1363-format. Legg til try/catch rundt verify.verify() for a fange implisitte feil.
4. ERR_OSSL_EC_GROUP_NOT_FOUND. Du spesifiserte et kurvenavn OpenSSL ikke kjenner. Bruk 'P-256', 'P-384', 'P-521' i Node.js-krypto-API. OpenSSL CLI aksepterer prime256v1, men Node.js-wrapperen foretrekker P-256. Sjekk tilgjengelige kurver med: node -e "require('crypto').getCurves().forEach(c => console.log(c))".
5. JWT-bibliotek aksepterer ikke ES256-nøkkel. Kontroller at du bruker P-256 for ES256, P-384 for ES384, P-521 for ES512. En P-384-nøkkel med ES256-algoritmen gir secretOrPublicKey must have a bit size of 256-feil. Mismatch mellom kurve og JWT-algoritme er en vanlig feil nar nøkkelpar er generert med andre verktøy.
6. OpenSSL-versjonsinkompatibilitet. Node.js 18 bruker OpenSSL 3.0, Node.js 16 (utgar) bruker OpenSSL 1.1.1. Noen eldre EC-nøkkeloperasjoner er fjernet i OpenSSL 3.0 (implisitt EC-parameterspesifikasjon er ikke lenger tillatt). Sjekk din OpenSSL-versjon: node -e "console.log(process.versions.openssl)". Oppgrader til Node.js 22 LTS for beste støtte.
7. ERR_OSSL_EVP_DIFFERENT_KEY_TYPES. Du forsøker a verifisere en ECDSA-signatur med en RSA-nøkkel eller omvendt. Dobbeltsjekk nøkkeltypen: createPublicKey(pem).asymmetricKeyType returnerer 'ec', 'rsa', 'ed25519' osv.
8. Signatur feiler bare i produksjon, ikke lokalt. Sjekk Node.js-versjon i begge miljøer (node --version). En vanlig arsak er at produksjonsserveren kjorer Node.js 16 (OpenSSL 1.1.1) mens lokal utvikling bruker Node.js 22 (OpenSSL 3.3). En annen vanlig arsak er linjeskift-problemer med PEM-nøkler i miljøvariabler (se punkt 1).
Ytelsesmaling og kurveoversikt
Benchmarktall for ECDSA-operasjoner pa moderne maskinvare (Apple M3 Pro, Node.js 22.x, OpenSSL 3.3) sammenlignet med RSA:
| Operasjon | Kurve/Algoritme | Tid per op. | Op./sekund | Signatur-størrelse |
|---|---|---|---|---|
| Nøkkelgenerering | P-256 | 0,12 ms | 8 333 | N/A |
| Signering | P-256 | 0,31 ms | 3 226 | 70-72 bytes (DER) |
| Verifisering | P-256 | 0,72 ms | 1 389 | N/A |
| Signering | P-384 | 0,78 ms | 1 282 | 102-104 bytes (DER) |
| Verifisering | P-384 | 1,84 ms | 543 | N/A |
| Signering | RSA-2048 (sammenligning) | 1,21 ms | 826 | 256 bytes |
| Verifisering | RSA-2048 (sammenligning) | 0,04 ms | 25 000 | N/A |
P-256-signering er 4x raskere enn RSA-2048-signering. RSA-verifisering er imidlertid raskere fordi RSA-verifisering krever en enkelt modulær eksponentieringsoperasjon med en liten offentlig eksponent (e=65537), mens ECDSA-verifisering krever to skalarmultiplikasjoner pa kurven. For applikasjoner som primært signerer (f.eks. en autentiseringstjeneste), er ECDSA det klare valget. For applikasjoner som primært verifiserer mange signaturer (f.eks. en gateway), er ytelsesavveiningen mer kompleks.
Avanserte tips for ECDSA i produksjon
Nøkkelrotasjon uten nedetid. Publiser offentlige nøkler via et JWKS-endepunkt med kid (Key ID)-felt. Inkluder kid i JWT-headere slik at klienter kan velge riktig offentlig nøkkel for verifisering. Rotasjonssekvens: (1) legg til ny nøkkel i JWKS-settet, (2) vent 24 timer til klientene cacher den nye nøkkelen, (3) begynn a signere med ny nøkkel, (4) fjern gammel nøkkel etter ytterligere 24 timer.
Signaturtidssikker sammenligning. Bruk crypto.timingSafeEqual() nar du sammenligner signaturer manuelt. Normal strengsammenligning (=== eller .equals()) er sårbar for timingside-kanal-angrep. createVerify().verify() gjor dette internt, sa du trenger bare timingSafeEqual hvis du implementerer egne sammenligninger.
Kombiner ECDSA med AES-256-GCM for ende-til-ende-autentisert kryptering. ECDSA bekrefter hvem som sendte noe, men krypterer ikke innholdet. For autentisert kryptering: signer meldingen med ECDSA (integritet og autentisitet), krypter med AES-256-GCM (konfidensialitet), send begge deler. Mottaker dekrypterer og verifiserer. Dette monsteret er kjent som “Encrypt-then-Sign”.
Kvanteresistens pa lang sikt. ECDSA er sarbar for Shorors algoritme pa tilstrekkelig kraftige kvantecomputere. NIST standardiserte i 2024 tre post-kvantum-signeringsalgoritmer: ML-DSA (CRYSTALS-Dilithium), SLH-DSA (SPHINCS+) og FN-DSA (FALCON). Node.js støtter ikke disse direkte per juni 2026, men liboqs-integrasjon er under utvikling. Planlegg migrering for systemer med 10+ ars sikkerhetshorisont.
Bruk Worker Threads for høy gjennomstromning. ECDSA-verifisering pa P-256 gir ca. 1 389 operasjoner per sekund i en enkelt Node.js-trad. For applikasjoner som trenger hoyre gjennomstrommen, fordel arbeid pa tvers av Worker Threads med worker_threads-modulen. Pa en 8-kjerners server kan dette gi 8-10x hohere total gjennomstromning.
Ofte stilte spørsmål om ECDSA i Node.js
Hva er forskjellen pa ECDSA og Ed25519?
ECDSA bruker NIST-kurver (P-256, P-384, P-521) og krever en unik tilfeldig nonce per signatur. Ed25519 bruker Edwards-kurven Curve25519 (EdDSA) og genererer signaturer deterministisk uten ekstern tilfeldighet, noe som gjor den mer resistent mot implementasjonsfeil. Ed25519-signaturer er alltid 64 bytes og er raskere enn P-256. Bruk Ed25519 for SSH-nøkler og nye protokoller der du velger fritt. Bruk P-256/ECDSA for TLS, JWT ES256 og systemer som krever bredest mulig kompatibilitet. Se vaar detaljerte sammenligning i Ed25519 vs RSA.
Kan jeg bruke ECDSA pa Node.js 16?
Node.js 16 (End of Life september 2023) støtter ECDSA via OpenSSL 1.1.1, og kodeeksemplene i denne guiden er teknisk kompatible. Men Node.js 16 mottar ikke lenger sikkerhetsoppdateringer. Oppgrader til Node.js 22 LTS (støttet til april 2027) for a motta OpenSSL 3.x-sikkerhetsoppdateringer.
Er ECDSA sikkert mot kvantecomputere?
Nei. Shorors algoritme kan knekke ECDSA-nøkler pa en tilstrekkelig kraftig kvantedatamaskin. Per juni 2026 eksisterer ingen kvantecomputer med nok stabile qubits til a true P-256 i praksis, men NIST anbefaler a planlegge overgang til post-kvantum-algoritmer for systemer med lang levetid.
Hva er DER-format og P1363-format?
Begge er malinger for ECDSA-signaturer som inneholder de to tallene r og s. DER (Distinguished Encoding Rules) er ASN.1-binærkoding og brukes av Node.js crypto-modulen. P1363 er en enklere concatenering av r og s som fast-lengde byte-arrays, brukt av WebCrypto API og JWT. For P-256 er DER 70-72 bytes variabelt, P1363 alltid 64 bytes.
Kan jeg bruke secp256k1 for vanlige webapplikasjoner?
Teknisk ja, men anbefales ikke. secp256k1 er ikke inkludert i TLS 1.3 sin kurvelist (RFC 8446), støttes ikke av nettlesere for TLS, og har ikke NIST-godkjenning. Bruk P-256 for webapplikasjoner og reserver secp256k1 for Bitcoin/Ethereum-relatert kode.
Hva skjer hvis privat nøkkel lekker?
Alle signaturer produsert med den lekkede nøkkelen ma regnes som potensielt forfalskede. Umiddelbar respons: (1) generer nytt nøkkelpar, (2) trekk tilbake relaterte sertifikater via din CA, (3) invalider alle JWT-tokens signert med gammel nøkkel (f.eks. ved a endre token-hemmeligheten eller sette en “invalidert-etter”-timestamp), (4) undersøk logger for mulig misbruk, (5) varsle paavirkede brukere og samarbeidspartnere. Se vaar guide om Privat Certificate Authority med OpenSSL for sertifikattilbakekalling.
Trenger jeg externe pakker for ECDSA i Node.js?
Nei, for grunnleggende operasjoner. Node.js innebygde crypto-modul dekker nøkkelgenerering, signering og verifisering med P-256, P-384 og P-521. For JWT-integrasjon trenger du jsonwebtoken. For konvertering mellom DER og P1363 kan @noble/curves eller elliptic-pakken vaere nyttige. For avansert PKI (sertifikatutstedelse, OCSP) er node-forge et alternativ.
Hvorfor er ECDSA-verifisering tregere enn RSA-verifisering?
RSA-verifisering er en enkelt modulær eksponentiering med den lille offentlige eksponenten e=65537, noe moderne prosessorer utforer ekstremt raskt. ECDSA-verifisering krever to skalarmultiplikasjoner pa elliptisk kurve. Disse er dyrere per operasjon, noe som gir P-256 ca. 1 389 verifiseringer/sekund mot RSA-2048 sine 25 000 verifiseringer/sekund. For signeringsintensive arbeidsbelastninger er ECDSA klart raskere.
Relatert dekning
Vil du gaa dypere inn i kryptografi og Node.js-sikkerhet? Her er relaterte guider pa shattered.io:
- Ed25519 vs RSA: 33x raskere signaturer, 128-bit sikkerhet [2026] – Detaljert sammenligning av moderne signeringsalgoritmer
- Privat Certificate Authority med OpenSSL: 12 steg, 30 min [2026] – Bygg din egen PKI-infrastruktur fra bunnen av
- OAuth 2.0 i Node.js: Sikker Autorisasjon i 12 Steg [2026] – JWT og token-basert autentisering i Express.js
- scrypt Passordhashing i Node.js: 11 Steg [2026] – Sikker passordhashing og nøkkelderivering
- SQL Injection i Node.js: 12 Steg for Sikre Databaser [2026] – Beskytt databasen din mot injeksjonsangrep




