{"id":119,"date":"2026-06-19T16:56:30","date_gmt":"2026-06-19T16:56:30","guid":{"rendered":"https:\/\/shattered.io\/fi\/2026\/06\/19\/ecdsa-nodejs\/"},"modified":"2026-06-19T16:57:56","modified_gmt":"2026-06-19T16:57:56","slug":"ecdsa-nodejs","status":"publish","type":"post","link":"https:\/\/shattered.io\/fi\/ecdsa-nodejs\/","title":{"rendered":"ECDSA Node.js:ss\u00e4: 12 vaihetta, 35 min [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">ECDSA (Elliptic Curve Digital Signature Algorithm) on elliptisiin k\u00e4yriin perustuva digitaalinen allekirjoitusalgoritmi, joka tarjoaa RSA-2048:aa vastaavan turvallisuuden murto-osalla avainkoosta. Node.js sis\u00e4lt\u00e4\u00e4 natiivin ECDSA-tuen <code>node:crypto<\/code>-moduulin kautta, ja se kattaa NIST P-256-, P-384- ja P-521-k\u00e4yr\u00e4t sek\u00e4 Bitcoin-ekosysteemin k\u00e4ytt\u00e4m\u00e4n secp256k1-k\u00e4yr\u00e4n. T\u00e4ss\u00e4 oppaassa rakennat t\u00e4ydellisen ECDSA-allekirjoitusj\u00e4rjestelm\u00e4n 12 vaiheessa noin 35 minuutissa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA on vakiostandardi JWT ES256\/ES384\/ES512-tokeneissa, TLS-sertifikaateissa, koodiallekirjoituksessa ja lohkoketjuj\u00e4rjestelmiss\u00e4. <a href=\"https:\/\/csrc.nist.gov\/publications\/detail\/fips\/186\/5\/final\" target=\"_blank\" rel=\"noopener\">NIST FIPS 186-5<\/a> -standardi, joka julkaistiin helmikuussa 2023, vahvistaa P-256:n, P-384:n ja P-521:n suositeltaviksi k\u00e4yriksi viranomaisk\u00e4ytt\u00f6\u00f6n. P-256-avain on vain 32 tavua, kun RSA-2048 vaatii 256 tavua, mink\u00e4 vuoksi ECDSA sopii erityisesti suorituskykykriittisiin ymp\u00e4rist\u00f6ihin.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"edellytykset\">Edellytykset<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ennen aloittamista varmista, ett\u00e4 seuraavat ty\u00f6kalut on asennettu:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Ty\u00f6kalu<\/th><th>Vaadittu versio<\/th><th>Tarkistuskomento<\/th><\/tr><\/thead><tbody><tr><td>Node.js<\/td><td>18.4.0 tai uudempi<\/td><td><code>node --version<\/code><\/td><\/tr><tr><td>npm<\/td><td>9.0.0 tai uudempi<\/td><td><code>npm --version<\/code><\/td><\/tr><tr><td>OpenSSL<\/td><td>3.0.0 tai uudempi<\/td><td><code>openssl version<\/code><\/td><\/tr><tr><td>jsonwebtoken<\/td><td>9.0.0 tai uudempi<\/td><td><code>npm list jsonwebtoken<\/code><\/td><\/tr><tr><td>asn1.js (valinnainen)<\/td><td>5.4.1 tai uudempi<\/td><td><code>npm list asn1.js<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA-tuki sis\u00e4ltyy Node.js:n sis\u00e4\u00e4nrakennettuun <code>node:crypto<\/code>-moduuliin, joten erillisi\u00e4 kryptografiakirjastoja ei tarvita perustoimintoihin. Node.js 18.4.0 toi mukanaan parannetun Ed25519\/Ed448-tuen, mutta ECDSA on tuettu jo Node.js 0.10:st\u00e4 asti. Varmista kuitenkin, ett\u00e4 k\u00e4yt\u00e4t v\u00e4hint\u00e4\u00e4n versiota 18 saadaksesi t\u00e4yden Web Crypto API -yhteensopivuuden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-1-projektin-alustus\">Vaihe 1: Projektin alustus<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Luo uusi Node.js-projekti ja asenna tarvittavat riippuvuudet. ECDSA-perustoiminnot eiv\u00e4t vaadi ulkoisia paketteja, mutta JWT-integraatio vaatii <code>jsonwebtoken<\/code>-paketin.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir ecdsa-tutorial && cd ecdsa-tutorial\nnpm init -y\nnpm install jsonwebtoken\n\n# Luo projektirakenne\nmkdir -p src\/keys src\/examples\ntouch src\/keygen.js src\/sign.js src\/verify.js src\/jwt-example.js src\/secp256k1.js<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Varmista, ett\u00e4 Node.js-ymp\u00e4rist\u00f6si tukee ECDSA:ta ajamalla seuraava komento. Se tulostaa k\u00e4ytett\u00e4viss\u00e4 olevat elliptiset k\u00e4yr\u00e4t:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node -e \"const crypto = require('node:crypto'); console.log(crypto.getCurves().filter(c => c.includes('prime') || c.includes('sec') || c.includes('384') || c.includes('521')));\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Odotettu tuloste on lista, joka sis\u00e4lt\u00e4\u00e4 ainakin <code>prime256v1<\/code>, <code>secp384r1<\/code>, <code>secp521r1<\/code> ja <code>secp256k1<\/code>. Jos lista on tyhj\u00e4, Node.js:si on k\u00e4\u00e4nnetty OpenSSL-tuesta ilman elliptisten k\u00e4yrien tukea, mik\u00e4 on eritt\u00e4in harvinaista virallisissa Node.js-julkaisuissa.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-2-ecdsa-p-256-avainparin-luominen\">Vaihe 2: ECDSA P-256 -avainparin luominen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">P-256 (tunnetaan my\u00f6s nimill\u00e4 prime256v1 ja secp256r1) on yleisin ECDSA-k\u00e4yr\u00e4. Se tarjoaa 128-bittisen turvallisuustason, mik\u00e4 vastaa AES-128:aa ja RSA-3072:ta. P-256 on TLS 1.3:n suosituin allekirjoitusk\u00e4yr\u00e4, ja se on oletuksena useimmissa HTTPS-palvelimissa. Node.js luo P-256-avainparin <code>crypto.generateKeyPairSync<\/code>-funktiolla, joka on synkroninen operaatio. Asynkronista versiota <code>crypto.generateKeyPair<\/code> kannattaa suosia tuotantokoodissa, jotta tapahtumakierto ei blockaudu avainparin luonnin ajaksi.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/keygen.js\nconst crypto = require('node:crypto');\nconst fs = require('node:fs');\nconst path = require('node:path');\n\nfunction generateECDSAKeyPair(curve = 'prime256v1') {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n    namedCurve: curve,\n    publicKeyEncoding: {\n      type: 'spki',\n      format: 'pem'\n    },\n    privateKeyEncoding: {\n      type: 'pkcs8',\n      format: 'pem'\n    }\n  });\n  return { privateKey, publicKey };\n}\n\n\/\/ Luo avainpari ja tallenna tiedostoihin\nconst { privateKey, publicKey } = generateECDSAKeyPair('prime256v1');\n\nfs.writeFileSync(path.join('src', 'keys', 'ec-private.pem'), privateKey);\nfs.writeFileSync(path.join('src', 'keys', 'ec-public.pem'), publicKey);\n\nconsole.log('Yksityinen avain (PKCS8 PEM):');\nconsole.log(privateKey);\nconsole.log('Julkinen avain (SPKI PEM):');\nconsole.log(publicKey);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aja skripti komennolla <code>node src\/keygen.js<\/code>. Odotettu tuloste n\u00e4ytt\u00e4\u00e4 seuraavalta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Yksityinen avain (PKCS8 PEM):\n-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...\n-----END PRIVATE KEY-----\n\nJulkinen avain (SPKI PEM):\n-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\n-----END PUBLIC KEY-----<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">P-256-yksityinen avain on 32 tavua (256 bitti\u00e4), kun taas RSA-2048-yksityinen avain on 256 tavua. PEM-formaatti Base64-enkoodaa n\u00e4m\u00e4 tavut, joten tiedostot ovat jonkin verran suurempia kuin raakadata. <code>type: 'spki'<\/code> (Subject Public Key Info) julkiselle avaimelle ja <code>type: 'pkcs8'<\/code> yksityiselle avaimelle ovat yleisimm\u00e4t ja yhteensopivimmat formaatit, jotka toimivat OpenSSL:n ja muiden kryptografiakirjastojen kanssa.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-3-p-384-ja-p-521-kayrat\">Vaihe 3: P-384 ja P-521 -k\u00e4yr\u00e4t<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Organisaatiot, joilla on korkeammat turvallisuusvaatimukset kuten puolustus-, hallitus- ja rahoituslaitokset, k\u00e4ytt\u00e4v\u00e4t P-384:\u00e4\u00e4 tai P-521:ta. P-384 tarjoaa 192-bittisen ja P-521 jopa 256-bittisen turvallisuustason. Vaikka kasvavat avainkoot hidastavat operaatioita, parannukset turvallisuustasoon ovat merkitt\u00e4v\u00e4t. Suomessa julkishallinnon j\u00e4rjestelm\u00e4t, jotka noudattavat Kyberturvallisuuslakia 124\/2025, hy\u00f6dynt\u00e4v\u00e4t yleisesti P-256:ta tai P-384:\u00e4\u00e4 allekirjoituksiin.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>K\u00e4yr\u00e4<\/th><th>Turvallisuustaso<\/th><th>Allekirjoituksen koko (DER)<\/th><th>Julkisen avaimen koko<\/th><th>Tyypilliset k\u00e4ytt\u00f6kohteet<\/th><\/tr><\/thead><tbody><tr><td>P-256 (prime256v1)<\/td><td>128 bitti\u00e4<\/td><td>70-72 tavua<\/td><td>65 tavua (kompressoimaton)<\/td><td>TLS, JWT ES256, yleinen web<\/td><\/tr><tr><td>P-384 (secp384r1)<\/td><td>192 bitti\u00e4<\/td><td>102-104 tavua<\/td><td>97 tavua<\/td><td>JWT ES384, hallituksen PKI<\/td><\/tr><tr><td>P-521 (secp521r1)<\/td><td>256 bitti\u00e4<\/td><td>137-139 tavua<\/td><td>133 tavua<\/td><td>JWT ES512, korkean turvallisuuden sovellukset<\/td><\/tr><tr><td>secp256k1<\/td><td>128 bitti\u00e4<\/td><td>70-72 tavua<\/td><td>65 tavua<\/td><td>Bitcoin, Ethereum, lohkoketjut<\/td><\/tr><tr><td>RSA-2048 (vertailu)<\/td><td>112 bitti\u00e4<\/td><td>256 tavua<\/td><td>270 tavua<\/td><td>Vanha infrastruktuuri<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Luo avainparit kaikille k\u00e4yrille ja vertaile kokoja\nconst crypto = require('node:crypto');\n\nconst curves = [\n  { name: 'prime256v1', hash: 'sha256', jwtAlg: 'ES256' },\n  { name: 'secp384r1', hash: 'sha384', jwtAlg: 'ES384' },\n  { name: 'secp521r1', hash: 'sha512', jwtAlg: 'ES512' }\n];\n\ncurves.forEach(({ name, hash, jwtAlg }) => {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n    namedCurve: name,\n    publicKeyEncoding: { type: 'spki', format: 'pem' },\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n  });\n\n  \/\/ Tee testiallekirjoitus ja mittaa koko\n  const sign = crypto.createSign(hash);\n  sign.update(Buffer.from('testiteksti\u00e4'));\n  const sig = sign.sign({ key: privateKey, dsaEncoding: 'der' });\n\n  console.log(`\\nK\u00e4yr\u00e4: ${name} (${jwtAlg})`);\n  console.log(`  Allekirjoituksen koko (DER): ${sig.length} tavua`);\n  console.log(`  Yksityinen avain PEM-koko: ${privateKey.length} merkki\u00e4`);\n  console.log(`  Julkinen avain PEM-koko: ${publicKey.length} merkki\u00e4`);\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Node.js k\u00e4ytt\u00e4\u00e4 OpenSSL:n k\u00e4\u00e4nt\u00e4mi\u00e4 toteutuksia kaikista NIST-k\u00e4yrist\u00e4. Jos sinun t\u00e4ytyy tarkistaa, mit\u00e4 k\u00e4yri\u00e4 Node.js-ymp\u00e4rist\u00f6si tukee, aja <code>node -e \"require('node:crypto').getCurves().forEach(c => console.log(c))\"<\/code>. Virallinen Node.js-dokumentaatio k\u00e4yrist\u00e4 l\u00f6ytyy osoitteesta <a href=\"https:\/\/nodejs.org\/api\/crypto.html\" target=\"_blank\" rel=\"noopener\">nodejs.org\/api\/crypto.html<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-4-digitaalisen-allekirjoituksen-luominen\">Vaihe 4: Digitaalisen allekirjoituksen luominen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA-allekirjoitus koostuu kahdesta suuresta luvusta r ja s, jotka yhdess\u00e4 todentavat, ett\u00e4 allekirjoittajalla oli hallussaan yksityinen avain allekirjoitushetkell\u00e4. Node.js:ss\u00e4 k\u00e4ytet\u00e4\u00e4n <code>crypto.createSign()<\/code>-metodia, joka ensin tiivist\u00e4\u00e4 datan valitulla hajautusfunktiolla ja sen j\u00e4lkeen allekirjoittaa tiivisteen ECDSA-algoritmilla.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA vaatii aina tiivisteen allekirjoitettavasta datasta, toisin kuin Ed25519, joka allekirjoittaa raakadatan suoraan. T\u00e4m\u00e4 on t\u00e4rke\u00e4 ero: P-256:lle suositellaan SHA-256:ta, P-384:lle SHA-384:\u00e4\u00e4 ja P-521:lle SHA-512:ta. N\u00e4m\u00e4 yhdistelm\u00e4t ovat NIST FIPS 186-5 -standardin suosittelemia ja varmistavat, ett\u00e4 kokonaisturvallisuustaso vastaa k\u00e4yr\u00e4n tarjoamaa suojausta.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/sign.js\nconst crypto = require('node:crypto');\nconst fs = require('node:fs');\n\nconst privateKeyPem = fs.readFileSync('src\/keys\/ec-private.pem', 'utf8');\nconst privateKey = crypto.createPrivateKey(privateKeyPem);\n\nfunction signMessage(message, privateKey, hashAlgorithm = 'sha256') {\n  const sign = crypto.createSign(hashAlgorithm);\n  sign.update(message);\n  sign.end();\n\n  \/\/ DER-formaatti on oletusarvo (ASN.1-enkoodattu)\n  const signature = sign.sign({\n    key: privateKey,\n    dsaEncoding: 'der'\n  });\n\n  return signature;\n}\n\nconst message = 'T\u00e4m\u00e4 viesti allekirjoitetaan ECDSA P-256:lla';\nconst signature = signMessage(message, privateKey, 'sha256');\n\nconsole.log('Allekirjoitus (DER, hex):');\nconsole.log(signature.toString('hex'));\nconsole.log(`\\nAllekirjoituksen koko: ${signature.length} tavua`);\nconsole.log(`\\nAllekirjoitus (Base64):`);\nconsole.log(signature.toString('base64'));\n\n\/\/ Tallenna allekirjoitus tiedostoon\nfs.writeFileSync('src\/keys\/signature.der', signature);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Odotettu tuloste osoittaa, ett\u00e4 DER-enkoodattu P-256-allekirjoitus on 70-72 tavua:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Allekirjoitus (DER, hex):\n3045022100a1b2c3...f0a1b2022067d3e4...ab12cd\n\nAllekirjoituksen koko: 71 tavua\n\nAllekirjoitus (Base64):\nMEUCIQC[...base64-enkoodattu DER...]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">T\u00e4rke\u00e4 huomio: ECDSA-allekirjoitukset eiv\u00e4t ole deterministisi\u00e4 oletusarvoina. Sama viesti samalla avaimella tuottaa eri allekirjoituksen joka kerta, koska algoritmi k\u00e4ytt\u00e4\u00e4 satunnaista nonce-arvoa k. Jos haluat deterministisi\u00e4 allekirjoituksia, k\u00e4yt\u00e4 RFC 6979 -standardin mukaista k-arvon johdatusta. Lue lis\u00e4\u00e4 RFC 6979:st\u00e4 osoitteesta <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc6979\" target=\"_blank\" rel=\"noopener\">rfc-editor.org\/rfc\/rfc6979<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-5-allekirjoituksen-vahvistaminen\">Vaihe 5: Allekirjoituksen vahvistaminen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Allekirjoituksen vahvistaminen vaatii ainoastaan julkisen avaimen, alkuper\u00e4isen viestin ja allekirjoituksen. Vahvistusprosessi rekonstruoi pistearitmeetiikalla pisteyhdistelm\u00e4n ja tarkistaa, vastaako se allekirjoituksessa olevaa r-arvoa. Yksityist\u00e4 avainta ei tarvita vahvistukseen, mik\u00e4 mahdollistaa julkisen avaimen jakamisen avoimesti esimerkiksi JWKS-p\u00e4\u00e4tepisteess\u00e4 tai X.509-sertifikaatissa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/verify.js\nconst crypto = require('node:crypto');\nconst fs = require('node:fs');\n\nconst publicKeyPem = fs.readFileSync('src\/keys\/ec-public.pem', 'utf8');\nconst signature = fs.readFileSync('src\/keys\/signature.der');\nconst publicKey = crypto.createPublicKey(publicKeyPem);\n\nfunction verifySignature(message, signature, publicKey, hashAlgorithm = 'sha256') {\n  const verify = crypto.createVerify(hashAlgorithm);\n  verify.update(message);\n  verify.end();\n\n  return verify.verify(\n    { key: publicKey, dsaEncoding: 'der' },\n    signature\n  );\n}\n\nconst message = 'T\u00e4m\u00e4 viesti allekirjoitetaan ECDSA P-256:lla';\nconst isValid = verifySignature(message, signature, publicKey, 'sha256');\n\nconsole.log(`Allekirjoitus kelpaa: ${isValid}`);\n\n\/\/ Testaa v\u00e4\u00e4r\u00e4ll\u00e4 viestill\u00e4\nconst wrongMessage = 'T\u00e4m\u00e4 on v\u00e4\u00e4r\u00e4 viesti';\nconst isValidWrong = verifySignature(wrongMessage, signature, publicKey, 'sha256');\nconsole.log(`V\u00e4\u00e4r\u00e4 viesti kelpaa: ${isValidWrong}`);\n\n\/\/ Testaa muutetulla allekirjoituksella\nconst tamperedSig = Buffer.from(signature);\ntamperedSig[10] ^= 0xFF;\ntry {\n  const isValidTampered = verifySignature(message, tamperedSig, publicKey, 'sha256');\n  console.log(`Muutettu allekirjoitus kelpaa: ${isValidTampered}`);\n} catch (err) {\n  console.log(`Muutettu allekirjoitus aiheutti virheen: ${err.message}`);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Odotettu tuloste vahvistaa, ett\u00e4 validointi toimii oikein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Allekirjoitus kelpaa: true\nV\u00e4\u00e4r\u00e4 viesti kelpaa: false\nMuutettu allekirjoitus aiheutti virheen: error:1E08010C:DECODER routines::unsupported<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Huomaa, ett\u00e4 muutettu allekirjoitus voi aiheuttaa joko <code>false<\/code>-paluuarvon tai poikkeuksen riippuen siit\u00e4, mihin kohtaan muutos osui. DER-enkoodauksessa allekirjoitus on rakenteistettu ASN.1-tietoa, joten joidenkin tavujen muuttaminen rikkoo rakenteen ja aiheuttaa parsintavirheen jo ennen kryptografista tarkistusta. T\u00e4m\u00e4 on normaalia k\u00e4ytt\u00e4ytymist\u00e4 ja osoittaa, ett\u00e4 ECDSA suojaa sek\u00e4 datan eheytt\u00e4 ett\u00e4 aitouden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-6-der-ja-ieee-p1363-formaatit\">Vaihe 6: DER- ja IEEE P1363 -formaatit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA-allekirjoituksilla on kaksi p\u00e4\u00e4formaattia: DER (Distinguished Encoding Rules) ja IEEE P1363. Node.js tukee molempia <code>dsaEncoding<\/code>-parametrin kautta. DER on oletusarvo ja se k\u00e4ytt\u00e4\u00e4 ASN.1-rakennetta, joka lis\u00e4\u00e4 muutaman tavun yl\u00e4rasitteen mutta on yleisimmin k\u00e4ytetty formaatti muiden j\u00e4rjestelmien kanssa. IEEE P1363 on yksinkertaisempi r||s-ketjutus kiinte\u00e4ll\u00e4 pituudella. Formaattien sekoittaminen on yleisin yhteensopivuusongelma ECDSA-integraatioissa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Vertaile DER ja IEEE P1363 -formaatteja\nconst crypto = require('node:crypto');\n\nconst { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n  namedCurve: 'prime256v1',\n  publicKeyEncoding: { type: 'spki', format: 'pem' },\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n});\n\nconst message = Buffer.from('Testidataa ECDSA-formaattivertailuun');\n\n\/\/ DER-allekirjoitus (ASN.1)\nconst signDER = crypto.createSign('sha256');\nsignDER.update(message);\nconst sigDER = signDER.sign({ key: privateKey, dsaEncoding: 'der' });\n\n\/\/ IEEE P1363 -allekirjoitus (r||s, kiinte\u00e4 pituus)\nconst signP1363 = crypto.createSign('sha256');\nsignP1363.update(message);\nconst sigP1363 = signP1363.sign({ key: privateKey, dsaEncoding: 'ieee-p1363' });\n\nconsole.log(`DER-allekirjoituksen koko: ${sigDER.length} tavua`);\nconsole.log(`IEEE P1363 -allekirjoituksen koko: ${sigP1363.length} tavua`);\n\n\/\/ Vahvistus oikealla formaatilla\nconst verifyDER = crypto.createVerify('sha256');\nverifyDER.update(message);\nconst validDER = verifyDER.verify({ key: publicKey, dsaEncoding: 'der' }, sigDER);\n\nconst verifyP1363 = crypto.createVerify('sha256');\nverifyP1363.update(message);\nconst validP1363 = verifyP1363.verify({ key: publicKey, dsaEncoding: 'ieee-p1363' }, sigP1363);\n\nconsole.log(`DER kelpaa: ${validDER}`);\nconsole.log(`IEEE P1363 kelpaa: ${validP1363}`);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">P-256-k\u00e4yr\u00e4ll\u00e4 IEEE P1363 tuottaa aina tarkalleen 64 tavun allekirjoituksen (32 tavua r + 32 tavua s), kun taas DER tuottaa 70-72 tavua johtuen ASN.1-otsikkorakenteesta. P-384:ll\u00e4 IEEE P1363 on 96 tavua ja P-521:ll\u00e4 132 tavua. K\u00e4yt\u00e4 IEEE P1363 -formaattia JWT-tokeneissa (ES256, ES384, ES512), koska JWT-standardi RFC 7518 vaatii kiinte\u00e4n pituiset r||s-allekirjoitukset. Lue JWT-allekirjoitusstandardista lis\u00e4\u00e4 osoitteesta <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7518\" target=\"_blank\" rel=\"noopener\">datatracker.ietf.org\/doc\/html\/rfc7518<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-7-ecdsa-jwt-tokeneissa-es256\">Vaihe 7: ECDSA JWT-tokeneissa (ES256)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">JWT-tokenit k\u00e4ytt\u00e4v\u00e4t ECDSA:ta algoritmien ES256, ES384 ja ES512 kautta, jotka vastaavat P-256+SHA-256, P-384+SHA-384 ja P-521+SHA-512 -yhdistelmi\u00e4. ECDSA-pohjaiset JWT-tokenit ovat turvallisempia kuin HMAC-pohjaiset (HS256), koska vahvistus ei vaadi jaettua salaisuutta. T\u00e4m\u00e4 mahdollistaa julkisen avaimen jakamisen avoimesti esimerkiksi JWKS-p\u00e4\u00e4tepisteess\u00e4, mik\u00e4 on hajautettujen mikropalveluarkkitehtuurien vakioratkaisu.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/jwt-example.js\nconst crypto = require('node:crypto');\nconst jwt = require('jsonwebtoken');\n\n\/\/ Luo ES256-avainpari JWT:t\u00e4 varten\nconst { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n  namedCurve: 'prime256v1',\n  publicKeyEncoding: { type: 'spki', format: 'pem' },\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n});\n\n\/\/ Luo JWT-token ES256-algoritmilla\nconst payload = {\n  sub: '1234567890',\n  name: 'Matti Meik\u00e4l\u00e4inen',\n  iat: Math.floor(Date.now() \/ 1000),\n  exp: Math.floor(Date.now() \/ 1000) + 3600,\n  roles: ['admin', 'user']\n};\n\nconst token = jwt.sign(payload, privateKey, {\n  algorithm: 'ES256',\n  header: { kid: 'avain-2026-06' }\n});\n\nconsole.log('JWT ES256 -token:');\nconsole.log(token);\nconsole.log(`\\nTokenin pituus: ${token.length} merkki\u00e4`);\n\n\/\/ Jaa vain julkinen avain tokenin vahvistajille\nconst decoded = jwt.verify(token, publicKey, { algorithms: ['ES256'] });\nconsole.log('\\nVahvistettu payload:');\nconsole.log(JSON.stringify(decoded, null, 2));\n\n\/\/ Yrit\u00e4 vahvistaa v\u00e4\u00e4r\u00e4ll\u00e4 julkisella avaimella\nconst { publicKey: wrongPublicKey } = crypto.generateKeyPairSync('ec', {\n  namedCurve: 'prime256v1',\n  publicKeyEncoding: { type: 'spki', format: 'pem' },\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n});\n\ntry {\n  jwt.verify(token, wrongPublicKey, { algorithms: ['ES256'] });\n} catch (err) {\n  console.log(`\\nV\u00e4\u00e4r\u00e4 avain estetty: ${err.message}`);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ES256 on suositeltavin JWT-algoritmi uusiin sovelluksiin, koska se yhdist\u00e4\u00e4 hyv\u00e4n suorituskyvyn ja turvallisuuden. Verratuna HS256:een (HMAC-SHA256), ES256 mahdollistaa hajautetun vahvistuksen ilman jaettua salaisuutta. Jos olet rakentanut JWT-sovelluksia aiemmin, lue lis\u00e4\u00e4 <a href=\"\/fi\/jwt-todennus-nodejs\/\">JWT-todennuksesta Node.js:ss\u00e4<\/a> -oppaastamme, jossa k\u00e4ymme l\u00e4pi my\u00f6s HS256-l\u00e4hestymistavan. Avaintunniste <code>kid<\/code>-kent\u00e4ss\u00e4 mahdollistaa sujuvan avainrotaation, kun palvelu julkaisee useita aktiivisia avaimia JWKS-p\u00e4\u00e4tepisteess\u00e4\u00e4n.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-8-avainten-turvallinen-tallennus\">Vaihe 8: Avainten turvallinen tallennus<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Yksityisten avainten turvallinen tallennus on kriittinen osa ECDSA-toteutusta. \u00c4l\u00e4 koskaan tallenna yksityisi\u00e4 avaimia versionhallintaan, ymp\u00e4rist\u00f6muuttujiin suoraan tai selkokielisen\u00e4 tiedostoj\u00e4rjestelm\u00e4\u00e4n. K\u00e4yt\u00e4 sen sijaan salasanasuojattua PKCS8-enkoodausta kehitysymp\u00e4rist\u00f6iss\u00e4 ja HSM-laitteistoja tai salausholvij\u00e4rjestelmi\u00e4 kuten HashiCorp Vault tai AWS KMS tuotannossa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Turvallinen avainten tallennus salasanasuojauksella\nconst crypto = require('node:crypto');\nconst fs = require('node:fs');\n\nconst passphrase = process.env.KEY_PASSPHRASE;\nif (!passphrase) {\n  throw new Error('KEY_PASSPHRASE-ymp\u00e4rist\u00f6muuttuja puuttuu');\n}\n\n\/\/ Luo avainpari AES-256-CBC-salauksella\nconst { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n  namedCurve: 'prime256v1',\n  publicKeyEncoding: {\n    type: 'spki',\n    format: 'pem'\n  },\n  privateKeyEncoding: {\n    type: 'pkcs8',\n    format: 'pem',\n    cipher: 'aes-256-cbc',\n    passphrase: passphrase\n  }\n});\n\n\/\/ Tallenna rajoitetuilla oikeuksilla (600 = vain omistaja voi lukea\/kirjoittaa)\nfs.writeFileSync('src\/keys\/ec-private-encrypted.pem', privateKey, { mode: 0o600 });\nfs.writeFileSync('src\/keys\/ec-public.pem', publicKey);\nconsole.log('Salattu yksityinen avain tallennettu (oikeudet 600)');\n\n\/\/ Lataa salattu avain oikealla salasanalla\nconst encryptedPem = fs.readFileSync('src\/keys\/ec-private-encrypted.pem', 'utf8');\nconst loadedKey = crypto.createPrivateKey({\n  key: encryptedPem,\n  passphrase: passphrase\n});\n\nconst keyDetails = loadedKey.asymmetricKeyDetails;\nconsole.log(`Avain ladattu: tyyppi=${loadedKey.asymmetricKeyType}, k\u00e4yr\u00e4=${keyDetails.namedCurve}`);\n\n\/\/ Varmista, ett\u00e4 salaus toimii latauksen j\u00e4lkeen\nconst sign = crypto.createSign('sha256');\nsign.update('Testiteksti\u00e4');\nconst sig = sign.sign(loadedKey);\nconsole.log(`Allekirjoitus ladatulla salatulla avaimella onnistui (${sig.length} tavua)`);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tuotantoymp\u00e4rist\u00f6iss\u00e4 yksityiset avaimet tulisi tallentaa aina salatussa muodossa. AES-256-CBC on vakiostandardi PEM-tiedostojen salaukseen. Tallenna salasana erilliseen turvalliseen paikkaan kuten salausholvia k\u00e4ytt\u00e4en, ei samaan tiedostoj\u00e4rjestelm\u00e4\u00e4n yksityisen avaimen kanssa. Tiedostoj\u00e4rjestelm\u00e4n k\u00e4ytt\u00f6oikeuksien rajoittaminen <code>mode: 0o600<\/code>-parametrilla est\u00e4\u00e4 muita k\u00e4ytt\u00e4ji\u00e4 lukemasta avainta Unix-j\u00e4rjestelmiss\u00e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-9-secp256k1-bitcoin-lohkoketjuun\">Vaihe 9: secp256k1 Bitcoin-lohkoketjuun<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">secp256k1 on Satoshi Nakamoton valitsema k\u00e4yr\u00e4 Bitcoiniin ja se on sittemmin levinnyt Ethereumiin ja muihin lohkoketjuihin. secp256k1 eroaa NIST P-256:sta k\u00e4yr\u00e4n parametreissa: secp256k1 on Koblitzin k\u00e4yr\u00e4 muodossa y\u00b2 = x\u00b3 + 7, kun taas P-256 on satunnaistettu k\u00e4yr\u00e4 muodossa y\u00b2 = x\u00b3 + ax + b. secp256k1:ll\u00e4 on tiettyj\u00e4 laskennallisia etuja Koblitzin rakenteen ansiosta, mink\u00e4 vuoksi se on lohkoketjuekosysteemiss\u00e4 laajasti k\u00e4yt\u00f6ss\u00e4. Node.js tukee secp256k1:\u00e4\u00e4 OpenSSL:n kautta, mutta tuki saattaa puuttua joissakin rajoitetuissa k\u00e4ytt\u00f6ymp\u00e4rist\u00f6iss\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/secp256k1.js - Bitcoin-yhteensopiva ECDSA\nconst crypto = require('node:crypto');\n\n\/\/ Tarkista secp256k1-tuki\nconst supportedCurves = crypto.getCurves();\nif (!supportedCurves.includes('secp256k1')) {\n  throw new Error('secp256k1 ei ole tuettu t\u00e4ss\u00e4 OpenSSL-versiossa');\n}\n\n\/\/ Luo secp256k1-avainpari\nconst { privateKey: secp256k1PrivKey, publicKey: secp256k1PubKey } =\n  crypto.generateKeyPairSync('ec', {\n    namedCurve: 'secp256k1',\n    publicKeyEncoding: { type: 'spki', format: 'pem' },\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n  });\n\n\/\/ Allekirjoita Bitcoin-transaktion kaltainen data\nconst transactionData = JSON.stringify({\n  from: '1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf',\n  to: '1BoatSLRHtKNngkdXEeobR76b53LETtpyT',\n  amount: '0.001',\n  nonce: 42\n});\n\nconst sign = crypto.createSign('sha256');\nsign.update(transactionData);\nconst txSignature = sign.sign({ key: secp256k1PrivKey, dsaEncoding: 'der' });\n\nconsole.log(`Transaktiodata: ${transactionData}`);\nconsole.log(`Allekirjoitus (hex): ${txSignature.toString('hex')}`);\nconsole.log(`Allekirjoituksen koko: ${txSignature.length} tavua`);\n\n\/\/ Vahvista transaktio\nconst verify = crypto.createVerify('sha256');\nverify.update(transactionData);\nconst isValid = verify.verify({ key: secp256k1PubKey, dsaEncoding: 'der' }, txSignature);\nconsole.log(`Transaktio kelpaa: ${isValid}`);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Huomaa, ett\u00e4 Bitcoin ja Ethereum k\u00e4ytt\u00e4v\u00e4t ECDSA:ta transaktioiden allekirjoittamiseen secp256k1-k\u00e4yr\u00e4ll\u00e4, mutta niiden protokollat vaativat erityisen enkoodauksen ja tiivisteformaatin. Todellisessa lohkoketjusovelluksessa kannattaa k\u00e4ytt\u00e4\u00e4 erikoistuneita kirjastoja kuten <code>bitcoinjs-lib<\/code> tai <code>ethers.js<\/code>, jotka hoitavat kaiken tarvittavan formatoinnin automaattisesti. Node.js:n natiivi <code>node:crypto<\/code> tuottaa matemaattisesti oikean ECDSA-allekirjoituksen, mutta transaktiospesifinen formaatointity\u00f6 kuuluu ekosysteemikirjastoille. OpenSSL:n ecparam-komento mahdollistaa secp256k1-avainten luomisen my\u00f6s komentorivilt\u00e4, lis\u00e4tietoa osoitteesta <a href=\"https:\/\/www.openssl.org\/docs\/man3.0\/man1\/openssl-ecparam.html\" target=\"_blank\" rel=\"noopener\">openssl.org\/docs\/man3.0\/man1\/openssl-ecparam.html<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-10-suorituskykyvertailu-rsahan\">Vaihe 10: Suorituskykyvertailu RSA:han<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA:n merkitt\u00e4vin etu on suorituskyky pienemm\u00e4ll\u00e4 avainkoolla. P-256-allekirjoitus on noin 2,7-kertaa nopeampi kuin RSA-2048-allekirjoitus, ja avainten luonti on jopa 10-kertaa nopeampaa. T\u00e4m\u00e4 n\u00e4kyy erityisesti TLS-k\u00e4ttelyiss\u00e4, joissa suuri m\u00e4\u00e4r\u00e4 lyhyit\u00e4 yhteyksi\u00e4 hy\u00f6tyy nopeammasta allekirjoituksesta. RSA-2048 on kuitenkin vahvistuksessa nopeampi pienen julkisen eksponenttinsa (65537) vuoksi.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Suorituskykyvertailu: ECDSA P-256 vs RSA-2048\nconst crypto = require('node:crypto');\n\nfunction benchmark(name, fn, iterations = 500) {\n  const start = process.hrtime.bigint();\n  for (let i = 0; i < iterations; i++) fn();\n  const end = process.hrtime.bigint();\n  const totalMs = Number(end - start) \/ 1_000_000;\n  const opsPerSec = Math.round(iterations \/ (totalMs \/ 1000));\n  console.log(`${name}: ${opsPerSec.toLocaleString('fi-FI')} ops\/s`);\n  return opsPerSec;\n}\n\nconst ecKeys = crypto.generateKeyPairSync('ec', {\n  namedCurve: 'prime256v1',\n  publicKeyEncoding: { type: 'spki', format: 'pem' },\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n});\n\nconst rsaKeys = crypto.generateKeyPairSync('rsa', {\n  modulusLength: 2048,\n  publicKeyEncoding: { type: 'spki', format: 'pem' },\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n});\n\nconst message = Buffer.from('Suorituskykytesti');\nconst ecSig = (() => { const s = crypto.createSign('sha256'); s.update(message); return s.sign(ecKeys.privateKey); })();\nconst rsaSig = (() => { const s = crypto.createSign('sha256'); s.update(message); return s.sign(rsaKeys.privateKey); })();\n\nconsole.log('\\n=== Allekirjoitus (ops\/s, suurempi = parempi) ===');\nbenchmark('ECDSA P-256', () => { const s = crypto.createSign('sha256'); s.update(message); s.sign(ecKeys.privateKey); });\nbenchmark('RSA-2048    ', () => { const s = crypto.createSign('sha256'); s.update(message); s.sign(rsaKeys.privateKey); });\n\nconsole.log('\\n=== Vahvistus (ops\/s, suurempi = parempi) ===');\nbenchmark('ECDSA P-256', () => { const v = crypto.createVerify('sha256'); v.update(message); v.verify(ecKeys.publicKey, ecSig); });\nbenchmark('RSA-2048    ', () => { const v = crypto.createVerify('sha256'); v.update(message); v.verify(rsaKeys.publicKey, rsaSig); });\n\nconsole.log(`\\nAllekirjoitusten koot:`);\nconsole.log(`ECDSA P-256 DER: ${ecSig.length} tavua`);\nconsole.log(`RSA-2048:        ${rsaSig.length} tavua`);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tyypilliset tulokset Node.js 22:lla Intel Core i7 -prosessorilla: ECDSA P-256 allekirjoittaa noin 45 000-50 000 ja vahvistaa noin 15 000-18 000 operaatiota sekunnissa. RSA-2048 allekirjoittaa noin 4 000-5 000 mutta vahvistaa jopa 100 000-120 000 operaatiota sekunnissa julkisen eksponentin pienen arvon vuoksi. TLS-palvelimissa, joissa palvelin allekirjoittaa mutta asiakkaat vahvistavat, ECDSA tarjoaa selv\u00e4sti paremman palvelimen suorituskyvyn. ECDSA:n allekirjoitukset ovat my\u00f6s 3,6-kertaa pienempi\u00e4 kuin RSA-2048:n allekirjoitukset.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-11-yleisimmat-virheet-ja-ansat\">Vaihe 11: Yleisimm\u00e4t virheet ja ansat<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA:n k\u00e4ytt\u00f6\u00f6notto on suoraviivainen, mutta tietyt virheet voivat johtaa tietoturva-aukkoihin tai toimintah\u00e4iri\u00f6ihin. T\u00e4ss\u00e4 ovat yleisimm\u00e4t sudenkuopat, joihin kehitt\u00e4j\u00e4t t\u00f6rm\u00e4\u00e4v\u00e4t Node.js-toteutuksissa.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ansa-1-nonce-arvon-uudelleenkaytto\">Ansa 1: Nonce-arvon uudelleenk\u00e4ytt\u00f6<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA:n vakavin haavoittuvuus on nonce-arvon k uudelleenk\u00e4ytt\u00f6. Jos sama k allekirjoittaa kaksi eri viesti\u00e4, yksityinen avain voidaan laskea algebrallisesti. T\u00e4m\u00e4 on suora matemaattinen seuraus ECDSA:n algoritmista. Sony PlayStation 3:n firmware-allekirjoitusj\u00e4rjestelm\u00e4 k\u00e4ytti vakio-k-arvoa, mik\u00e4 mahdollisti hakkereille konsolin koodiallekirjoitusj\u00e4rjestelm\u00e4n murtamisen vuonna 2010. Node.js:n <code>crypto.createSign()<\/code> k\u00e4ytt\u00e4\u00e4 OpenSSL:n kryptografisesti turvallista satunnaislukugeneraattoria automaattisesti, joten t\u00e4t\u00e4 ongelmaa ei esiinny standardia toteutusta k\u00e4ytett\u00e4ess\u00e4. \u00c4l\u00e4 koskaan yrit\u00e4 toteuttaa omaa k-arvon generointia.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ansa-2-dsaencoding-formaatin-sekoittaminen\">Ansa 2: dsaEncoding-formaatin sekoittaminen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Jos allekirjoittaja k\u00e4ytt\u00e4\u00e4 DER-formaattia mutta vahvistaja odottaa IEEE P1363 -formaattia, vahvistus ep\u00e4onnistuu hiljaisesti (palauttaa false). T\u00e4m\u00e4 on yleisin yhteensopivuusongelma eri j\u00e4rjestelmien v\u00e4lill\u00e4. JWT-kirjastot odottavat IEEE P1363 -formaattia, mutta Node.js:n oletusarvo on DER. Varmista aina, ett\u00e4 allekirjoittajan ja vahvistajan <code>dsaEncoding<\/code>-parametrit vastaavat toisiaan. Kun integroit kolmannen osapuolen palveluun, tarkista aina, mit\u00e4 formaattia kyseinen palvelu odottaa.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Virhetilanne<\/th><th>Oireet<\/th><th>Ratkaisu<\/th><\/tr><\/thead><tbody><tr><td>V\u00e4\u00e4r\u00e4 dsaEncoding<\/td><td>verify() palauttaa false, allekirjoitus on oikea<\/td><td>Varmista &#8216;der&#8217; tai &#8216;ieee-p1363&#8217; molemmissa p\u00e4iss\u00e4<\/td><\/tr><tr><td>V\u00e4\u00e4r\u00e4 hajautusalgoritmi<\/td><td>verify() palauttaa false tai OpenSSL-virhe<\/td><td>K\u00e4yt\u00e4 samaa sha256\/sha384\/sha512 molemmissa<\/td><\/tr><tr><td>Avain v\u00e4\u00e4r\u00e4ll\u00e4 k\u00e4yr\u00e4ll\u00e4<\/td><td>error:100AE081:elliptic curve routines<\/td><td>Varmista namedCurve on sama generointivaiheessa<\/td><\/tr><tr><td>PEM-muotoilu rikki<\/td><td>error:PEM routines:bad end line<\/td><td>Tarkista ett\u00e4 BEGIN\/END-otsikot ovat ehji\u00e4<\/td><\/tr><tr><td>Salattu avain ilman salasanaa<\/td><td>error:06065064:digital envelope routines<\/td><td>Anna passphrase createPrivateKey()-kutsussa<\/td><\/tr><tr><td>V\u00e4\u00e4r\u00e4 avaintyyppi JWT:ss\u00e4<\/td><td>JsonWebTokenError: secretOrPublicKey is invalid<\/td><td>K\u00e4yt\u00e4 EC-avainta ES256:lle, ei RSA-avainta<\/td><\/tr><tr><td>secp256k1 ei tuettu<\/td><td>Error: unknown group name secp256k1<\/td><td>P\u00e4ivit\u00e4 OpenSSL 3.0+:aan tai tarkista k\u00e4yr\u00e4tuki<\/td><\/tr><tr><td>Suuri tiedosto blockaa tapahtumakierron<\/td><td>Palvelin jumiutuu allekirjoituksen ajaksi<\/td><td>K\u00e4yt\u00e4 streaming-rajapintaa tai generateKeyPair (async)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">V\u00e4\u00e4r\u00e4 hajautusalgoritmi on eritt\u00e4in yleinen virhe erityisesti siirrytt\u00e4ess\u00e4 P-256:sta P-384:\u00e4\u00e4n. P-256:n kanssa k\u00e4ytet\u00e4\u00e4n SHA-256:ta, P-384:n kanssa SHA-384:\u00e4\u00e4 ja P-521:n kanssa SHA-512:ta. N\u00e4it\u00e4 yhdistelmi\u00e4 suosittelee NIST FIPS 186-5 -standardi. Voit k\u00e4ytt\u00e4\u00e4 mit\u00e4 tahansa SHA-2-varianttia mink\u00e4 tahansa k\u00e4yr\u00e4n kanssa, mutta k\u00e4yr\u00e4n turvallisuustaso hukkuu jos k\u00e4yt\u00e4t liian heikkoa hajautusfunktiota. Esimerkiksi P-521 + SHA-256 toimii teknisesti, mutta SHA-256:n 128-bittinen tuloste rajoittaa kokonaisturvallisuuden 128 bittiin vaikka k\u00e4yr\u00e4 tarjoaisi 256-bittisen turvallisuuden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vaihe-12-turvallisuuden-parhaat-kaytannot\">Vaihe 12: Turvallisuuden parhaat k\u00e4yt\u00e4nn\u00f6t<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA:n turvallinen k\u00e4ytt\u00f6 edellytt\u00e4\u00e4 useita lis\u00e4varotoimia avaingeneroinnin ja allekirjoitusprosessin lis\u00e4ksi. Seuraavat k\u00e4yt\u00e4nn\u00f6t ovat alan standardeja vuoden 2026 suosituksiin perustuen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Avainten kierr\u00e4tys:<\/strong> Rotoi ECDSA-avaimet s\u00e4\u00e4nn\u00f6llisesti. TLS-sertifikaateille suositellaan enint\u00e4\u00e4n 1 vuoden voimassaoloaikaa (Let&#8217;s Encrypt k\u00e4ytt\u00e4\u00e4 90 p\u00e4iv\u00e4\u00e4). JWT-avaimille suositellaan kierr\u00e4tyst\u00e4 6-12 kuukauden v\u00e4lein. Toteuta JWKS-p\u00e4\u00e4tepiste, jotta asiakkaat voivat automaattisesti noutaa uudet julkiset avaimet ilman palvelukatkosta.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>K\u00e4yt\u00e4 P-256:ta oletuksena:<\/strong> P-256 on turvallinen, nopea ja yhteensopiva kaikkien modernien j\u00e4rjestelmien kanssa. P-384 ja P-521 ovat tarpeellisia vain tiukkimman turvallisuusluokituksen sovelluksiin, joissa kvanttilaskentaresistanssi on t\u00e4rkeint\u00e4. Valitse k\u00e4yr\u00e4 sovelluksesi turvallisuusvaatimusten perusteella, ei &#8220;suurempi on aina parempi&#8221; -periaatteella.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u00c4l\u00e4 toteuta omaa kryptografiaa:<\/strong> K\u00e4yt\u00e4 aina Node.js:n sis\u00e4\u00e4nrakennettua <code>node:crypto<\/code>-moduulia tai vakiintuneita kirjastoja. Oman ECDSA-toteutuksen kirjoittaminen on eritt\u00e4in vaarallista, koska pienikin virhe nonce-generoinnissa tai pistearitmeetiikassa voi johtaa yksityisen avaimen paljastumiseen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Validoi allekirjoitukset aina palvelimella:<\/strong> \u00c4l\u00e4 luota asiakaspuolen allekirjoitusvalidointiin yksin. Palvelin tulee aina vahvistaa allekirjoituksen aitous, vaikka asiakas tarkistaisi sen my\u00f6s omalta puoleltaan.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">NIST FIPS 186-5 -standardi osoitteessa <a href=\"https:\/\/csrc.nist.gov\/publications\/detail\/fips\/186\/5\/final\" target=\"_blank\" rel=\"noopener\">csrc.nist.gov\/publications\/detail\/fips\/186\/5\/final<\/a> on ensisijainen viite ECDSA-k\u00e4yr\u00e4n valinnassa viranomaisk\u00e4ytt\u00f6\u00f6n. Suomessa Kyberturvallisuuslain 124\/2025 noudattavissa j\u00e4rjestelmiss\u00e4 P-256 tai P-384 on suositeltava minimitaso kryptografisille allekirjoituksille.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vianmaaritys-8-yleista-ongelmaa\">Vianm\u00e4\u00e4ritys: 8 yleist\u00e4 ongelmaa<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ECDSA-toteutuksissa t\u00f6rm\u00e4\u00e4 toistuvasti samoihin ongelmiin. T\u00e4ss\u00e4 kattava lista ratkaisuineen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 1: &#8220;error:1E08010C:DECODER routines::unsupported&#8221;<\/strong><br>Syy: Yritit ladata yksityist\u00e4 avainta v\u00e4\u00e4r\u00e4ll\u00e4 formaatilla tai avain on vahingoittunut. Ratkaisu: Tarkista, ett\u00e4 PEM-tiedosto alkaa <code>-----BEGIN PRIVATE KEY-----<\/code> (PKCS8) eik\u00e4 <code>-----BEGIN EC PRIVATE KEY-----<\/code> (SEC1, vanhempi formaatti). Node.js suosii PKCS8-formaattia, ja <code>crypto.generateKeyPairSync<\/code> tuottaa PKCS8:aa oletuksena kun k\u00e4ytet\u00e4\u00e4n <code>type: 'pkcs8'<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 2: verify() palauttaa aina false<\/strong><br>Syy: Yleisimmin v\u00e4\u00e4r\u00e4 <code>dsaEncoding<\/code>. Allekirjoitus luotiin DER-formaatissa mutta vahvistus odottaa IEEE P1363 -formaattia tai p\u00e4invastoin. Ratkaisu: Lis\u00e4\u00e4 <code>dsaEncoding: 'der'<\/code> sek\u00e4 sign.sign()- ett\u00e4 verify.verify()-kutsuihin. Tai j\u00e4t\u00e4 se pois molemmista, jolloin oletuksena k\u00e4ytet\u00e4\u00e4n DER:\u00e4\u00e4 molemmissa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 3: &#8220;JsonWebTokenError: invalid algorithm&#8221;<\/strong><br>Syy: Yrit\u00e4t k\u00e4ytt\u00e4\u00e4 EC-avainta HS256-algoritmin kanssa tai RSA-avainta ES256:n kanssa. Ratkaisu: ES256 vaatii EC-avaimen P-256-k\u00e4yr\u00e4ll\u00e4. Luo avain <code>namedCurve: 'prime256v1'<\/code> -parametrilla ja k\u00e4yt\u00e4 <code>algorithm: 'ES256'<\/code> jwt.sign()-kutsussa. ES384 vaatii P-384-k\u00e4yr\u00e4n, ES512 vaatii P-521-k\u00e4yr\u00e4n.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 4: &#8220;Error: unknown group name secp256k1&#8221;<\/strong><br>Syy: OpenSSL-versio ei tue secp256k1:\u00e4\u00e4 tai nimi on kirjoitettu v\u00e4\u00e4rin. Ratkaisu: Tarkista <code>openssl ecparam -list_curves | grep secp256k1<\/code>. Node.js:n virallisissa binary-julkaisuissa secp256k1 on tuettu, mutta joissakin Linux-jakeluissa OpenSSL on k\u00e4\u00e4nnetty ilman sit\u00e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 5: Salattu PEM-avain ei avaudu<\/strong><br>Syy: V\u00e4\u00e4r\u00e4 salasana tai salasanaa ei annettu. Ratkaisu: Kun avain on salattu, latauksessa t\u00e4ytyy antaa passphrase: <code>crypto.createPrivateKey({ key: pem, passphrase: 'salasana' })<\/code>. Tarkista my\u00f6s, ett\u00e4 passphrase on t\u00e4sm\u00e4lleen sama merkkijono jolla salaus tehtiin, mukaan lukien isot\/pienet kirjaimet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 6: Allekirjoitus vaihtelee joka kerta<\/strong><br>Syy: ECDSA on oletusarvoinen ei-deterministinen. T\u00e4m\u00e4 on odotettua ja oikeaa toimintaa, koska algoritmi k\u00e4ytt\u00e4\u00e4 satunnaista nonce-arvoa. Ratkaisu: T\u00e4m\u00e4 on normaali toiminta. Jos tarvitset deterministisi\u00e4 allekirjoituksia testaukseen tai auditointiin, k\u00e4yt\u00e4 Ed25519:\u00e4\u00e4 tai RFC 6979 -yhteensopivaa kirjastoa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 7: &#8220;error:100AE081:elliptic curve routines::group2pkparameters failure&#8221;<\/strong><br>Syy: K\u00e4yr\u00e4 ei t\u00e4sm\u00e4\u00e4 avaimen sis\u00e4isen k\u00e4yr\u00e4n kanssa. Ratkaisu: Tarkista avaimen k\u00e4yr\u00e4: <code>node -e \"const k = require('crypto').createPrivateKey(require('fs').readFileSync('avain.pem')); console.log(k.asymmetricKeyDetails)\"<\/code>. Varmista, ett\u00e4 namedCurve vastaa sit\u00e4 mit\u00e4 k\u00e4yt\u00e4t allekirjoituksessa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ongelma 8: Suorituskyky on huono suurilla tiedostoilla<\/strong><br>Syy: Koko tiedoston lataaminen muistiin ennen allekirjoitusta kuluttaa muistia ja aikaa. Ratkaisu: K\u00e4yt\u00e4 streaming-rajapintaa: <code>const sign = crypto.createSign('sha256'); readStream.pipe(sign); sign.on('finish', () => { const sig = sign.sign(key); });<\/code>. N\u00e4in tiedoston tiivistys tapahtuu virtauksena ilman koko tiedoston lataamista muistiin kerralla.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"edistyneet-tekniikat\">Edistyneet tekniikat<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Kun perusvaiheet ovat hallussa, seuraavat edistyneet tekniikat parantavat ECDSA-toteutuksesi turvallisuutta, yhteensopivuutta ja yll\u00e4pidett\u00e4vyytt\u00e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Avainten luominen OpenSSL-komentorivilt\u00e4:<\/strong> Voit luoda ECDSA-avaimet my\u00f6s OpenSSL:ll\u00e4, mik\u00e4 on hy\u00f6dyllist\u00e4 skriptauksessa ja CI\/CD-putkissa. Komento <code>openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem<\/code> luo PKCS8-formaatissa olevan P-256-yksityisen avaimen. Julkinen avain saadaan komennolla <code>openssl pkey -in private.pem -pubout -out public.pem<\/code>. Node.js voi lukea suoraan OpenSSL:n tuottamat PEM-tiedostot ilman muunnoksia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>JWKS-p\u00e4\u00e4tepiste avainten julkaisemiseen:<\/strong> Tuotantosovelluksissa julkiset avaimet tulisi julkaista JWKS (JSON Web Key Set) -p\u00e4\u00e4tepisteen\u00e4, josta asiakkaat voivat automaattisesti noutaa uudet avaimet avainrotaation yhteydess\u00e4. Toteuta <code>GET \/.well-known\/jwks.json<\/code> -p\u00e4\u00e4tepiste, joka palauttaa avaimet JWK-muodossa. Node.js:n <code>crypto.createPublicKey(pem).export({ format: 'jwk' })<\/code> tuottaa JWK-formaatin automaattisesti ilman lis\u00e4kirjastoja.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Avaintunnisteet avainrotaatioon:<\/strong> K\u00e4yt\u00e4 <code>kid<\/code>-kentti\u00e4 JWT-otsikossa osoittamaan, mill\u00e4 avaimella token on allekirjoitettu. T\u00e4m\u00e4 mahdollistaa useiden aktiivisten avainten k\u00e4yt\u00f6n samanaikaisesti, mik\u00e4 on kriittist\u00e4 sujuvan avainrotaation toteuttamiselle ilman palvelukatkosta. Pid\u00e4 kaksi rinnakkaista aktiivista avainta siirtym\u00e4vaiheessa: vanha vahvistaa vanhat tokenit, uusi allekirjoittaa uudet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Web Crypto API -vaihtoehto:<\/strong> Node.js 16+ sis\u00e4lt\u00e4\u00e4 Web Crypto API:n (<code>globalThis.crypto.subtle<\/code>), joka on selain-yhteensopiva rajapinta. T\u00e4m\u00e4 mahdollistaa saman koodin k\u00e4yt\u00f6n sek\u00e4 Node.js:ss\u00e4 ett\u00e4 selaimessa: <code>crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify'])<\/code>. Web Crypto API on asynkroninen, mik\u00e4 sopii paremmin Node.js:n tapahtumapohjaiseen arkkitehtuuriin kuin synkroniset <code>generateKeyPairSync<\/code>-kutsut. Lue lis\u00e4\u00e4 <a href=\"\/fi\/webcrypto-api-nodejs\/\">Node.js WebCrypto API -oppaastamme<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Allekirjoitusten tallentaminen tietokantaan:<\/strong> Base64url-enkoodaus on suositeltava formaatti allekirjoitusten tallentamiseen tietokantaan tai siirt\u00e4miseen HTTP-otsikoissa. Muunna allekirjoitus Base64url-muotoon: <code>signature.toString('base64').replace(\/\\+\/g, '-').replace(\/\\\/\/g, '_').replace(\/=\/g, '')<\/code>. T\u00e4m\u00e4 tuottaa URL-turvallisen merkkijonon ilman t\u00e4yt\u00f6smerkej\u00e4, joka toimii suoraan JWT-tokenin allekirjoitusosana.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"taydellinen-projekti-tiedostojen-allekirjoitusjarjestelma\">T\u00e4ydellinen projekti: Tiedostojen allekirjoitusj\u00e4rjestelm\u00e4<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Kootaan kaikki vaiheet yhteen toimivaksi tiedostojen allekirjoitusj\u00e4rjestelm\u00e4ksi, joka luo, allekirjoittaa ja vahvistaa tiedostoja ECDSA P-256:lla. T\u00e4m\u00e4 on suoraan tuotantoon soveltuva pohjarakenne, jota voit laajentaa omien tarpeidesi mukaan.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/file-signer.js - T\u00e4ydellinen tiedostojen allekirjoitusj\u00e4rjestelm\u00e4\nconst crypto = require('node:crypto');\nconst fs = require('node:fs');\nconst path = require('node:path');\n\nclass ECDSAFileSigner {\n  constructor(curve = 'prime256v1') {\n    this.curve = curve;\n    this.hashAlgorithm = curve === 'secp521r1' ? 'sha512' :\n                         curve === 'secp384r1' ? 'sha384' : 'sha256';\n  }\n\n  generateKeyPair(outputDir = '.') {\n    const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n      namedCurve: this.curve,\n      publicKeyEncoding: { type: 'spki', format: 'pem' },\n      privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n    });\n\n    const privPath = path.join(outputDir, 'signing-key.pem');\n    const pubPath = path.join(outputDir, 'signing-key.pub.pem');\n    fs.writeFileSync(privPath, privateKey, { mode: 0o600 });\n    fs.writeFileSync(pubPath, publicKey);\n    console.log(`Avainpari luotu k\u00e4yr\u00e4ll\u00e4 ${this.curve}`);\n    return { privateKey, publicKey };\n  }\n\n  signFile(filePath, privateKeyPem) {\n    const fileData = fs.readFileSync(filePath);\n    const sign = crypto.createSign(this.hashAlgorithm);\n    sign.update(fileData);\n    const signature = sign.sign({ key: privateKeyPem, dsaEncoding: 'der' });\n\n    const metadata = {\n      algorithm: 'ECDSA',\n      curve: this.curve,\n      hash: this.hashAlgorithm,\n      encoding: 'der',\n      timestamp: new Date().toISOString(),\n      fileSize: fileData.length,\n      signature: signature.toString('base64')\n    };\n\n    const sigPath = filePath + '.sig';\n    fs.writeFileSync(sigPath, JSON.stringify(metadata, null, 2));\n    console.log(`Allekirjoitettu: ${sigPath}`);\n    return sigPath;\n  }\n\n  verifyFile(filePath, sigPath, publicKeyPem) {\n    const fileData = fs.readFileSync(filePath);\n    const metadata = JSON.parse(fs.readFileSync(sigPath, 'utf8'));\n    const signature = Buffer.from(metadata.signature, 'base64');\n\n    const verify = crypto.createVerify(metadata.hash);\n    verify.update(fileData);\n    const isValid = verify.verify(\n      { key: publicKeyPem, dsaEncoding: metadata.encoding },\n      signature\n    );\n\n    console.log(`Tiedosto: ${filePath}`);\n    console.log(`Algoritmi: ${metadata.algorithm} ${metadata.curve} + ${metadata.hash.toUpperCase()}`);\n    console.log(`Allekirjoitusaika: ${metadata.timestamp}`);\n    console.log(`Kelpaa: ${isValid ? 'KYLL\u00c4' : 'EI'}`);\n    return isValid;\n  }\n}\n\n\/\/ K\u00e4ytt\u00f6esimerkki\nconst signer = new ECDSAFileSigner('prime256v1');\nconst { privateKey, publicKey } = signer.generateKeyPair('src\/keys');\n\nconst testFile = 'src\/testdoc.txt';\nfs.writeFileSync(testFile, 'S\u00e4hk\u00f6isesti allekirjoitettu asiakirja\\nP\u00e4iv\u00e4m\u00e4\u00e4r\u00e4: 2026-06-19');\n\nconst sigFile = signer.signFile(testFile, privateKey);\nconsole.log('\\n--- Vahvistus (alkuper\u00e4inen) ---');\nsigner.verifyFile(testFile, sigFile, publicKey);\n\n\/\/ Testaa manipulointi\nfs.appendFileSync(testFile, '\\nManipuloitu lis\u00e4ys');\nconsole.log('\\n--- Vahvistus (manipuloitu) ---');\nsigner.verifyFile(testFile, sigFile, publicKey);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">T\u00e4m\u00e4 t\u00e4ydellinen projekti osoittaa ECDSA:n k\u00e4yt\u00e4nn\u00f6n sovelluksen: tiedostojen eheyden varmistamisen. Allekirjoitusmetadata tallennetaan JSON-formaatissa, mik\u00e4 tekee siit\u00e4 helposti tarkistettavan ja yhteensopivan muiden j\u00e4rjestelmien kanssa. Sama l\u00e4hestymistapa toimii ohjelmistojulkaisujen allekirjoittamiseen, API-pyynt\u00f6jen todentamiseen ja asiakirjojen s\u00e4hk\u00f6iseen allekirjoittamiseen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"avainten-rotaatio-tuotannossa\">Avainten rotaatio tuotannossa<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avainten rotaatio on yksi t\u00e4rkeimmist\u00e4 k\u00e4yt\u00e4nn\u00f6ist\u00e4 ECDSA-j\u00e4rjestelmien yll\u00e4pidossa. Oikein toteutettuna rotaatio ei aiheuta palvelukatkosta, koska vanhat tokenit pysyv\u00e4t voimassa kunnes ne vanhentuvat luonnollisesti. T\u00e4ss\u00e4 on k\u00e4yt\u00e4nn\u00f6n strategia avainrotaatiolle JWT-pohjaisessa sovelluksessa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rotaatiostrategia perustuu kolmeen vaiheeseen. Ensimm\u00e4isess\u00e4 vaiheessa luot uuden avainparin ja julkaiset sen JWKS-p\u00e4\u00e4tepisteess\u00e4 vanhan avaimen rinnalle. Molemmat avaimet ovat aktiivisia samanaikaisesti, ja JWT-otsikon <code>kid<\/code>-kentt\u00e4 kertoo validaattorille, kumpaa avainta k\u00e4ytet\u00e4\u00e4n vahvistukseen. Toisessa vaiheessa uudet tokenit allekirjoitetaan uudella avaimella, mutta vanhat tokenit validoidaan edelleen vanhalla avaimella. Kolmannessa vaiheessa, kun kaikki vanhat tokenit ovat vanhentuneet (tyypillisesti 1-24 tuntia riippuen tokenin elini\u00e4st\u00e4), vanha avain poistetaan JWKS-p\u00e4\u00e4tepisteest\u00e4.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Automaattisen avainrotaation toteuttaminen Node.js:ss\u00e4 on suoraviivaista. Tallenna avaimet tietokantaan tai Redis-v\u00e4limuistiin yhdess\u00e4 metadata-tietojen kuten luomisajan ja k\u00e4yt\u00f6st\u00e4poistoajan kanssa. Rotaatioskripti luo uuden avainparin, merkitsee vanhan avaimen &#8220;poistumassa&#8221;-tilaan ja p\u00e4ivitt\u00e4\u00e4 JWKS-p\u00e4\u00e4tepisteen vastauksen. Kun poistumisaika saavutetaan, avain poistetaan pysyv\u00e4sti tallennustilasta.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avainrotaation laiminly\u00f6nnill\u00e4 on vakavia seurauksia. Jos yksityinen avain vaarantuu, kaikki sill\u00e4 allekirjoitetut tokenit voivat paljastaa v\u00e4\u00e4rink\u00e4ytt\u00f6\u00f6n. T\u00e4ll\u00f6in sinun t\u00e4ytyy tehd\u00e4 h\u00e4t\u00e4rotaatio: luo uusi avainpari, poista vanha avain v\u00e4litt\u00f6m\u00e4sti, ja mit\u00e4t\u00f6i kaikki olemassa olevat tokenit pakottamalla k\u00e4ytt\u00e4j\u00e4t kirjautumaan uudelleen. H\u00e4t\u00e4rotaation protokollan tulisi olla dokumentoitu ja testattu ennen kuin sit\u00e4 tarvitaan kriisitilanteessa.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ecdsa-vs-muut-allekirjoitusalgoritmit\">ECDSA vs muut allekirjoitusalgoritmit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">K\u00e4yr\u00e4n ja algoritmin valinta riippuu sovelluksen tarpeista, yhteensopivuusvaatimuksista ja suorituskykyprioriteeteista. T\u00e4ss\u00e4 selke\u00e4 vertailu yleisimmist\u00e4 vaihtoehdoista.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Algoritmi<\/th><th>Allekirjoitusnopeus<\/th><th>Allekirjoituksen koko<\/th><th>K\u00e4ytt\u00f6kohteet<\/th><th>Suositus 2026<\/th><\/tr><\/thead><tbody><tr><td>ECDSA P-256<\/td><td>~45 000 ops\/s<\/td><td>70-72 tavua (DER)<\/td><td>TLS, JWT ES256, X.509<\/td><td>Hyv\u00e4 yleisk\u00e4ytt\u00f6\u00f6n<\/td><\/tr><tr><td>Ed25519<\/td><td>~120 000 ops\/s<\/td><td>64 tavua<\/td><td>SSH, Signal, TLS 1.3<\/td><td>Paras uusiin sovelluksiin<\/td><\/tr><tr><td>RSA-2048<\/td><td>~5 000 ops\/s<\/td><td>256 tavua<\/td><td>Vanha infrastruktuuri<\/td><td>V\u00e4lt\u00e4 uusissa projekteissa<\/td><\/tr><tr><td>RSA-4096<\/td><td>~800 ops\/s<\/td><td>512 tavua<\/td><td>Korkean turvallisuuden PKI<\/td><td>V\u00e4lt\u00e4, k\u00e4yt\u00e4 P-384:\u00e4\u00e4<\/td><\/tr><tr><td>ECDSA secp256k1<\/td><td>~40 000 ops\/s<\/td><td>70-72 tavua (DER)<\/td><td>Bitcoin, Ethereum<\/td><td>Vain lohkoketjuihin<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Ed25519 on vuoden 2026 paras valinta uusiin sovelluksiin, joissa ei ole erityist\u00e4 vaatimusta NIST-k\u00e4yrille. ECDSA on parempi valinta silloin, kun tarvitset X.509-sertifikaattiyhteensopivuutta, JWT-standardien mukaisia ES256\/ES384-algoritmeja tai Bitcoin\/Ethereum-yhteensopivuutta secp256k1-k\u00e4yr\u00e4ll\u00e4. Lue vertailumme <a href=\"\/fi\/ed25519-allekirjoitus-nodejs\/\">Ed25519-allekirjoituksista Node.js:ss\u00e4<\/a> l\u00f6yt\u00e4\u00e4ksesi lis\u00e4\u00e4 tietoa EdDSA-vaihtoehdosta. Kryptografisia hajautusfunktioita k\u00e4sittelemme syv\u00e4llisesti <a href=\"\/fi\/blake3-hash-nodejs\/\">BLAKE3-hajautus Node.js:ss\u00e4 -oppaassamme<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"usein-kysytyt-kysymykset\">Usein kysytyt kysymykset<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Mik\u00e4 on ECDSA:n ja Ed25519:n t\u00e4rkein ero?<\/strong><br>ECDSA k\u00e4ytt\u00e4\u00e4 NIST-k\u00e4yri\u00e4 (P-256, P-384, P-521) Weierstrass-muodossa ja vaatii erillisen tiivisteoperaation ennen allekirjoitusta. Ed25519 k\u00e4ytt\u00e4\u00e4 Edwards25519-k\u00e4yr\u00e4\u00e4 ja allekirjoittaa raakadatan suoraan. Ed25519 on noin 2-3 kertaa nopeampi ja vastustuskykyisempi sivukanava-hy\u00f6kk\u00e4yksille. ECDSA:lla on parempi yhteensopivuus vanhojen j\u00e4rjestelmien ja X.509-sertifikaattien kanssa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Mik\u00e4 ECDSA-k\u00e4yr\u00e4 on turvallisin?<\/strong><br>P-521 tarjoaa korkeimman turvallisuustason (256-bittinen), mutta P-256 riitt\u00e4\u00e4 k\u00e4yt\u00e4nn\u00f6ss\u00e4 kaikille sovelluksille vuoteen 2030 ja todenn\u00e4k\u00f6isesti pidemm\u00e4lle. NIST suosittelee P-256:ta tai P-384:\u00e4\u00e4 yleisk\u00e4ytt\u00f6\u00f6n. P-521:\u00e4 suositellaan vain kaikista korkeimmille turvallisuusvaatimuksille, kuten pitk\u00e4aikaisille asiakirja-allekirjoituksille, joiden on oltava voimassa vuosikymmenien kuluttua.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Voiko ECDSA:n yksityinen avain vuotaa?<\/strong><br>Kyll\u00e4, mutta vain erityistilanteissa: nonce-arvon k uudelleenk\u00e4ytt\u00f6 tai heikko satunnaislukugeneraattori voi paljastaa yksityisen avaimen algebrallisella laskennalla. Node.js:n virallinen toteutus k\u00e4ytt\u00e4\u00e4 OpenSSL:n kryptografista satunnaislukugeneraattoria, joka on turvallinen. \u00c4l\u00e4 koskaan toteuta omaa k-arvon generointia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Miten ECDSA toimii TLS-sertifikaateissa?<\/strong><br>TLS 1.3 suosii ECDSA-sertifikaatteja, koska niiden pienempi koko nopeuttaa TLS-k\u00e4ttely\u00e4. Let&#8217;s Encrypt my\u00f6nt\u00e4\u00e4 oletuksena RSA-sertifikaatteja mutta tukee my\u00f6s ECDSA:ta. ECDSA-sertifikaattipyynt\u00f6 (CSR): <code>openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -keyout key.pem -out csr.pem -nodes<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Onko ECDSA kvanttiresistentti?<\/strong><br>Ei. Kaikki elliptisiin k\u00e4yriin perustuvat algoritmit ovat haavoittuvaisia Shorin algoritmille, jota riitt\u00e4v\u00e4n suuri kvanttitietokone voisi ajaa. NIST standardisoi kvanttiturvallisia algoritmeja (CRYSTALS-Dilithium allekirjoituksille), jotka korvaavat ECDSA:n pitk\u00e4ll\u00e4 aikav\u00e4lill\u00e4. Lue lis\u00e4\u00e4 <a href=\"\/fi\/post-quantum-cryptography-2026\/\">postkvanttikryptografiasta<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Miksi allekirjoitukseni ei kelpaa eri kielell\u00e4 toteutetussa j\u00e4rjestelm\u00e4ss\u00e4?<\/strong><br>Todenn\u00e4k\u00f6isin syy on eri dsaEncoding-formaatti. Java ja monet muut kielet k\u00e4ytt\u00e4v\u00e4t DER-formaattia oletuksena, mutta jotkut JavaScript-kirjastot odottavat IEEE P1363 -formaattia. Tarkista kohdekirjaston dokumentaatio ja varmista, ett\u00e4 molemmat j\u00e4rjestelm\u00e4t k\u00e4ytt\u00e4v\u00e4t samaa formaattia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pit\u00e4isik\u00f6 k\u00e4ytt\u00e4\u00e4 node:crypto vai Web Crypto API:ta?<\/strong><br>Node.js-sovelluksiin, jotka eiv\u00e4t tarvitse selainyhteensopivuutta, <code>node:crypto<\/code> on suositeltavampi sen synkronisten API-kutsujen ja laajemman ominaisuusjoukon vuoksi. Jos kirjoitat isomorfista koodia, joka toimii sek\u00e4 Node.js:ss\u00e4 ett\u00e4 selaimessa, k\u00e4yt\u00e4 Web Crypto API:ta (<code>globalThis.crypto.subtle<\/code>). Node.js 18+ tukee t\u00e4ytt\u00e4 Web Crypto API:ta ilman erillisi\u00e4 paketteja.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Miten testaan ECDSA-toteutukseni kattavasti?<\/strong><br>Testaa v\u00e4hint\u00e4\u00e4n: 1) allekirjoitus onnistuu oikealla avaimella, 2) vahvistus onnistuu oikealla julkisella avaimella, 3) vahvistus ep\u00e4onnistuu v\u00e4\u00e4r\u00e4ll\u00e4 viestill\u00e4, 4) vahvistus ep\u00e4onnistuu v\u00e4\u00e4r\u00e4ll\u00e4 julkisella avaimella, 5) vahvistus ep\u00e4onnistuu muutetulla allekirjoituksella, 6) suuret tiedostot toimivat streaming-rajapinnalla, 7) avainten tallennus ja lataaminen toimii oikein salattuina avaimina. Lis\u00e4\u00e4 yksikk\u00f6testit kaikille virheskenaarioille, joita kuvataan vianm\u00e4\u00e4ritys-osiossa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Miten ECDSA-allekirjoitukset eroavat s\u00e4hk\u00f6isest\u00e4 allekirjoituksesta?<\/strong><br>ECDSA on kryptografinen primitiivi, s\u00e4hk\u00f6inen allekirjoitus on juridinen ja tekninen kokonaisuus. Euroopan unionin eIDAS-asetus m\u00e4\u00e4rittelee kolme s\u00e4hk\u00f6isen allekirjoituksen tasoa: tavallinen, edistyksellinen (AdES) ja hyv\u00e4ksytty (QES). ECDSA P-256 tai P-384 muodostaa teknisen perustan edistykselliselle s\u00e4hk\u00f6iselle allekirjoitukselle, mutta juridisesti p\u00e4tev\u00e4 hyv\u00e4ksytty allekirjoitus vaatii lis\u00e4ksi akkreditoidun luottamuspalveluntarjoajan my\u00f6nt\u00e4m\u00e4n sertifikaatin. Suomessa hyv\u00e4ksyttyj\u00e4 s\u00e4hk\u00f6isi\u00e4 allekirjoituksia my\u00f6nt\u00e4\u00e4 V\u00e4est\u00f6rekisterikeskus DVV:n kautta.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Miksi ECDSA:ssa tarvitaan niin tarkka hajautusalgoritmin valinta?<\/strong><br>ECDSA:n turvallisuustaso on matematiikassa kiinte\u00e4sti sidottu sek\u00e4 k\u00e4yr\u00e4n bittipituuteen ett\u00e4 k\u00e4ytettyyn hajautusfunktioon. Jos k\u00e4yr\u00e4 tarjoaa 128-bittisen turvallisuuden (P-256) mutta hajautusfunktio tuottaa 256-bittisen tiivisteen (SHA-256), kokonaisturvallisuus pysyy 128 bitiss\u00e4, koska k\u00e4yr\u00e4 on pullonkaula. Vastaavasti, jos k\u00e4yr\u00e4 tarjoaa 256-bittisen turvallisuuden (P-521) mutta k\u00e4yt\u00e4t SHA-256:ta (128-bittinen turvallisuus), kokonaisturvallisuus tippuu 128 bittiin. T\u00e4m\u00e4n vuoksi NIST FIPS 186-5 suosittelee parittamaan P-256:n SHA-256:n, P-384:n SHA-384:n ja P-521:n SHA-512:n kanssa, jotta turvallisuustasot vastaavat toisiaan molemmissa komponenteissa.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"aiheeseen-liittyvaa\">Aiheeseen liittyv\u00e4\u00e4<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/fi\/ed25519-allekirjoitus-nodejs\/\">Ed25519 Node.js:ss\u00e4: 10 vaihetta, 30 min<\/a> (Edwards-k\u00e4yr\u00e4n vaihtoehto ECDSA:lle)<\/li>\n<li><a href=\"\/fi\/webcrypto-api-nodejs\/\">Node.js WebCrypto API: 12 vaihetta, 35 min<\/a> (selaimen kanssa yhteensopiva kryptografia)<\/li>\n<li><a href=\"\/fi\/jwt-todennus-nodejs\/\">JWT-todennus Node.js:ss\u00e4: 12 vaihetta, 40 min<\/a> (ES256-tokenien k\u00e4yt\u00e4nn\u00f6n toteutus)<\/li>\n<li><a href=\"\/fi\/hmac-node-js\/\">HMAC Node.js:ss\u00e4: 10 vaihetta, 30 min<\/a> (symmetrinen viestin todentaminen)<\/li>\n<li><a href=\"\/fi\/blake3-hash-nodejs\/\">BLAKE3-hajautus Node.js:ss\u00e4: 10 vaihetta, 30 min<\/a> (nopein kryptografinen hajautusfunktio)<\/li>\n<li><a href=\"\/cryptography\/\">Kryptografia-opas<\/a> (kattava johdanto kryptografian perusteisiin)<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>ECDSA (Elliptic Curve Digital Signature Algorithm) on elliptisiin k\u00e4yriin perustuva digitaalinen allekirjoitusalgoritmi, joka tarjoaa RSA-2048:aa vastaavan turvallisuuden murto-osalla avainkoosta. Node.js sis\u00e4lt\u00e4\u00e4 natiivin ECDSA-tuen node:crypto-moduulin kautta, ja se kattaa NIST P-256-,\u2026<\/p>\n","protected":false},"author":8,"featured_media":120,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,2],"tags":[],"class_list":["post-119","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-10","category-cryptography"],"_links":{"self":[{"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/posts\/119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/users\/8"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/comments?post=119"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/posts\/119\/revisions"}],"predecessor-version":[{"id":121,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/posts\/119\/revisions\/121"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/media\/120"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/media?parent=119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/categories?post=119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/fi\/wp-json\/wp\/v2\/tags?post=119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}