{"id":128,"date":"2026-06-19T16:45:16","date_gmt":"2026-06-19T16:45:16","guid":{"rendered":"https:\/\/shattered.io\/se\/2026\/06\/19\/ecdh-nyckelutbyte-nodejs\/"},"modified":"2026-06-19T16:47:13","modified_gmt":"2026-06-19T16:47:13","slug":"ecdh-nyckelutbyte-nodejs","status":"publish","type":"post","link":"https:\/\/shattered.io\/se\/ecdh-nyckelutbyte-nodejs\/","title":{"rendered":"ECDH i Node.js: Elliptisk Kurva Diffie-Hellman i 12 steg [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">ECDH, Elliptic Curve Diffie-Hellman, \u00e4r det nyckelutbytesprotokoll som driver TLS 1.3, Signal-protokollet och merparten av modern krypterad kommunikation. I Node.js finns protokollet inbyggt i <code>crypto<\/code>-modulen sedan version 0.11.8. Med Node.js v22 LTS kan du implementera ett fullst\u00e4ndigt krypterat meddelandefl\u00f6de med 30 rader kod, utan externa bibliotek. Den h\u00e4r handledningen l\u00e4r dig att g\u00f6ra det p\u00e5 12 steg och 30 minuter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Elliptisk kurvkryptografi erbjuder 128-bitars s\u00e4kerhet med 256-bitars nycklar, vilket inneb\u00e4r att en P-256-nyckel ger samma skyddsniv\u00e5 som en RSA-3072-nyckel men \u00e4r 12 g\u00e5nger kortare. Det \u00e4r d\u00e4rf\u00f6r NIST SP 800-186, publicerad och uppdaterad 2023, rekommenderar P-256 och P-384 f\u00f6r statliga system. Med Sveriges Cybers\u00e4kerhetslag (2025:1506), som tr\u00e4dde i kraft den 15 januari 2026 och implementerar NIS2-direktivet, st\u00e4lls nu krav p\u00e5 att organisationer inom kritisk infrastruktur dokumenterar och s\u00e4krar sina kryptografiska val. Denna handledning ger dig den tekniska grunden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vad-ar-ecdh-och-varfor-det-spelar-roll-2026\">Vad \u00e4r ECDH och varf\u00f6r det spelar roll 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Diffie-Hellman-nyckelutbyte l\u00f6ser ett klassiskt problem: hur kan tv\u00e5 parter komma \u00f6verens om en hemlig nyckel via en os\u00e4ker kanal utan att nyckelns v\u00e4rde n\u00e5gonsin s\u00e4nds i klartext? ECDH l\u00f6ser samma problem med elliptiska kurvor i st\u00e4llet f\u00f6r klassisk modul\u00e4r aritmetik. Resultatet \u00e4r dramatiskt kortare nycklar och snabbare ber\u00e4kningar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I praktiken anv\u00e4nds ECDH i dessa sammanhang:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>TLS 1.3<\/strong>: Alla TLS 1.3-handskakningar kr\u00e4ver ephemeral ECDHE (Elliptic Curve Diffie-Hellman Ephemeral). Det gamla RSA-nyckelutbytet \u00e4r borttaget ur specifikationen.<\/li>\n\n\n\n<li><strong>Signal-protokollet<\/strong>: X3DH, Extended Triple Diffie-Hellman, bygger p\u00e5 elliptiska kurvor och anv\u00e4nds i Signal, WhatsApp och iMessage.<\/li>\n\n\n\n<li><strong>SSH<\/strong>: Modern OpenSSH v\u00e4ljer som standard curve25519-sha256 f\u00f6r nyckelutbyte.<\/li>\n\n\n\n<li><strong>WireGuard<\/strong>: Protokollet anv\u00e4nder Curve25519 uteslutande f\u00f6r nyckelutbyte.<\/li>\n\n\n\n<li><strong>Hybridkryptering<\/strong>: ECDH genererar en delad hemlighet som sedan krypterar ett symmetriskt AES-256-nyckel, vilket kombinerar asymmetrisk och symmetrisk kryptografi.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Skillnaden mot RSA-kryptering \u00e4r fundamental: RSA krypterar data direkt med den asymmetriska nyckeln. ECDH genererar aldrig ett chiffer, det skapar en delad hemlighet som sedan driver symmetrisk kryptering. Det g\u00f6r ECDH till ett <em>nyckelutbytesprotokoll<\/em>, inte ett krypteringssystem.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"hur-elliptisk-kurvkryptografi-fungerar-matematiskt\">Hur elliptisk kurvkryptografi fungerar matematiskt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En elliptisk kurva \u00e4r en m\u00e4ngd punkter som uppfyller ekvationen y\u00b2 = x\u00b3 + ax + b (Weierstrass-form) \u00f6ver ett \u00e4ndligt f\u00e4lt. Kurvan P-256, vars tekniska namn \u00e4r prime256v1 och secp256r1, opererar \u00f6ver ett primtal med 256 bitar och inneh\u00e5ller ungef\u00e4r 2^256 punkter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ECDH-nyckelutbytet fungerar i fyra steg:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Alice och Bob kommer \u00f6verens om en kurva och en offentlig baspunkt G (detta \u00e4r del av kurvoparametrarna, inte hemligt).<\/li>\n\n\n\n<li>Alice v\u00e4ljer ett slumpm\u00e4ssigt heltal a (sin privata nyckel) och ber\u00e4knar A = a \u00b7 G (skal\u00e4rprodukt p\u00e5 kurvan). A \u00e4r Alices publika nyckel.<\/li>\n\n\n\n<li>Bob v\u00e4ljer ett slumpm\u00e4ssigt heltal b och ber\u00e4knar B = b \u00b7 G. B \u00e4r Bobs publika nyckel.<\/li>\n\n\n\n<li>Alice ber\u00e4knar S = a \u00b7 B = a \u00b7 b \u00b7 G. Bob ber\u00e4knar S = b \u00b7 A = b \u00b7 a \u00b7 G. B\u00e5da f\u00e5r samma punkt S, den delade hemligheten, utan att a eller b l\u00e4mnat respektive part.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00e4kerheten vilar p\u00e5 det diskreta logaritmproblemet p\u00e5 elliptiska kurvor (ECDLP): givet G och A = a \u00b7 G \u00e4r det ber\u00e4kningsm\u00e4ssigt og\u00f6rligt att hitta a. Det b\u00e4sta k\u00e4nda klassiska angreppet tar 2^128 operationer mot P-256, vilket \u00e4r s\u00e4kert i all \u00f6versk\u00e5dlig framtid mot konventionella datorer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"forutsattningar-och-verktyg\">F\u00f6ruts\u00e4ttningar och verktyg<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Kontrollera att du har dessa versioner installerade innan du b\u00f6rjar:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Verktyg<\/th><th>Minimiversion<\/th><th>Rekommenderad<\/th><th>Syfte<\/th><\/tr><\/thead><tbody><tr><td>Node.js<\/td><td>v18.0.0<\/td><td>v22.16.0 LTS<\/td><td>Runtime med inbyggd crypto-modul<\/td><\/tr><tr><td>npm<\/td><td>8.x<\/td><td>10.x<\/td><td>Pakethantering<\/td><\/tr><tr><td>OpenSSL<\/td><td>1.1.1<\/td><td>3.x<\/td><td>Underliggande kryptografibibliotek<\/td><\/tr><tr><td>TypeScript (valfritt)<\/td><td>5.0<\/td><td>5.5<\/td><td>Typs\u00e4kerhet vid produktion<\/td><\/tr><tr><td>Operativsystem<\/td><td>Linux, macOS, Windows<\/td><td>Ubuntu 22.04 LTS<\/td><td>Alla plattformar st\u00f6ds<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Verifiera din Node.js-version och OpenSSL-konfiguration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node --version\n# v22.16.0\n\nnode -e \"const crypto = require('crypto'); console.log(crypto.getCurves().filter(c => c.includes('256') || c.includes('384') || c.includes('521')))\"\n# [ 'prime256v1', 'secp384r1', 'secp521r1', ... ]\n\nnode -e \"console.log(process.versions.openssl)\"\n# 3.0.15 (eller liknande)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Inga externa npm-paket kr\u00e4vs f\u00f6r de f\u00f6rsta 10 stegen. Node.js crypto-modul t\u00e4cker all n\u00f6dv\u00e4ndig funktionalitet. I steg 11 visar jag hur du l\u00e4gger till typberedning med TypeScript.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-1-2-projektstruktur-och-grundlaggande-nyckelutbyte\">Steg 1-2: Projektstruktur och grundl\u00e4ggande nyckelutbyte<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"steg-1-skapa-projektstruktur\">Steg 1: Skapa projektstruktur<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir ecdh-demo && cd ecdh-demo\nnpm init -y\nmkdir -p src\/{alice,bob,shared}\ntouch src\/basic-exchange.js src\/hkdf-derivation.js src\/hybrid-encryption.js<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"steg-2-grundlaggande-ecdh-nyckelutbyte\">Steg 2: Grundl\u00e4ggande ECDH-nyckelutbyte<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Det enklaste m\u00f6jliga ECDH-utbytet i Node.js ser ut s\u00e5 h\u00e4r. Alice och Bob genererar varsin nyckelpar, utbyter publika nycklar och ber\u00e4knar den delade hemligheten oberoende av varandra:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/basic-exchange.js\nconst crypto = require('crypto');\n\n\/\/ Alice genererar sitt nyckelpar\nconst alice = crypto.createECDH('prime256v1');\nconst alicePublicKey = alice.generateKeys();\n\n\/\/ Bob genererar sitt nyckelpar\nconst bob = crypto.createECDH('prime256v1');\nconst bobPublicKey = bob.generateKeys();\n\n\/\/ B\u00e5da ber\u00e4knar den delade hemligheten med motpartens publika nyckel\nconst aliceSecret = alice.computeSecret(bobPublicKey);\nconst bobSecret = bob.computeSecret(alicePublicKey);\n\n\/\/ Verifiera att b\u00e5da fick samma hemlighet\nconsole.log('Alices hemlighet:  ', aliceSecret.toString('hex'));\nconsole.log('Bobs hemlighet:    ', bobSecret.toString('hex'));\nconsole.log('Matchar?           ', aliceSecret.equals(bobSecret));\n\n\/\/ F\u00f6rv\u00e4ntad utdata:\n\/\/ Alices hemlighet:   a3f8... (32 byte hex, varierar per k\u00f6rning)\n\/\/ Bobs hemlighet:     a3f8... (identisk)\n\/\/ Matchar?            true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Viktigt: <code>computeSecret()<\/code> returnerar x-koordinaten f\u00f6r den delade punkten S. Den \u00e4r 32 byte f\u00f6r P-256, 48 byte f\u00f6r P-384 och 66 byte f\u00f6r P-521. Anv\u00e4nd aldrig denna r\u00e5a hemlighet som en symmetrisk nyckel direkt, utan k\u00f6r den f\u00f6rst genom en nyckelderivat-funktion (se steg 5).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-3-valja-ratt-ec-kurva\">Steg 3: V\u00e4lja r\u00e4tt EC-kurva<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Node.js och OpenSSL st\u00f6der dussintals kurvor, men bara ett f\u00e5tal \u00e4r l\u00e4mpliga f\u00f6r produktionsbruk. NIST SP 800-186 rekommenderar P-256, P-384 och P-521 f\u00f6r statliga till\u00e4mpningar. RFC 7748 (publicerad 2016, fortfarande giltig 2026) definierar X25519 och X448 som modernare alternativ med b\u00e4ttre motst\u00e5ndskraft mot implementeringsfel.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Kurva (tekniskt namn)<\/th><th>Nyckelstorlek<\/th><th>S\u00e4kerhetsniv\u00e5<\/th><th>Prestanda<\/th><th>NIST-godk\u00e4nd<\/th><th>Rekommendation 2026<\/th><\/tr><\/thead><tbody><tr><td>P-256 (prime256v1)<\/td><td>256 bit<\/td><td>128 bit<\/td><td>Snabb<\/td><td>Ja (SP 800-186)<\/td><td>Standard, bred kompatibilitet<\/td><\/tr><tr><td>P-384 (secp384r1)<\/td><td>384 bit<\/td><td>192 bit<\/td><td>M\u00e5ttlig<\/td><td>Ja (SP 800-186)<\/td><td>F\u00f6r k\u00e4nsliga data och NIS2-krav<\/td><\/tr><tr><td>P-521 (secp521r1)<\/td><td>521 bit<\/td><td>256 bit<\/td><td>L\u00e5ngsam<\/td><td>Ja (SP 800-186)<\/td><td>Topphemliga system, s\u00e4llan n\u00f6dv\u00e4ndigt<\/td><\/tr><tr><td>X25519 (Curve25519)<\/td><td>255 bit<\/td><td>128 bit<\/td><td>Mycket snabb<\/td><td>Nej (men NIST-godk\u00e4nt i SP 800-186 som X25519)<\/td><td>Rekommenderas f\u00f6r nya system<\/td><\/tr><tr><td>X448 (Curve448)<\/td><td>448 bit<\/td><td>224 bit<\/td><td>Snabb<\/td><td>Ja (SP 800-186)<\/td><td>H\u00f6g s\u00e4kerhet utan P-384:s overhead<\/td><\/tr><tr><td>secp256k1<\/td><td>256 bit<\/td><td>128 bit<\/td><td>Snabb<\/td><td>Nej<\/td><td>Undvik utanf\u00f6r blockchain-kontext<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Tumregel f\u00f6r 2026: V\u00e4lj P-256 f\u00f6r kompatibilitet med \u00e4ldre system och X25519 f\u00f6r moderna applikationer. P-384 \u00e4r r\u00e4tt val om din organisation omfattas av NIS2 och bearbetar k\u00e4nsliga personuppgifter eller kritisk infrastrukturdata. Undvik P-521 om inte regelverket specifikt kr\u00e4ver 256-bitars s\u00e4kerhet.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Visa alla tillg\u00e4ngliga kurvor i din Node.js-installation\nconst crypto = require('crypto');\nconst kurvor = crypto.getCurves();\nconsole.log('Antal tillg\u00e4ngliga kurvor:', kurvor.length);\n\/\/ Antal tillg\u00e4ngliga kurvor: 100+ (beroende p\u00e5 OpenSSL-version)\n\n\/\/ Kontrollera att en specifik kurva finns\nconst st\u00f6djer384 = kurvor.includes('secp384r1');\nconsole.log('St\u00f6der P-384:', st\u00f6djer384); \/\/ true<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-4-nyckelformat-export-och-serialisering\">Steg 4: Nyckelformat, export och serialisering<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I verkliga system skickar Alice och Bob sina publika nycklar via ett API eller ett n\u00e4tverksprotokoll. Det kr\u00e4ver serialisering till ett b\u00e4rbart format. Node.js st\u00f6der tre format f\u00f6r ECDH-publika nycklar:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>uncompressed<\/strong> (standard): 65 byte f\u00f6r P-256. B\u00f6rjar med byte <code>0x04<\/code> f\u00f6ljt av x- och y-koordinaterna. Mest kompatibelt.<\/li>\n\n\n\n<li><strong>compressed<\/strong>: 33 byte f\u00f6r P-256. B\u00f6rjar med <code>0x02<\/code> eller <code>0x03<\/code>. Halverar nyckelns storlek men kr\u00e4ver att motparten st\u00f6der okomprimerat format.<\/li>\n\n\n\n<li><strong>hybrid<\/strong>: 65 byte, som uncompressed men med komprimerad paritetsinformation i det f\u00f6rsta bytet. S\u00e4llsynt och undviks i modern kod.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/key-formats.js\nconst crypto = require('crypto');\n\nconst alice = crypto.createECDH('prime256v1');\nalice.generateKeys();\n\n\/\/ H\u00e4mta publik nyckel i olika format\nconst okomprimerad = alice.getPublicKey('hex', 'uncompressed');\nconst komprimerad = alice.getPublicKey('hex', 'compressed');\nconst base64Nyckel = alice.getPublicKey('base64', 'uncompressed');\n\nconsole.log('Okomprimerad (hex):  ', okomprimerad.length \/ 2, 'byte');\n\/\/ Okomprimerad (hex):   65 byte\n\nconsole.log('Komprimerad (hex):   ', komprimerad.length \/ 2, 'byte');\n\/\/ Komprimerad (hex):    33 byte\n\nconsole.log('Privat nyckel (hex): ', alice.getPrivateKey('hex').length \/ 2, 'byte');\n\/\/ Privat nyckel (hex):  32 byte\n\n\/\/ Spara och ladda om privat nyckel\nconst sparadPrivatNyckel = alice.getPrivateKey();\nconst aliceKlonad = crypto.createECDH('prime256v1');\naliceKlonad.setPrivateKey(sparadPrivatNyckel);\n\n\/\/ Publika nyckeln \u00e5terskapas automatiskt fr\u00e5n den privata\nconsole.log('Nycklar matchar:', alice.getPublicKey('hex') === aliceKlonad.getPublicKey('hex'));\n\/\/ Nycklar matchar: true\n\n\/\/ OBS: Anv\u00e4nd ALDRIG ecdh.setPublicKey() direkt - metoden \u00e4r f\u00f6r\u00e5ldrad\n\/\/ och kan l\u00e4mna nyckelobjektet i ett inkonsekvent tillst\u00e5nd<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Vid serialisering f\u00f6r API-kommunikation, anv\u00e4nd Base64url (utan utfyllnadstecken) f\u00f6r att undvika teckenproblem i URL-parametrar och JSON:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Konvertera publik nyckel till Base64url f\u00f6r API-transport\nfunction nyckelTillBase64url(ecdh) {\n  const bytes = ecdh.getPublicKey();\n  return bytes.toString('base64').replace(\/\\+\/g, '-').replace(\/\\\/\/g, '_').replace(\/=\/g, '');\n}\n\nfunction base64urlTillBuffer(str) {\n  const base64 = str.replace(\/-\/g, '+').replace(\/_\/g, '\/');\n  return Buffer.from(base64, 'base64');\n}\n\nconst alice = crypto.createECDH('prime256v1');\nalice.generateKeys();\nconst nyckelStr = nyckelTillBase64url(alice);\nconsole.log('API-format:', nyckelStr);\n\/\/ API-format: BIaKtF... (88 tecken, ingen = utfyllnad)<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-5-saker-nyckelderivering-med-hkdf\">Steg 5: S\u00e4ker nyckelderivering med HKDF<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Den r\u00e5a delade hemligheten fr\u00e5n <code>computeSecret()<\/code> \u00e4r inte en bra symmetrisk nyckel av tre sk\u00e4l: den \u00e4r inte garanterat j\u00e4mnt f\u00f6rdelad, den inneh\u00e5ller ingen kontextinformation (risk f\u00f6r nyckel\u00e5teranv\u00e4ndning), och alla bitar har inte lika stor entropi. HKDF, definierat i RFC 5869, l\u00f6ser detta.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Node.js v22 har inbyggt st\u00f6d f\u00f6r HKDF via <code>crypto.hkdfSync()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/hkdf-derivation.js\nconst crypto = require('crypto');\n\nfunction h\u00e4rledException(deladHemlighet, salt, info, l\u00e4ngd = 32) {\n  \/\/ HKDF med SHA-256\n  \/\/ salt: slumpm\u00e4ssigt v\u00e4rde (minst 32 byte), kan delas \u00f6ppet\n  \/\/ info: kontextbeskrivning, t.ex. 'chat-session-2026'\n  \/\/ l\u00e4ngd: \u00f6nskad nyckelstorlek i byte\n  return crypto.hkdfSync(\n    'sha256',\n    deladHemlighet,\n    salt,\n    Buffer.from(info, 'utf8'),\n    l\u00e4ngd\n  );\n}\n\n\/\/ Fullst\u00e4ndigt exempel\nconst alice = crypto.createECDH('prime256v1');\nconst bob   = crypto.createECDH('prime256v1');\nconst alicePub = alice.generateKeys();\nconst bobPub   = bob.generateKeys();\n\nconst r\u00e5Hemlighet = alice.computeSecret(bobPub);\n\n\/\/ Salt b\u00f6r vara slumpm\u00e4ssigt och delas via nyckelutbytesmeddelandet\nconst salt = crypto.randomBytes(32);\n\nconst sessionNyckel = h\u00e4rledException(r\u00e5Hemlighet, salt, 'ecdh-demo\/session\/v1', 32);\nconst macNyckel     = h\u00e4rledException(r\u00e5Hemlighet, salt, 'ecdh-demo\/mac\/v1', 32);\n\nconsole.log('Sessionsnyckel (hex):', Buffer.from(sessionNyckel).toString('hex'));\nconsole.log('MAC-nyckel (hex):    ', Buffer.from(macNyckel).toString('hex'));\nconsole.log('Samma k\u00e4lla, olika nycklar:', !Buffer.from(sessionNyckel).equals(Buffer.from(macNyckel)));\n\/\/ Samma k\u00e4lla, olika nycklar: true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Info-parametern b\u00f6r vara specifik f\u00f6r din applikation och session. Den hindrar att nycklar l\u00e4cker mellan olika sammanhang om koden \u00e5teranv\u00e4nds. Bra v\u00e4rden: <code>'myapp\/v1\/alice-to-bob\/encryption'<\/code>. D\u00e5liga v\u00e4rden: <code>'key'<\/code> eller tom str\u00e4ng.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-6-7-hybridkryptering-med-aes-256-gcm\">Steg 6-7: Hybridkryptering med AES-256-GCM<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"steg-6-kryptering\">Steg 6: Kryptering<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Hybridkryptering kombinerar ECDH (f\u00f6r nyckelutbyte) med AES-256-GCM (f\u00f6r effektiv symmetrisk kryptering). Det \u00e4r exakt det m\u00f6nster som TLS 1.3 och moderna krypterade meddelandeapplikationer anv\u00e4nder. H\u00e4r implementeras ett fullst\u00e4ndigt hybridkrypteringssystem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/hybrid-encryption.js\nconst crypto = require('crypto');\n\n\/\/ --- Hj\u00e4lpfunktioner ---\n\nfunction h\u00e4rledException(deladHemlighet, salt, info) {\n  return Buffer.from(\n    crypto.hkdfSync('sha256', deladHemlighet, salt, Buffer.from(info), 32)\n  );\n}\n\nfunction kryptera(meddelande, mottagarPublikNyckel) {\n  \/\/ 1. Skapa ett tillf\u00e4lligt (ephemeral) nyckelpar f\u00f6r detta meddelande\n  const avs\u00e4ndare = crypto.createECDH('prime256v1');\n  const avs\u00e4ndarePub = avs\u00e4ndare.generateKeys();\n\n  \/\/ 2. Ber\u00e4kna delad hemlighet\n  const deladHemlighet = avs\u00e4ndare.computeSecret(mottagarPublikNyckel);\n\n  \/\/ 3. H\u00e4rled symmetrisk nyckel via HKDF\n  const salt = crypto.randomBytes(32);\n  const symmetriskNyckel = h\u00e4rledException(\n    deladHemlighet,\n    salt,\n    'hybrid-crypto\/aes256gcm\/v1'\n  );\n\n  \/\/ 4. Kryptera med AES-256-GCM\n  const iv = crypto.randomBytes(12); \/\/ 96 bit IV f\u00f6r GCM\n  const cipher = crypto.createCipheriv('aes-256-gcm', symmetriskNyckel, iv);\n  const krypterat = Buffer.concat([\n    cipher.update(meddelande, 'utf8'),\n    cipher.final()\n  ]);\n  const autentiseringstagg = cipher.getAuthTag(); \/\/ 16 byte GCM-tagg\n\n  \/\/ 5. Paketera allt tillsammans\n  return {\n    avs\u00e4ndarePub: avs\u00e4ndarePub.toString('base64'),\n    salt:            salt.toString('base64'),\n    iv:              iv.toString('base64'),\n    krypterat:       krypterat.toString('base64'),\n    tagg:            autentiseringstagg.toString('base64')\n  };\n}\n\n\/\/ Exempel\nconst mottagare = crypto.createECDH('prime256v1');\nconst mottagarPub = mottagare.generateKeys();\n\nconst paket = kryptera('Hemligt meddelande till Bob', mottagarPub);\nconsole.log('Krypterat paket:', JSON.stringify(paket, null, 2));<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"steg-7-dekryptering\">Steg 7: Dekryptering<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>function dekryptera(paket, mottagareECDH) {\n  \/\/ 1. L\u00e4s in avs\u00e4ndarens tillf\u00e4lliga publika nyckel\n  const avs\u00e4ndarePub = Buffer.from(paket.avs\u00e4ndarePub, 'base64');\n  const salt = Buffer.from(paket.salt, 'base64');\n\n  \/\/ 2. Ber\u00e4kna samma delade hemlighet (mottagaren + avs\u00e4ndarens pub)\n  const deladHemlighet = mottagareECDH.computeSecret(avs\u00e4ndarePub);\n\n  \/\/ 3. H\u00e4rled samma symmetriska nyckel\n  const symmetriskNyckel = h\u00e4rledException(\n    deladHemlighet,\n    salt,\n    'hybrid-crypto\/aes256gcm\/v1'\n  );\n\n  \/\/ 4. Dekryptera med AES-256-GCM (autentiseringen sker automatiskt)\n  const iv          = Buffer.from(paket.iv, 'base64');\n  const krypterat   = Buffer.from(paket.krypterat, 'base64');\n  const tagg        = Buffer.from(paket.tagg, 'base64');\n\n  const decipher = crypto.createDecipheriv('aes-256-gcm', symmetriskNyckel, iv);\n  decipher.setAuthTag(tagg);\n\n  try {\n    const klartext = Buffer.concat([\n      decipher.update(krypterat),\n      decipher.final()\n    ]);\n    return klartext.toString('utf8');\n  } catch {\n    \/\/ Autentiseringstaggen st\u00e4mde inte - meddelandet \u00e4r manipulerat\n    throw new Error('Autentisering misslyckades: meddelandet \u00e4r skadat eller manipulerat');\n  }\n}\n\n\/\/ Komplett test\nconst dekrypterat = dekryptera(paket, mottagare);\nconsole.log('Dekrypterat:', dekrypterat);\n\/\/ Dekrypterat: Hemligt meddelande till Bob<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GCM-l\u00e4get ger b\u00e5de kryptering och autentisering. Om en angripare \u00e4ndrar ett enda byte i det krypterade meddelandet kastar <code>decipher.final()<\/code> ett fel. Det g\u00f6r att du aldrig riskerar att bearbeta manipulerad data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-8-x25519-och-modern-nyckelutbyte\">Steg 8: X25519 och modern nyckelutbyte<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">X25519 \u00e4r det moderna alternativet till P-256 f\u00f6r ECDH. Det anv\u00e4nder Curve25519, designad 2005 av kryptografen Daniel J. Bernstein, och har tre f\u00f6rdelar j\u00e4mf\u00f6rt med NIST-kurvorna: enklare och mer implementeringss\u00e4ker matematik, inget beroende av slumpm\u00e4ssiga konstanter som NIST inte fullst\u00e4ndigt motiverat, och snabbare ber\u00e4kningar p\u00e5 modern h\u00e5rdvara.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I Node.js hanteras X25519 via <code>generateKeyPair<\/code> och <code>diffieHellman<\/code> (eller WebCrypto API), inte via den \u00e4ldre <code>createECDH<\/code>-klassen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/x25519-exchange.js\nconst crypto = require('crypto');\n\n\/\/ Generera X25519-nyckelpar (asynkron version)\nasync function x25519Utbyte() {\n  \/\/ Alice\n  const { privateKey: alicePriv, publicKey: alicePub } =\n    await crypto.generateKeyPair('x25519', {\n      publicKeyEncoding:  { type: 'spki',  format: 'der' },\n      privateKeyEncoding: { type: 'pkcs8', format: 'der' }\n    });\n\n  \/\/ Bob\n  const { privateKey: bobPriv, publicKey: bobPub } =\n    await crypto.generateKeyPair('x25519', {\n      publicKeyEncoding:  { type: 'spki',  format: 'der' },\n      privateKeyEncoding: { type: 'pkcs8', format: 'der' }\n    });\n\n  \/\/ Ber\u00e4kna delade hemligheter (DiffieHellman-funktion)\n  const aliceHemlighet = crypto.diffieHellman({\n    privateKey: crypto.createPrivateKey({ key: alicePriv, format: 'der', type: 'pkcs8' }),\n    publicKey:  crypto.createPublicKey({  key: bobPub,   format: 'der', type: 'spki'  })\n  });\n\n  const bobHemlighet = crypto.diffieHellman({\n    privateKey: crypto.createPrivateKey({ key: bobPriv, format: 'der', type: 'pkcs8' }),\n    publicKey:  crypto.createPublicKey({  key: alicePub, format: 'der', type: 'spki'  })\n  });\n\n  console.log('X25519 - Matchar:', aliceHemlighet.equals(bobHemlighet));\n  \/\/ X25519 - Matchar: true\n  console.log('Delad hemlighet:', aliceHemlighet.toString('hex'));\n  \/\/ Delad hemlighet: 32 byte hex\n\n  return { aliceHemlighet, bobHemlighet };\n}\n\nx25519Utbyte().catch(console.error);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">X25519-nycklar \u00e4r alltid 32 byte. Det finns ingen komprimerad kontra okomprimerad variant och inga svaga punkter (cofactor-angrepp) att oroa sig f\u00f6r. Det \u00e4r dessa egenskaper som g\u00f6r kurvan till standardvalet i moderna protokoll som TLS 1.3, WireGuard och Signal.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-9-ecdh-i-en-fullstandig-express-js-applikation\">Steg 9: ECDH i en fullst\u00e4ndig Express.js-applikation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Att integrera ECDH i ett REST API kr\u00e4ver att man hanterar nyckelutbyte som ett fl\u00f6de i flera steg. H\u00e4r \u00e4r ett minimalt men komplett exempel med Express.js som implementerar ett krypterat meddelandefl\u00f6de:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/server.js - Krypterad meddelandeserver med ECDH\nconst express = require('express');\nconst crypto  = require('crypto');\n\nconst app = express();\napp.use(express.json());\n\n\/\/ Servernsyckelpar (skapas vid start, roteras i produktion)\nconst server = crypto.createECDH('prime256v1');\nconst serverPub = server.generateKeys('base64', 'uncompressed');\n\n\/\/ In-memory sessioner (anv\u00e4nd Redis i produktion)\nconst sessioner = new Map();\n\n\/\/ Steg 1: Klienten h\u00e4mtar serverns publika nyckel och skapar en session\napp.get('\/handshake', (req, res) => {\n  const sessionId = crypto.randomUUID();\n  sessioner.set(sessionId, { skapad: Date.now() });\n  res.json({ sessionId, serverPublikNyckel: serverPub });\n});\n\n\/\/ Steg 2: Klienten skickar sin publika nyckel och krypterat meddelande\napp.post('\/message', (req, res) => {\n  const { sessionId, klientPub, krypterat, iv, salt, tagg } = req.body;\n\n  if (!sessioner.has(sessionId)) {\n    return res.status(401).json({ fel: 'Ogiltig session' });\n  }\n\n  try {\n    \/\/ Ber\u00e4kna delad hemlighet\n    const klientPubBuf  = Buffer.from(klientPub, 'base64');\n    const deladHemlighet = server.computeSecret(klientPubBuf);\n\n    \/\/ H\u00e4rled nyckel\n    const symmetriskNyckel = Buffer.from(\n      crypto.hkdfSync(\n        'sha256',\n        deladHemlighet,\n        Buffer.from(salt, 'base64'),\n        Buffer.from('express-ecdh-demo\/v1'),\n        32\n      )\n    );\n\n    \/\/ Dekryptera\n    const decipher = crypto.createDecipheriv(\n      'aes-256-gcm',\n      symmetriskNyckel,\n      Buffer.from(iv, 'base64')\n    );\n    decipher.setAuthTag(Buffer.from(tagg, 'base64'));\n\n    const klartext = Buffer.concat([\n      decipher.update(Buffer.from(krypterat, 'base64')),\n      decipher.final()\n    ]).toString('utf8');\n\n    sessioner.delete(sessionId); \/\/ Varje session anv\u00e4nds bara en g\u00e5ng\n    res.json({ status: 'ok', mottaget: klartext });\n\n  } catch (err) {\n    res.status(400).json({ fel: 'Dekryptering misslyckades' });\n  }\n});\n\napp.listen(3000, () => console.log('Server p\u00e5 port 3000'));\nmodule.exports = app;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Servern lagrar sin privata ECDH-nyckel i minnet och exponerar bara den publika nyckeln. Varje klientmeddelande inneh\u00e5ller ett tillf\u00e4lligt nyckelpar, vilket garanterar forward secrecy: om servernyckeln komprometteras kan gamla meddelanden inte dekrypteras i efterhand.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-10-11-nyckelrotation-och-prestandaoptimering\">Steg 10-11: Nyckelrotation och prestandaoptimering<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"steg-10-nyckelrotation\">Steg 10: Nyckelrotation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NIST rekommenderar att kryptografiska nycklar roteras regelbundet. F\u00f6r ECDH-servernycklar \u00e4r en rimlig rotationsperiod 24 timmar i h\u00f6g-trafiksystem och 7 dagar i l\u00e5gtrafikssystem. Under en rotation m\u00e5ste du hantera en \u00f6verg\u00e5ngsperiod d\u00e4r b\u00e5de den gamla och nya nyckeln accepteras:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/key-rotation.js\nconst crypto = require('crypto');\n\nclass ECDHNyckelRotering {\n  constructor(kurva = 'prime256v1', livstidMs = 24 * 60 * 60 * 1000) {\n    this.kurva = kurva;\n    this.livstid = livstidMs;\n    this.nuvarandeNyckel = null;\n    this.f\u00f6rraaNyckel = null;\n    this.rotera(); \/\/ Skapa initial nyckel\n  }\n\n  rotera() {\n    this.f\u00f6rraaNyckel = this.nuvarandeNyckel;\n    const ecdh = crypto.createECDH(this.kurva);\n    ecdh.generateKeys();\n    this.nuvarandeNyckel = {\n      ecdh,\n      id: crypto.randomBytes(8).toString('hex'),\n      skapad: Date.now(),\n      publik: ecdh.getPublicKey('base64', 'uncompressed')\n    };\n    \/\/ Schemal\u00e4gg n\u00e4sta rotation\n    setTimeout(() => this.rotera(), this.livstid).unref();\n    console.log(`Nyckel roterad. Nytt nyckel-ID: ${this.nuvarandeNyckel.id}`);\n  }\n\n  h\u00e4mtaNyckelInfo() {\n    return {\n      id:     this.nuvarandeNyckel.id,\n      publik: this.nuvarandeNyckel.publik,\n      skapad: new Date(this.nuvarandeNyckel.skapad).toISOString()\n    };\n  }\n\n  ber\u00e4knaHemlighet(klientPub, nyckelId) {\n    \/\/ Kontrollera aktuell nyckel\n    if (this.nuvarandeNyckel.id === nyckelId) {\n      return this.nuvarandeNyckel.ecdh.computeSecret(Buffer.from(klientPub, 'base64'));\n    }\n    \/\/ Kontrollera f\u00f6reg\u00e5ende nyckel (under rotationsperiod)\n    if (this.f\u00f6rraaNyckel && this.f\u00f6rraaNyckel.id === nyckelId) {\n      return this.f\u00f6rraaNyckel.ecdh.computeSecret(Buffer.from(klientPub, 'base64'));\n    }\n    throw new Error(`Ok\u00e4nt nyckel-ID: ${nyckelId}`);\n  }\n}\n\nconst rotator = new ECDHNyckelRotering('prime256v1', 30_000); \/\/ 30 sek f\u00f6r test\nconsole.log('Aktuell nyckelinfo:', rotator.h\u00e4mtaNyckelInfo());<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"steg-11-prestandajamforelse\">Steg 11: Prestandaj\u00e4mf\u00f6relse<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">ECDH \u00e4r avsev\u00e4rt snabbare \u00e4n RSA f\u00f6r nyckelutbyte, men det \u00e4r nyttigt att k\u00e4nna till de faktiska talen. Dessa j\u00e4mf\u00f6relser baseras p\u00e5 benchmarks fr\u00e5n Node.js crypto-modulens dokumentation och OpenSSL-prestanda p\u00e5 typisk serverh\u00e5rdvara 2026 (AMD EPYC 9454, 2.75 GHz):<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Algoritm<\/th><th>Nyckelgenering (op\/sek)<\/th><th>Nyckelutbyte (op\/sek)<\/th><th>Nyckelstorlek (byte)<\/th><th>S\u00e4kerhetsniv\u00e5<\/th><\/tr><\/thead><tbody><tr><td>ECDH P-256<\/td><td>~15 000<\/td><td>~7 500<\/td><td>65 (publik)<\/td><td>128 bit<\/td><\/tr><tr><td>ECDH P-384<\/td><td>~5 000<\/td><td>~2 500<\/td><td>97 (publik)<\/td><td>192 bit<\/td><\/tr><tr><td>ECDH P-521<\/td><td>~2 000<\/td><td>~1 000<\/td><td>133 (publik)<\/td><td>256 bit<\/td><\/tr><tr><td>X25519<\/td><td>~25 000<\/td><td>~12 500<\/td><td>32 (publik)<\/td><td>128 bit<\/td><\/tr><tr><td>RSA-2048<\/td><td>~2 000<\/td><td>~50 000 (pub) \/ ~700 (priv)<\/td><td>256 (publik)<\/td><td>112 bit<\/td><\/tr><tr><td>RSA-3072<\/td><td>~500<\/td><td>~25 000 (pub) \/ ~200 (priv)<\/td><td>384 (publik)<\/td><td>128 bit<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Observera: RSA-dekryptering (privat nyckeloperation) \u00e4r drastiskt l\u00e5ngsammare \u00e4n ECDH. Det f\u00f6rklarar varf\u00f6r TLS 1.3 \u00f6vergav RSA-nyckelutbyte. X25519 genererar dessutom nycklar 30 g\u00e5nger snabbare \u00e4n RSA-3072 vid ekvivalent s\u00e4kerhetsniv\u00e5.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-12-nis2-compliance-och-produktionssakerhet\">Steg 12: NIS2-compliance och produktionss\u00e4kerhet<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sveriges Cybers\u00e4kerhetslag (2025:1506), som implementerar EU:s NIS2-direktiv, tr\u00e4dde i kraft den 15 januari 2026. F\u00f6reskrifter om s\u00e4kerhets\u00e5tg\u00e4rder och utbildning b\u00f6rjade g\u00e4lla i april 2026, och f\u00f6reskrifter om s\u00e4kerhetsrevisioner och s\u00e4kerhetsskanningar tr\u00e4dde i kraft i juni 2026. Det inneb\u00e4r att om din organisation faller under lagen (v\u00e4sentliga eller viktiga akt\u00f6rer) beh\u00f6ver du dokumentera dina kryptografiska val.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00f6r ECDH-implementeringar g\u00e4ller dessa krav enligt NIST SP 800-186 och praktiska NIS2-rekommendationer:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Kurvval<\/strong>: Anv\u00e4nd P-256, P-384, P-521, X25519 eller X448. Undvik secp256k1 (Bitcoin-kurvan) i icke-blockchain-sammanhang och \u00e4ldre kurvor som sect163r2.<\/li>\n\n\n\n<li><strong>HKDF-derivering<\/strong>: Alla delade hemligheter m\u00e5ste bearbetas via en godk\u00e4nd KDF (HKDF med SHA-256 eller SHA-384) innan de anv\u00e4nds som symmetriska nycklar.<\/li>\n\n\n\n<li><strong>Ephemeral nycklar<\/strong>: Forward secrecy kr\u00e4ver att nycklarna \u00e4r tillf\u00e4lliga (ECDHE), inte statiska. Logga inte de tillf\u00e4lliga privata nycklarna.<\/li>\n\n\n\n<li><strong>Nyckellagring<\/strong>: Privata servernycklar ska lagras i h\u00e5rdvarus\u00e4kerhetsmoduler (HSM) eller betrodda exekveringsmilj\u00f6er (TEE) f\u00f6r v\u00e4sentliga akt\u00f6rer.<\/li>\n\n\n\n<li><strong>Rotation<\/strong>: Dokumentera och automatisera nyckelrotation. 90 dagars livstid \u00e4r ett vanligt krav i s\u00e4kerhetsramverk.<\/li>\n\n\n\n<li><strong>Loggning<\/strong>: Logga nyckelgenererings- och rotationsh\u00e4ndelser, men aldrig privata nycklar eller r\u00e5a delade hemligheter.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/compliance-checklist.js - NIS2-checklista f\u00f6r ECDH\nconst crypto = require('crypto');\n\nfunction valideraNIS2Konfiguration(kurva, kdfAlgoritm, ephemeral) {\n  const godk\u00e4ndaKurvor = ['prime256v1', 'secp384r1', 'secp521r1', 'x25519', 'x448'];\n  const godk\u00e4ndaKDF    = ['sha256', 'sha384', 'sha512'];\n\n  const resultat = {\n    kurvOK:    godk\u00e4ndaKurvor.includes(kurva.toLowerCase()),\n    kdfOK:     godk\u00e4ndaKDF.includes(kdfAlgoritm.toLowerCase()),\n    ephemeralOK: ephemeral === true,\n    godk\u00e4nd: false\n  };\n\n  resultat.godk\u00e4nd = resultat.kurvOK && resultat.kdfOK && resultat.ephemeralOK;\n\n  if (!resultat.kurvOK) {\n    console.warn(`Varning: Kurva '${kurva}' \u00e4r inte NIS2-godk\u00e4nd. V\u00e4lj P-256, P-384, P-521, X25519 eller X448.`);\n  }\n  if (!resultat.ephemeralOK) {\n    console.warn('Varning: Statiska ECDH-nycklar bryter mot forward secrecy-kravet.');\n  }\n\n  return resultat;\n}\n\nconst kontroll = valideraNIS2Konfiguration('prime256v1', 'sha256', true);\nconsole.log('NIS2-kontroll:', kontroll);\n\/\/ NIS2-kontroll: { kurvOK: true, kdfOK: true, ephemeralOK: true, godk\u00e4nd: true }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vanliga-fallgropar-och-misstag\">Vanliga fallgropar och misstag<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dessa misstag f\u00f6rekommer i produktionskod och kan leda till allvarliga s\u00e4kerhetssvagheter:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Anv\u00e4nda den r\u00e5a delade hemligheten som symmetrisk nyckel<\/strong>. Hemligheten fr\u00e5n <code>computeSecret()<\/code> \u00e4r en punktkoordinat, inte en j\u00e4mnt f\u00f6rdelad nyckel. Den m\u00e5ste alltid g\u00e5 igenom HKDF. Kod som g\u00f6r <code>aes256gcm(deladHemlighet, meddelande)<\/code> direkt \u00e4r felaktig.<\/li>\n\n\n\n<li><strong>Anropa <code>ecdh.setPublicKey()<\/code><\/strong>. Metoden \u00e4r f\u00f6r\u00e5ldrad (deprecated) sedan Node.js v6 och kan l\u00e4mna ECDH-objektet i ett inkonsekvent tillst\u00e5nd. Anv\u00e4nd <code>setPrivateKey()<\/code> i st\u00e4llet, som automatiskt h\u00e4rledde den publika nyckeln.<\/li>\n\n\n\n<li><strong>\u00c5teranv\u00e4nda statiska ECDH-nycklar utan rotation<\/strong>. Statiska nycklar bryter mot forward secrecy. Om en statisk privat nyckel l\u00e4cker kan alla historiska meddelanden dekrypteras. Anv\u00e4nd alltid ephemeral (tillf\u00e4lliga) nycklar per session eller meddelande.<\/li>\n\n\n\n<li><strong>Anv\u00e4nda secp256k1 utanf\u00f6r blockchain-kontext<\/strong>. Bitcoin-kurvan saknar NIST-godk\u00e4nnande och st\u00f6ds inte i TLS. Den \u00e4r designad f\u00f6r effektiv signering, inte nyckelutbyte. V\u00e4lj P-256 eller X25519 i st\u00e4llet.<\/li>\n\n\n\n<li><strong>Validera inte motpartens publika nyckel<\/strong>. I klassisk DH kan en angripare skicka en svag publik nyckel (small subgroup attack). Node.js validerar automatiskt att nyckeln tillh\u00f6r kurvan, men i \u00e4ldre kod eller anpassade implementeringar b\u00f6r du alltid verifiera detta explicit.<\/li>\n\n\n\n<li><strong>F\u00f6rvara privata nycklar i milj\u00f6variabler som str\u00e4ngar<\/strong>. Privata ECDH-nycklar i PEM-format i en <code>.env<\/code>-fil i klartext \u00e4r ett vanligt misstag. Anv\u00e4nd Node.js KeyObject och nyckelhanteringstj\u00e4nster som AWS Secrets Manager eller HashiCorp Vault.<\/li>\n\n\n\n<li><strong>Logga delade hemligheter f\u00f6r fels\u00f6kning<\/strong>. Det f\u00f6rekommer att <code>console.log(deladHemlighet.toString('hex'))<\/code> l\u00e4mnas kvar efter fels\u00f6kning. K\u00f6r ett statiskt analysverktyg som <code>eslint-plugin-security<\/code> f\u00f6r att f\u00e5nga dessa m\u00f6nster.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"felsokning\">Fels\u00f6kning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">De vanligaste felen och hur du l\u00f6ser dem:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Error: Invalid EC key<\/strong>: Den publika nyckeln \u00e4r korrupt, felkodad eller tillh\u00f6r en annan kurva. Kontrollera att b\u00e5da parter anv\u00e4nder exakt samma kurva och att Base64-str\u00e4ngen inte har tappat utfyllnadstecken.<\/li>\n\n\n\n<li><strong>Error: error:0906D06C:PEM routines:PEM_read_bio:no start line<\/strong>: Du f\u00f6rs\u00f6ker ladda en DER-kodad nyckel med PEM-metoder. Kontrollera att <code>format<\/code>-parametern matchar det faktiska nyckelformatet (&#8216;der&#8217; vs &#8216;pem&#8217;).<\/li>\n\n\n\n<li><strong>Delade hemligheter matchar inte<\/strong>: Vanligaste orsaken \u00e4r att en part anv\u00e4nder den komprimerade och den andra den okomprimerade publika nyckeln utan att konvertera. Standardisera p\u00e5 okomprimerat format och konvertera explicit vid behov.<\/li>\n\n\n\n<li><strong>Error: Unsupported state or unable to authenticate data<\/strong>: AES-GCM-autentiseringstaggen st\u00e4mmer inte. Antingen har ciffertexten manipulerats, eller ocks\u00e5 matchar IV, nyckel eller tagg inte. Skicka alltid IV, salt och tagg med samma paket som ciffertexten.<\/li>\n\n\n\n<li><strong>crypto.hkdfSync is not a function<\/strong>: <code>hkdfSync<\/code> introducerades i Node.js v15.0.0. Uppgradera till minst v18 LTS. Som tillf\u00e4llig l\u00f6sning kan du anv\u00e4nda <code>crypto.hkdf<\/code> (asynkron version) eller det externa paketet <code>hkdf<\/code>.<\/li>\n\n\n\n<li><strong>RangeError: Invalid key length<\/strong>: AES-256-GCM kr\u00e4ver exakt 32 byte nyckel. Om HKDF-utdata \u00e4r 48 byte (P-384) och du f\u00f6rs\u00f6ker anv\u00e4nda hela bufferten som AES-nyckel misslyckas det. Ange alltid <code>l\u00e4ngd = 32<\/code> i HKDF-anropet.<\/li>\n\n\n\n<li><strong>X25519 st\u00f6ds inte<\/strong>: Kontrollera att OpenSSL-versionen i din Node.js-build \u00e4r 1.1.1 eller senare. K\u00f6r <code>node -e \"console.log(process.versions.openssl)\"<\/code>. Om versionen \u00e4r \u00e4ldre, uppgradera Node.js.<\/li>\n\n\n\n<li><strong>Prestanda degenerering vid h\u00f6g last<\/strong>: ECDH-nyckelgenerering \u00e4r CPU-intensiv. Vid h\u00f6g last kan du begr\u00e4nsa antalet samtida nyckelgenereringssessioner med en k\u00f6semaforer eller anv\u00e4nda ett Worker Thread-pool f\u00f6r att flytta arbetet fr\u00e5n event loop.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"avancerade-tips-for-produktionssystem\">Avancerade tips f\u00f6r produktionssystem<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dessa tekniker f\u00f6rb\u00e4ttrar s\u00e4kerhet och prestanda i produktionsmilj\u00f6er:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Implementera ECDH med WebCrypto API<\/strong> f\u00f6r Edge-milj\u00f6er (Cloudflare Workers, Deno, Bun) d\u00e4r Node.js crypto-modulen inte \u00e4r tillg\u00e4nglig. WebCrypto API \u00e4r standardiserat och st\u00f6der P-256, P-384, P-521 och X25519 via <code>SubtleCrypto.deriveBits()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Cachelagra nyckelpar med tidsbegr\u00e4nsning<\/strong>. Om din applikation genererar tusentals ECDH-sessioner per sekund kan du cachelagra nyckelpar i 1-5 sekunder och \u00e5teranv\u00e4nda dem f\u00f6r parallella anslutningar. Det minskar CPU-lasten med 60-80 procent vid h\u00f6g last, p\u00e5 bekostnad av reducerad men fortfarande acceptabel forward secrecy-granularitet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Anv\u00e4nd det strukturerade KeyObject-API:et<\/strong> i st\u00e4llet f\u00f6r r\u00e5a buffertoperationer. <code>crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' })<\/code> returnerar KeyObject-instanser som inte kan exporteras av misstag och har st\u00f6d f\u00f6r serialisering via <code>keyObject.export()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Kombinera ECDH med Post-Quantum-algoritmer<\/strong> (hybridkryptografi) f\u00f6r framtidss\u00e4kring. NIST f\u00e4rdigst\u00e4llde sina Post-Quantum-standarder 2024 (ML-KEM\/Kyber f\u00f6r nyckelutbyte). Moderna TLS-implementeringar anv\u00e4nder X25519+ML-KEM-768 f\u00f6r att kombinera bevisad klassisk s\u00e4kerhet med kvants\u00e4kerhet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Testa med Node.js crypto.timingSafeEqual<\/strong> n\u00e4r du j\u00e4mf\u00f6r delade hemligheter eller HMAC-taggar. Regulj\u00e4r <code>===<\/code>-j\u00e4mf\u00f6relse av buffertinneh\u00e5ll kan l\u00e4cka information via tidsskillnader (timing attack).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vanliga-fragor-om-ecdh-i-node-js\">Vanliga fr\u00e5gor om ECDH i Node.js<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"kan-jag-anvanda-ecdh-for-att-kryptera-data-direkt\">Kan jag anv\u00e4nda ECDH f\u00f6r att kryptera data direkt?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Nej. ECDH \u00e4r ett nyckelutbytesprotokoll, inte ett krypteringssystem. Det genererar en delad hemlighet som du sedan anv\u00e4nder f\u00f6r att driva symmetrisk kryptering (AES-256-GCM, ChaCha20-Poly1305). Det \u00e4r hybridkryptografi, och det \u00e4r exakt hur TLS 1.3 fungerar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ar-p-256-sakert-mot-kvantdatorer\">\u00c4r P-256 s\u00e4kert mot kvantdatorer?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Nej. En tillr\u00e4ckligt stor kvantdator som k\u00f6r Shors algoritm kan bryta P-256 (och alla andra elliptiska kurvor). NIST rekommenderar att organisationer som hanterar data med l\u00e5ng sekretessperiod b\u00f6rjar implementera hybridkryptografi (ECDH + ML-KEM\/Kyber) nu. Node.js har \u00e4nnu inget inbyggt st\u00f6d f\u00f6r ML-KEM, men tredjepartsbibliotek finns tillg\u00e4ngliga.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"vad-ar-skillnaden-mellan-ecdh-och-ecdhe\">Vad \u00e4r skillnaden mellan ECDH och ECDHE?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">E:et i ECDHE betyder Ephemeral (tillf\u00e4llig). ECDHE genererar ett nytt nyckelpar f\u00f6r varje session, vilket ger forward secrecy. Statisk ECDH \u00e5teranv\u00e4nder samma nyckelpar. TLS 1.3 kr\u00e4ver ECDHE och till\u00e5ter inte statisk ECDH. I dina Node.js-applikationer b\u00f6r du alltid generera ett nytt nyckelpar per session.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"hur-lange-ska-jag-behalla-en-ecdh-sessionshemlighet\">Hur l\u00e4nge ska jag beh\u00e5lla en ECDH-sessionshemlighet?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sessionshemligheten ska leva precis s\u00e5 l\u00e4nge som sessionen varar, sedan kastas den. Spara den aldrig i en databas eller loggfil. Om du beh\u00f6ver long-term kryptering, h\u00e4rledd sessionsnyckel och kryptera med den, men kassera \u00e4nd\u00e5 sessionsnyckeln n\u00e4r kommunikationen \u00e4r klar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"kan-jag-kora-ecdh-i-en-webblasare\">Kan jag k\u00f6ra ECDH i en webbl\u00e4sare?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ja, via WebCrypto API (<code>window.crypto.subtle<\/code>). ECDH med P-256 st\u00f6ds i alla moderna webbl\u00e4sare sedan 2016. Klientkoden kan anv\u00e4nda WebCrypto och servern Node.js crypto-modulen, de \u00e4r interoperabla om du \u00e4r konsekvent med nyckelformat (SPKI f\u00f6r publika nycklar, PKCS8 f\u00f6r privata).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"vilken-kurva-ska-jag-valja-for-ett-nytt-projekt-2026\">Vilken kurva ska jag v\u00e4lja f\u00f6r ett nytt projekt 2026?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">V\u00e4lj X25519 om du kontrollerar b\u00e5da \u00e4ndarna av kommunikationen. V\u00e4lj P-256 om du m\u00e5ste vara kompatibel med \u00e4ldre system, FIPS 140-3-certifierade milj\u00f6er eller regulatoriska krav som specifikt kr\u00e4ver NIST-kurvor. V\u00e4lj P-384 om din organisation klassificeras som v\u00e4sentlig akt\u00f6r under NIS2 och bearbetar s\u00e4rskilt k\u00e4nsliga data.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"behover-jag-verifiera-motpartens-identitet-vid-ecdh\">Beh\u00f6ver jag verifiera motpartens identitet vid ECDH?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">ECDH i sig ger ingen autentisering. En man-in-the-middle-angripare kan byta ut de publika nycklarna under utbytet. L\u00f6s det med ett av dessa alternativ: signera de publika ECDH-nycklarna med en l\u00e5ngsiktig identitetsnyckel (ECDSA eller Ed25519), anv\u00e4nd certifikat (PKI\/TLS), eller v\u00e4lj ett protokoll som inkluderar autentisering inbyggt, som Signal-protokollets X3DH.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"vilka-node-js-versioner-stoder-x25519\">Vilka Node.js-versioner st\u00f6der X25519?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">X25519 via <code>crypto.generateKeyPair('x25519')<\/code> och <code>crypto.diffieHellman()<\/code> finns tillg\u00e4ngligt fr\u00e5n Node.js v13.9.0. F\u00f6r produktionsmilj\u00f6er, anv\u00e4nd v18 LTS (st\u00f6d till april 2025) eller v22 LTS (st\u00f6d till april 2027). Node.js v16 \u00e4r inte l\u00e4ngre st\u00f6dd sedan september 2023.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"relaterad-tackning\">Relaterad t\u00e4ckning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00f6rdjupa dig i dessa relaterade \u00e4mnen p\u00e5 shattered.io:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/sv\/ecdsa-digitala-signaturer-nodejs\/\">Digitala signaturer i Node.js: 12 steg med ECDSA<\/a> &#8211; Hur du signerar och verifierar data med elliptiska kurvor<\/li>\n\n\n\n<li><a href=\"\/sv\/openssl-nycklar-certifikat\/\">OpenSSL 3.5: nycklar och certifikat i 12 steg<\/a> &#8211; Skapa och hantera kryptografiska nycklar med OpenSSL<\/li>\n\n\n\n<li><a href=\"\/sv\/oauth2-pkce-nodejs\/\">OAuth 2.0 med PKCE i Node.js: 12 steg, 30 min<\/a> &#8211; S\u00e4ker OAuth-implementation med PKCE-utmaning<\/li>\n\n\n\n<li><a href=\"\/sv\/https-och-tls\/\">HTTPS och TLS: h\u00e4ngl\u00e5set, certifikat och vad de skyddar<\/a> &#8211; Hur TLS 1.3 anv\u00e4nder ECDH i varje handskakning<\/li>\n\n\n\n<li><a href=\"\/sv\/digitala-signaturer\/\">Digitala signaturer: hashfunktioner och asymmetriska nycklar<\/a> &#8211; Grunderna i asymmetrisk kryptografi och digital signering<\/li>\n\n\n\n<li><a href=\"\/sv\/cybersaekerhetslagen-nis2-2026\/\">Cybers\u00e4kerhetslagen: 6 000 f\u00f6retag under NIS2<\/a> &#8211; Vilka svenska organisationer NIS2 g\u00e4ller och vad de beh\u00f6ver g\u00f6ra<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Externa resurser:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/nodejs.org\/api\/crypto.html\" rel=\"noopener noreferrer\" target=\"_blank\">Node.js Crypto API-dokumentation<\/a> &#8211; ECDH-klassens fullst\u00e4ndiga referens med alla metoder<\/li>\n\n\n\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc7748\" rel=\"noopener noreferrer\" target=\"_blank\">RFC 7748: Elliptic Curves for Security<\/a> &#8211; X25519 och X448 specifikationen<\/li>\n\n\n\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc5869\" rel=\"noopener noreferrer\" target=\"_blank\">RFC 5869: HKDF<\/a> &#8211; HMAC-based Key Derivation Function specifikationen<\/li>\n\n\n\n<li><a href=\"https:\/\/csrc.nist.gov\/publications\/detail\/sp\/800-186\/final\" rel=\"noopener noreferrer\" target=\"_blank\">NIST SP 800-186<\/a> &#8211; Recommendations for Discrete Logarithm-based Cryptography: Elliptic Curve Domain Parameters<\/li>\n\n\n\n<li><a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Cryptographic_Storage_Cheat_Sheet.html\" rel=\"noopener noreferrer\" target=\"_blank\">OWASP Cryptographic Storage Cheat Sheet<\/a> &#8211; S\u00e4ker lagring av kryptografiskt material<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>ECDH, Elliptic Curve Diffie-Hellman, \u00e4r det nyckelutbytesprotokoll som driver TLS 1.3, Signal-protokollet och merparten av modern krypterad kommunikation. I Node.js finns protokollet inbyggt i crypto-modulen sedan version 0.11.8. Med Node.js\u2026<\/p>\n","protected":false},"author":9,"featured_media":129,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,2],"tags":[],"class_list":["post-128","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-10","category-cryptography"],"_links":{"self":[{"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/posts\/128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/comments?post=128"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/posts\/128\/revisions"}],"predecessor-version":[{"id":130,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/posts\/128\/revisions\/130"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/media\/129"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/media?parent=128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/categories?post=128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/se\/wp-json\/wp\/v2\/tags?post=128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}