Node.js WebCrypto API muutti turvallisen kryptografian kirjoittamisen palvelimella vuodesta 2020 alkaen. globalThis.crypto.subtle-rajapinta noudattaa W3C-standardia, joten sama koodi toimii sekä selaimessa että Node.js:ssä. Node.js 24.7.0 lisäsi ChaCha20-Poly1305-salauksen ja SubtleCrypto.supports()-metodin, ja Node.js 24.8.0 toi Argon2-tuen avainjohdannaisiin. Tässä oppaassa käyt läpi 12 konkreettista vaihetta: turvallinen satunnaisluku, SHA-tiivisteet, PBKDF2-avainjohdannainen, AES-GCM-salaus, ECDSA-allekirjoitukset, HKDF, JWK-vienti ja avainten kääriminen. Valmis projekti syntyy 35 minuutissa ilman yhtään npm-pakettia.
WebCrypto API on erityisen ajankohtainen vuonna 2026. EU:n kyberresilienssiasetus (CRA) asettaa syyskuussa 2026 ohjelmistokomponenteille tiukat vaatimukset, mukaan lukien kryptografisten algoritmien dokumentointi ja turvallisten oletusasetusten käyttö. Suomen kyberturvallisuuslaki 124/2025, joka implementoi NIS2-direktiivin, edellyttää teknisten suojatoimien käyttöä kaikilta NIS2:n piirissä olevilta organisaatioilta. Standardoitujen algoritmien käyttö WebCrypto-rajapinnan kautta on konkreettisin tapa täyttää nämä vaatimukset Node.js-sovelluksissa. ENISA:n 2026 kyberturvallisuusosaamiskehys tunnustaa erikseen kryptografian käytännöntoteutuksen kehittäjätason taitona, jonka hallitseminen on keskeinen osa turvallisuusosaamista.
Mitä on Node.js WebCrypto API?
WebCrypto API on W3C:n standardoima kryptografinen rajapinta, joka tuli osaksi Node.js:ää versiossa 15.0.0. Ydin on SubtleCrypto-rajapinta, johon pääsee crypto.subtle-ominaisuuden kautta. Node.js:n virallinen WebCrypto-dokumentaatio kattaa kaikki tuetut algoritmit ja metodit.
WebCrypto eroaa vanhasta node:crypto-moduulista kahdella keskeisellä tavalla. Ensinnäkin se perustuu W3C-standardiin, joten sama koodi toimii selaimessa ja palvelimella ilman muutoksia. Toiseksi se käyttää CryptoKey-objekteja pelkän puskuridatan sijaan: avain on olio, jonka sallitut operaatiot on sidottu sen luontiparametreihin. Tämä tekee avainten väärinkäytöstä vaikeampaa jo ohjelmointitasolla.
Node.js WebCrypto tukee vuonna 2026 seuraavia algoritmiryhmiä: AES-GCM, AES-CBC, AES-CTR, AES-KW (salaus ja avainten kääriminen), RSA-OAEP ja RSA-PSS (epäsymmetrinen salaus ja allekirjoitukset), ECDSA ja ECDH P-256, P-384, P-521 sekä X25519 ja X448 (allekirjoitukset ja avaintenvaihto), PBKDF2 ja HKDF (avainjohdannaiset), SHA-256, SHA-384 ja SHA-512 (tiivisteet), ja versiosta 24.7.0 alkaen ChaCha20-Poly1305 ja AES-OCB. Argon2 lisättiin versiossa 24.8.0.
SubtleCrypto-rajapinta tarjoaa 12 päämetodia: encrypt(), decrypt(), sign(), verify(), digest(), generateKey(), importKey(), exportKey(), wrapKey(), unwrapKey(), deriveKey() ja deriveBits(). Kaikki metodit palauttavat Promise-objekteja, joten ne sopivat luontevasti async/await-syntaksiin. Jokainen metodikutsu on atominen: se joko onnistuu tai heittää poikkeuksen. Osittaisen onnistumisen tilaa ei ole. Tämä tekee virheenkäsittelystä suoraviivaista: try/catch-blokki riittää kattamaan kaikki WebCrypto-virhetilat.
| Ominaisuus | WebCrypto API (crypto.subtle) | node:crypto |
|---|---|---|
| Standardi | W3C WebCrypto API | Node.js-spesifinen |
| Selainyhteensopivuus | Kyllä, sama koodi | Ei |
| Avaintyypit | CryptoKey-objektit | KeyObject tai Buffer |
| Ed25519-tuki | Ei (ei W3C-standardissa) | Kyllä (v15.0.0+) |
| Argon2-tuki | Kyllä (Node.js v24.8.0+) | Ei |
| ChaCha20-Poly1305 | Kyllä (Node.js v24.7.0+) | Kyllä (aiemmin) |
| API-tyyli | Promise-pohjainen | Callback ja Promise |
| Avainten suojaus | extractable: false estää vuodon | Ei suoraa vastaavaa |
Esitietovaatimukset
Tarvitset seuraavat ennen kuin aloitat:
- Node.js 18.x LTS tai uudempi, suositellaan 22.x LTS tai 24.x. Versio 18 on minimivaatimus vakaalle WebCrypto-tuelle. ChaCha20-Poly1305 ja
SubtleCrypto.supports()vaativat version 24.7.0. - npm 9+ tai pnpm tai yarn projektin hallintaan
- Perustiedot JavaScriptistä ja
async/await-syntaksista - Tekstieditori, esimerkiksi VS Code tai Neovim
- Terminaali: Unix-komentorivi tai WSL Windowsilla
| Node.js-versio | WebCrypto-tila | Huomioitavaa |
|---|---|---|
| v15.0.0 | Ensimmäinen julkaisu | Kokeellinen, ei tuotantoon |
| v16.x | Vakaa | X448 lisätty v16.17.0:ssa |
| v18.x LTS | Täysin vakaa | X25519 lisätty v18.4.0:ssa, minimisuositus |
| v20.x LTS | Vakaa | Kaikki keskeiset algoritmit mukana |
| v22.x LTS | Vakaa, suositeltava | Pitkäaikainen tuki 2027 asti |
| v24.7.0+ | Laajennettu | ChaCha20-Poly1305, AES-OCB, supports() |
| v24.8.0+ | Laajennettu | Argon2-avainjohdannainen WebCryptossa |
Vaihe 1 – Projektin alustaminen
Luo uusi hakemisto ja alusta npm-projekti. WebCrypto on sisäänrakennettu Node.js:ään, eikä vaadi ulkoisia riippuvuuksia:
mkdir webcrypto-projekti
cd webcrypto-projekti
npm init -y
Avaa package.json ja lisää "type": "module"-kenttä. Tämä mahdollistaa ylimmän tason await-avainsanan ilman erillistä async-funktiota, mikä tekee koodista selkeämpää oppimisen kannalta:
{
"name": "webcrypto-projekti",
"version": "1.0.0",
"type": "module"
}
Luo pääohjelmatiedosto ja tarkista että WebCrypto toimii:
touch index.js
node -e "console.log(typeof crypto.subtle === 'object' ? 'WebCrypto OK' : 'Ei tuettu')"
# Tulostaa: WebCrypto OK
globalThis.crypto on saatavilla automaattisesti Node.js 18+:ssa. Et tarvitse require– tai import-lausetta. Vanhemmissa versioissa (15.x–17.x) voit käyttää const { webcrypto: crypto } = require('crypto').
Vaihe 2 – Turvallinen satunnaisluku getRandomValues()-metodilla
Kryptografinen satunnaisluku on kaiken turvallisen kryptografian perusta. Math.random() ei koskaan sovi salausavaimiin, alustusvektoreihin tai suolaarvoihin, koska se ei ole kryptografisesti turvallinen. WebCryton crypto.getRandomValues() käyttää käyttöjärjestelmän kryptografista satunnaislukugeneraattoria (/dev/urandom Linuxilla, BCryptGenRandom Windowsilla).
Lisää index.js-tiedostoon:
// Vaihe 2: Turvallinen satunnaisluku
// 32 tavua (256 bittiä) – sopii salausavainmateriaaliksi
const bytes32 = crypto.getRandomValues(new Uint8Array(32));
console.log('Satunnaistavut (hex):', Buffer.from(bytes32).toString('hex'));
// 12 tavun alustusvektori AES-GCM-salaukseen
const iv = crypto.getRandomValues(new Uint8Array(12));
console.log('AES-GCM IV (hex):', Buffer.from(iv).toString('hex'));
// Satunnainen UUID v4
const uuid = crypto.randomUUID();
console.log('UUID:', uuid);
node index.js
# Satunnaistavut (hex): a3f2c1d8e4b59f7a2c3d1e0f8b7a6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0ab
# AES-GCM IV (hex): 9f1a2b3c4d5e6f7a8b9c0d1e
# UUID: 550e8400-e29b-41d4-a716-446655440000
getRandomValues() hyväksyy vain tyypitetyn taulukon (Uint8Array, Uint16Array tai Uint32Array). Maksimikoko on 65 536 tavua yhdellä kutsulla. Jos tarvitset yli 64 kilotavua kerralla, kutsu metodia useammin pienemmissä erissä.
Vaihe 3 – SHA-tiivisteet subtle.digest()-metodilla
WebCryton crypto.subtle.digest() laskee kryptografisia tiivisteitä. Tuetut algoritmit ovat SHA-256, SHA-384 ja SHA-512. SHA-1 ja MD5 eivät ole tuettu WebCrypton kautta, koska ne ovat kryptografisesti murtuneet. Lue lisää tiivistefunktioiden teoriasta BLAKE3-oppaastamme.
Tiivistefunktio ottaa syötteekseen ArrayBuffer– tai TypedArray-dataa. Muunna tekstimerkkijono TextEncoder:lla, joka on globaali sekä selaimessa että Node.js:ssä versiosta 11 alkaen:
// Vaihe 3: SHA-tiivisteet
const enc = new TextEncoder();
const viesti = enc.encode('Hei, WebCrypto!');
const sha256 = await crypto.subtle.digest('SHA-256', viesti);
console.log('SHA-256:', Buffer.from(sha256).toString('hex'));
console.log('SHA-256 pituus:', sha256.byteLength, 'tavua'); // 32
const sha512 = await crypto.subtle.digest('SHA-512', viesti);
console.log('SHA-512:', Buffer.from(sha512).toString('hex'));
console.log('SHA-512 pituus:', sha512.byteLength, 'tavua'); // 64
const sha384 = await crypto.subtle.digest('SHA-384', viesti);
console.log('SHA-384:', Buffer.from(sha384).toString('hex'));
console.log('SHA-384 pituus:', sha384.byteLength, 'tavua'); // 48
SHA-256 tuottaa aina 32 tavua (64 heksadesimaalista merkkiä), SHA-384 tuottaa 48 tavua ja SHA-512 tuottaa 64 tavua. digest() on deterministinen: sama syöte tuottaa aina saman tulosteen. Se sopii datan eheyden tarkistukseen, mutta ei salasanojen tallentamiseen, koska tiivistefunktio on liian nopea raa’an voiman hyökkäyksiä vastaan. Salasanoille käytä PBKDF2:ta tai Argon2:ta seuraavissa vaiheissa.
Vaihe 4 – PBKDF2-avainjohdannainen salasanoista
PBKDF2 (Password-Based Key Derivation Function 2) muuntaa salasanan kryptografiseksi avaimeksi. Se toistaa hajautuslaskennan tuhansia kertoja hidastaen raa’an voiman hyökkäyksiä merkittävästi. OWASP suosittelee vuodelle 2026 vähintään 310 000 iteraatiota SHA-256:n kanssa.
PBKDF2 vaatii kaksi vaihetta: ensin salasana tuodaan importKey():lla “raakamateriaaliksi”, sitten varsinainen avain johdetaan deriveKey():lla. Sama rakenne sallii yhden perusmateriaalin käytön useiden eri avainten johtamiseen:
// Vaihe 4: PBKDF2-avainjohdannainen
const enc = new TextEncoder();
const salasana = 'oikea-hevonen-paristo-hakasulku';
// Satunnainen suola – tallennetaan salatun datan rinnalle
const suola = crypto.getRandomValues(new Uint8Array(16));
// Tuo salasana avainmateriaaliksi
const perusAvain = await crypto.subtle.importKey(
'raw',
enc.encode(salasana),
'PBKDF2',
false, // ei extrahoida
['deriveKey']
);
// Johda 256-bittinen AES-GCM-avain
const avain = await crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: suola,
iterations: 310_000, // OWASP 2026 -suositus
hash: 'SHA-256',
},
perusAvain,
{ name: 'AES-GCM', length: 256 },
false, // avain ei ole extrahoitavissa tuotannossa
['encrypt', 'decrypt']
);
console.log('PBKDF2-avain luotu onnistuneesti');
console.log('Suola (tallenna tämä!):', Buffer.from(suola).toString('hex'));
Suola on tallennettava salatun datan rinnalle, koska sama avain voidaan johtaa vain samalla suolalla ja salasanalla. Suola ei ole salainen, mutta sen on oltava ainutlaatuinen jokaiselle käyttäjälle tai salasanalle. 310 000 iteraatiota hidastaa PBKDF2-laskentaa tarkoituksellisesti, noin 150-300 millisekuntia tavallisella palvelimella. Raa’an voiman hyökkääjä hidastuu samassa suhteessa. Jos tarvitset muistivaikean vaihtoehdon, käytä Argon2:ta Node.js 24.8.0+:ssa.
PBKDF2:n SHA-256:n 310 000 iteraatiota on OWASP:n 2026-suositus, koska se tarjoaa riittävän hitauden nykyisillä GPU-hyökkäyksillä. Vertailun vuoksi: SHA-512:lla 120 000 iteraatiota tarjoaa vastaavan suojaustason, ja SHA-1:n kanssa OWASP suosittelee 1 300 000 iteraatiota. Iteraatiomäärä kannattaa tehdä konfiguroitavaksi, jotta se voidaan nostaa tulevaisuudessa ilman salasanojen uudelleenhajautusta: tallenna iteraatiomäärä suolan rinnalle ja käytä tallennettua arvoa purkaessa. Tämä mahdollistaa asteittaisen siirtymisen korkeampiin iteraatiomääriin kirjautumisen yhteydessä.
Argon2:ta WebCryton kautta (Node.js 24.8.0+) harkitaan tilanteissa, joissa PBKDF2:n suorituskyky ei riitä hyökkäyksiä vastaan: Argon2id on muistivaikea, joten GPU-hyökkäys on paljon kalliimpi kuin PBKDF2:lla. Argon2id-parametrit Node.js WebCryptossa noudattavat OWASP:n 2026-suositusta: muistiparametri 19 456 kilotavua, aikalaskelma 2, rinnakkaisuus 1 vähintään. Nämä vastaavat bcrypt-laskennan kovuuskerrointa 12 turvallisuustasoltaan. Lue lisää salasanatiivisteistä istunnonhallintaoppaastamme.
Vaihe 5 – AES-GCM-salaus ja purku
AES-GCM (Advanced Encryption Standard Galois/Counter Mode) on WebCryton suositelluin salausalgorimi vuonna 2026. Se tarjoaa sekä luottamuksellisuuden (salaus) että eheyden varmistuksen (autentikointikoodi), joten erillistä HMAC-laskentaa ei tarvita. 256-bittinen AES-GCM on NIST-sertifioitu ja IANA:n AEAD-parametrirekisterin listattu standardi.
AES-GCM vaatii 12 tavun alustusvektorin (IV). IV:n on oltava ainutlaatuinen jokaiselle salausoperaatiolle samalla avaimella. IV:n toistaminen saman avaimen kanssa vaarantaa sekä luottamuksellisuuden että eheyden. Käytä aina getRandomValues() IV:n generointiin:
// Vaihe 5: AES-GCM-salaus ja purku
// (jatkaa vaiheen 4 avain-muuttujaa)
async function salaaData(avain, teksti) {
const enc = new TextEncoder();
const iv = crypto.getRandomValues(new Uint8Array(12));
const salattu = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
avain,
enc.encode(teksti)
);
// Palauta IV + salattu data yhtenä puskurina
const tulos = new Uint8Array(12 + salattu.byteLength);
tulos.set(iv, 0);
tulos.set(new Uint8Array(salattu), 12);
return tulos;
}
async function puraData(avain, yhdistettyData) {
const iv = yhdistettyData.slice(0, 12);
const ciphertext = yhdistettyData.slice(12);
const plaintext = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
avain,
ciphertext
);
return new TextDecoder().decode(plaintext);
}
const salattuData = await salaaData(avain, 'Tämä on salainen viesti');
console.log('Salattu (base64):', Buffer.from(salattuData).toString('base64'));
console.log('Salattu koko:', salattuData.byteLength, 'tavua');
const palautettu = await puraData(avain, salattuData);
console.log('Purettu:', palautettu);
Salattu (base64): mK3f9aZ1Xp2qR7sT+uV5wX4yZ6aB3cD2eF1gH0iJkLmN...
Salattu koko: 57 tavua
Purettu: Tämä on salainen viesti
Pakettimalla IV salatun datan eteen tallennuksen yhteydessä varmistat, että kaikki puramiseen tarvittava tieto on yhdessä paikassa. AES-GCM:n 16-tavun autentikointikoodi (GCM-tunniste) sisältyy automaattisesti encrypt():n tuottamaan salatekstiin. Jos data on muuttunut tai IV on väärä, decrypt() heittää DOMException-virheen sen sijaan että palauttaisi väärää dataa.
AES-GCM tukee myös valinnaista lisätietokenttää (additionalData, eli AAD), joka lisätään autentikointilaskentaan mutta ei salata. Tämä mahdollistaa salauksen sitomisen kontekstiinsa: voit esimerkiksi sisällyttää tietueId:n tai käyttäjätunnuksen additionalData-kenttään, jolloin salattu data ei kelpaa ilman oikeaa kontekstia. Tietue, jonka additionalData on user:123, ei aukea user:456:n kontekstissa, vaikka avain ja IV olisivat samat. Käytä tätä ominaisuutta tietokantapohjaisissa sovelluksissa estämään salattujen rivien uudelleenkäyttöhyökkäyksiä:
// AES-GCM additionalData-esimerkki
const enc = new TextEncoder();
const kayttajaId = enc.encode('user:123');
const salattuAad = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv, additionalData: kayttajaId },
avain,
enc.encode('Salainen data')
);
// Purku onnistuu vain oikealla kontekstilla
const purettuAad = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv, additionalData: kayttajaId },
avain,
salattuAad
);
console.log(new TextDecoder().decode(purettuAad)); // Salainen data
AES-GCM:n 12-tavun IV on standardin mukainen ja optimaalinen valinta: lyhyempi IV heikentää turvallisuutta ja pidempi IV vaatii ylimääräisen hajautuslaskennan. Jotkut äärimmäisesti suorituskykyä vaativat sovellukset käyttävät laskuria IV:n sijaan (counter-based nonce), mutta tämä vaatii huolellista toteutusta samanaikaisuuden hallitsemiseksi. Satunnainen IV getRandomValues(new Uint8Array(12)):lla on turvallinen oletusvalinta kaikissa tapauksissa, joissa salausmäärä pysyy alle 2^32:ssa samalla avaimella.
Vaihe 6 – HKDF-avainjohdannainen istuntoavaimille
HKDF (HMAC-based Key Derivation Function, RFC 5869) on suunniteltu tilanteisiin, joissa sinulla on jo korkealaatuista avainmateriaalia, kuten Diffie-Hellman-vaihdon tulos tai satunnainen tavu. Siitä halutaan johtaa useita eritarkoituksisia avaimia. PBKDF2 on suunniteltu salasanoille, HKDF avaintenvaihdoille ja olemassa oleville avaimille.
HKDF:n info-parametri on sovelluspesifinen kontekstimerkkijono, joka sitoo johdetun avaimen käyttötarkoitukseensa. Se ei ole salainen, mutta erottaa saman perusmateriaalin eri käyttötarkoituksiin johdetut avaimet toisistaan:
// Vaihe 6: HKDF-avainjohdannainen
const enc = new TextEncoder();
const avainMateriaali = crypto.getRandomValues(new Uint8Array(32));
// Tuo avainmateriaali HKDF:lle
const hkdfPerus = await crypto.subtle.importKey(
'raw',
avainMateriaali,
'HKDF',
false,
['deriveKey']
);
const hkdfSuola = crypto.getRandomValues(new Uint8Array(32));
// Johda AES-GCM-avain viestinnälle
const viestintaAvain = await crypto.subtle.deriveKey(
{
name: 'HKDF',
hash: 'SHA-256',
salt: hkdfSuola,
info: enc.encode('viestintaavain-v1'),
},
hkdfPerus,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
// Johda toinen avain eri käyttötarkoitukseen
const varmuusAvain = await crypto.subtle.deriveKey(
{
name: 'HKDF',
hash: 'SHA-256',
salt: hkdfSuola,
info: enc.encode('varmuusavain-v1'), // eri konteksti -> eri avain
},
hkdfPerus,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
console.log('HKDF-avaimet johdettu onnistuneesti');
Sisällytä info-merkkijonoon versionumero ('v1', 'v2'), jotta avaimet voidaan kierrättää tulevaisuudessa purkamisen mahdollistaminen vanhalla avaimella samalla kun uudet tiedot salataan uudella avaimella.
Vaihe 7 – ECDSA-digitaaliset allekirjoitukset
ECDSA (Elliptic Curve Digital Signature Algorithm) mahdollistaa viestin allekirjoittamisen yksityisellä avaimella ja allekirjoituksen verifioimisen julkisella avaimella. WebCrypto tukee P-256, P-384 ja P-521 -käyriä. P-256:n turvallisuustaso on 128 bittiä, mikä riittää useimpiin sovelluksiin vuoteen 2030 asti NIST:n arvion mukaan.
Tärkeä huomio: Ed25519 ei kuulu W3C WebCrypto -standardiin. Se puuttuu SubtleCrypto-rajapinnasta, koska Ed25519 käyttää PureEdDSA-mallia ilman esihajautusta, mikä ei sovi yhteen WebCryptoAPI:n nykyisen allekirjoitusarkkitehtuurin kanssa. Ed25519 on kuitenkin saatavilla node:crypto-moduulin kautta. Lue Ed25519-oppaastamme lisätietoja tästä vaihtoehdosta.
// Vaihe 7: ECDSA-digitaaliset allekirjoitukset
const enc = new TextEncoder();
// Luo P-256 ECDSA-avainpari
const avainpari = await crypto.subtle.generateKey(
{ name: 'ECDSA', namedCurve: 'P-256' },
true, // extractable: avaimet voidaan viedä JWK:ksi
['sign', 'verify']
);
const viesti = enc.encode('Tämä viesti allekirjoitetaan ECDSA P-256:lla');
// Allekirjoita yksityisellä avaimella
const allekirjoitus = await crypto.subtle.sign(
{ name: 'ECDSA', hash: 'SHA-256' },
avainpari.privateKey,
viesti
);
console.log('Allekirjoitus (hex):', Buffer.from(allekirjoitus).toString('hex'));
console.log('Allekirjoituksen pituus:', allekirjoitus.byteLength, 'tavua'); // 70–72
// Verifioi julkisella avaimella
const kelpaa = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
avainpari.publicKey,
allekirjoitus,
viesti
);
console.log('Allekirjoitus kelvollinen:', kelpaa); // true
// Testaa muutetulla viestillä
const muutettu = enc.encode('Tämä on eri viesti');
const vaaraKelpaa = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
avainpari.publicKey,
allekirjoitus,
muutettu
);
console.log('Muutettu viesti kelvollinen:', vaaraKelpaa); // false
ECDSA-allekirjoituksen pituus P-256:lla vaihtelee 70-72 tavun välillä, koska allekirjoituksen kaksi komponenttia (r ja s) ovat DER-enkoodattuja ja niiden pituus vaihtelee. Jos tarvitset kiinteää 64 tavun allekirjoitusta, harkitse HMAC-SHA256:ta tai Ed25519:ää node:crypto:n kautta.
ECDSA P-256 -avainparin generaatio on nopea: alle millisekunnin nykyaikaisella laitteistolla. Allekirjoittaminen on myös nopea, noin 0,1-0,5 millisekuntia. Verifiointi on hivenen hitaampaa kuin allekirjoittaminen P-256:lla. Vertailuksi: RSA-2048-allekirjoitus kestää allekirjoitusoperaatiossa noin 1-3 millisekuntia ja verifiointi alle millisekunnin. ECDSA P-256 on siis nopeampi allekirjoittamisessa kuin RSA-2048. Kun kyseessä on palvelinkuorma, jossa allekirjoitetaan tuhansia pyyntöjä sekunnissa, P-256-valinta on perusteltu sekä suorituskyvyn että turvallisuuden puolesta.
ECDSA:n käyttö JWT-tokeneiden allekirjoittamiseen on yleinen käyttötapaus. ES256 (ECDSA P-256 SHA-256) on yksi virallisista JWT RFC 7518 -algoritmeista. Se on vaihtoehto HS256:lle (HMAC-SHA256), kun allekirjoitus täytyy voida verifioida julkisella avaimella ilman, että verifikoijalla on pääsy yksityiseen avaimeen. Tämä soveltuu erityisesti mikropalveluarkkitehtuureihin, joissa yksi palvelu allekirjoittaa tokenin ja muut palvelut verifioivat sen omalla julkisella avaimellaan. Lue lisää JWT-todennusoppaastamme.
Vaihe 8 – Avainten vienti ja tuonti JWK-muodossa
JWK (JSON Web Key) on standardoitu muoto kryptografisten avainten esittämiseen JSON-objekteina. WebCrypto tukee avainten vientiä neljässä muodossa: raw (symmetriset avaimet), pkcs8 (yksityiset avaimet), spki (julkiset avaimet) ja jwk. JWK on suositeltavin, koska se sisältää algoritmin metatiedot eikä vaadi erillistä ASN.1-enkoodausta.
Avain on oltava extrahoitavissa (extractable: true) jo luontivaiheessa. Jos loit avaimen false:lla, sitä ei voi enää muuttaa extrahoitavaksi jälkikäteen. Suunnittele avainten elinkaari ennen koodin kirjoittamista:
// Vaihe 8: JWK-vienti ja tuonti
// Vie julkinen avain JWK-muodossa
const julkinenJWK = await crypto.subtle.exportKey('jwk', avainpari.publicKey);
console.log('Julkinen avain (JWK):', JSON.stringify(julkinenJWK, null, 2));
// Tuo avain takaisin JWK:sta
const tuotuJulkinen = await crypto.subtle.importKey(
'jwk',
julkinenJWK,
{ name: 'ECDSA', namedCurve: 'P-256' },
true,
['verify'] // julkinen avain vain verifiointiin
);
// Verifioi alkuperäisellä allekirjoituksella tuodulla avaimella
const toimii = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
tuotuJulkinen,
allekirjoitus,
viesti
);
console.log('Tuotu avain toimii:', toimii); // true
// Vie yksityinen avain PKCS8-muodossa (yleinen tallennusmuoto)
const yksityinenPKCS8 = await crypto.subtle.exportKey(
'pkcs8',
avainpari.privateKey
);
console.log('Yksityinen avain (PKCS8, tavua):', yksityinenPKCS8.byteLength);
JWK-muotoinen julkinen P-256-avain näyttää tältä:
{
"kty": "EC",
"crv": "P-256",
"x": "mPUKT_bAWGHIhg0TpjjqVsP1rXWQu_vwVOHHtNkdYoA",
"y": "8BQAsImGeAS46fyWw5MhYfGTT0IjBpFw2SS34Dv4Irs",
"key_ops": ["verify"],
"ext": true
}
Turvallisuushuomio: JWK-muotoinen yksityinen avain sisältää "d"-kentän, joka on itse yksityinen avain base64url-muodossa. Tallenna yksityiset avaimet vain salattuun tietokantaan tai laitteistoon suojattuun avainvarastoon (HSM, AWS KMS, Google Cloud KMS). Älä koskaan tallenna yksityisiä avaimia ympäristömuuttujiin tuotantoympäristöissä.
Vaihe 9 – AES-GCM-avainten kääriminen wrapKey():lla
Avainten kääriminen (key wrapping) tarkoittaa yhden kryptografisen avaimen salaamista toisella avaimella turvallista tallennusta tai siirtoa varten. WebCrytossa tähän on erityiset wrapKey() ja unwrapKey()-metodit. Ne ovat turvallisempia kuin avaimen vienti JWK:ksi ja salaaminen erikseen, koska raaka avainmateriaali ei joudu JavaScript-muuttujaan välivaiheessa.
// Vaihe 9: Avainten kääriminen
// Käärimisavain – hallitsee sisältöavaimia
const kaarimisAvain = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
false, // käärimisavain pysyy ei-extrahoitavana
['wrapKey', 'unwrapKey']
);
// Sisältöavain – käytetään varsinaisen datan salaamiseen
const sisaltoAvain = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true, // oltava true, jotta voidaan kääriä
['encrypt', 'decrypt']
);
const kaarimisIV = crypto.getRandomValues(new Uint8Array(12));
// Kääri sisältöavain käärimisavaimella
const kaarittyAvain = await crypto.subtle.wrapKey(
'jwk',
sisaltoAvain,
kaarimisAvain,
{ name: 'AES-GCM', iv: kaarimisIV }
);
console.log('Kääritty avain (tavua):', kaarittyAvain.byteLength);
// Pura avain myöhemmin käyttöä varten
const purettuAvain = await crypto.subtle.unwrapKey(
'jwk',
kaarittyAvain,
kaarimisAvain,
{ name: 'AES-GCM', iv: kaarimisIV }, // purkamisparametrit
{ name: 'AES-GCM', length: 256 }, // purettavan avaimen tyyppi
false,
['encrypt', 'decrypt']
);
console.log('Avain purettu onnistuneesti');
// Testaa purettua avainta
const testaus = await salaaData(purettuAvain, 'testi');
const tulos = await puraData(purettuAvain, testaus);
console.log('Puretulla avaimella toimii:', tulos === 'testi');
Vaihe 10 – ChaCha20-Poly1305 Node.js v24.7.0:ssa
Node.js 24.7.0 lisäsi ChaCha20-Poly1305:n WebCrypto-rajapintaan. ChaCha20-Poly1305 on vaihtoehto AES-GCM:lle, erityisesti laitteissa joissa ei ole AES-laitteistokiihdytystä (AES-NI). Mobiililaitteissa ja IoT-laitteissa ChaCha20 on usein nopeampi. WireGuard-protokolla ja TLS 1.3 tukevat molempia algoritmeja.
ChaCha20-Poly1305:n API on lähes identtinen AES-GCM:n kanssa. Molemmat käyttävät 12-tavun nonce/IV-arvoa ja tuottavat 16-tavun autentikointikoodin. Tarkista tuki SubtleCrypto.supports():lla ennen käyttöä:
// Vaihe 10: ChaCha20-Poly1305 (vaatii Node.js 24.7.0+)
const onNodejs247Plus = typeof crypto.subtle.supports === 'function';
if (onNodejs247Plus) {
const chacha20Tuettu = await crypto.subtle.supports({
name: 'ChaCha20-Poly1305',
});
console.log('ChaCha20-Poly1305 tuettu:', chacha20Tuettu);
if (chacha20Tuettu) {
const chachaAvain = await crypto.subtle.generateKey(
{ name: 'ChaCha20-Poly1305', length: 256 },
true,
['encrypt', 'decrypt']
);
const nonce = crypto.getRandomValues(new Uint8Array(12));
const enc = new TextEncoder();
const plaintext = enc.encode('Salattu viesti ChaCha20-Poly1305:lla');
const salattu = await crypto.subtle.encrypt(
{ name: 'ChaCha20-Poly1305', iv: nonce },
chachaAvain,
plaintext
);
const purettu = await crypto.subtle.decrypt(
{ name: 'ChaCha20-Poly1305', iv: nonce },
chachaAvain,
salattu
);
console.log('ChaCha20 tulos:', new TextDecoder().decode(purettu));
}
} else {
console.log('Node.js < 24.7.0 – ChaCha20-Poly1305 ei saatavilla WebCrytossa');
}
Jos sovelluksesi vaatii Node.js 22.x LTS -yhteensopivuuden, pysy AES-GCM:ssä. Node.js 24.7.0+ -ympäristöissä voit tarjota molempia algoritmeja ja tarkistaa ajon aikana, kumpi on parempi kyseiselle laitteistolle.
Vaihe 11 – SubtleCrypto.supports()-ominaisuustarkistus
SubtleCrypto.supports() lisättiin Node.js 24.7.0:ssa ja on myös saatavilla nykyaikaisissa selaimissa. Se mahdollistaa algoritmin tuen tarkistamisen ajon aikana ilman try/catch-blokkeja. Tämä on erityisen hyödyllistä koodissa, joka toimii useissa ympäristöissä:
// Vaihe 11: SubtleCrypto.supports()-tarkistus
async function tarkistaKryptoTuki() {
if (typeof crypto.subtle.supports !== 'function') {
console.log('supports() ei ole saatavilla – Node.js < 24.7.0');
return;
}
const algoritmit = [
{ name: 'AES-GCM' },
{ name: 'AES-CBC' },
{ name: 'RSA-OAEP', modulusLength: 2048, hash: 'SHA-256' },
{ name: 'ECDSA', namedCurve: 'P-256' },
{ name: 'PBKDF2' },
{ name: 'HKDF' },
{ name: 'SHA-256' },
{ name: 'SHA-512' },
{ name: 'ChaCha20-Poly1305' },
];
const tuetut = [];
for (const algo of algoritmit) {
const onTuettu = await crypto.subtle.supports(algo);
const merkki = onTuettu ? 'OK' : 'EI';
console.log(`[${merkki}] ${algo.name}`);
if (onTuettu) tuetut.push(algo.name);
}
console.log(`\nTuetut algoritmit (${tuetut.length}/${algoritmit.length}):`, tuetut.join(', '));
}
await tarkistaKryptoTuki();
Vaihe 12 – Täydellinen projekti: salattu muistiinpano-API
Yhdistetään kaikki aiemmat vaiheet täydelliseksi toimivaksi projektiksi. Tämä esimerkki rakentaa salatun muistiinpanojärjestelmän, joka käyttää PBKDF2-avainjohdannaista salasanasta, AES-GCM:ää datan salaamiseen ja ECDSA:ta datan eheyden allekirjoitukseen. Luo tiedosto muistiinpano-api.js:
// muistiinpano-api.js – salattu muistiinpanojärjestelmä WebCryptolla
const enc = new TextEncoder();
const dec = new TextDecoder();
async function alustaSalausAvain(salasana, suola = null) {
const kaytettavaSuola = suola ?? crypto.getRandomValues(new Uint8Array(16));
const perus = await crypto.subtle.importKey(
'raw', enc.encode(salasana), 'PBKDF2', false, ['deriveKey']
);
const avain = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt: kaytettavaSuola, iterations: 310_000, hash: 'SHA-256' },
perus,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
return { avain, suola: kaytettavaSuola };
}
async function salaa(avain, teksti) {
const iv = crypto.getRandomValues(new Uint8Array(12));
const ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, avain, enc.encode(teksti));
const buf = new Uint8Array(12 + ct.byteLength);
buf.set(iv); buf.set(new Uint8Array(ct), 12);
return buf;
}
async function pura(avain, buf) {
const pt = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: buf.slice(0, 12) },
avain, buf.slice(12)
);
return dec.decode(pt);
}
async function allekirjoita(yksityinenAvain, teksti) {
return crypto.subtle.sign(
{ name: 'ECDSA', hash: 'SHA-256' }, yksityinenAvain, enc.encode(teksti)
);
}
async function verifioi(julkinenAvain, allekirjoitus, teksti) {
return crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' }, julkinenAvain, allekirjoitus, enc.encode(teksti)
);
}
// --- Pääohjelma ---
// Luodaan allekirjoitusavainpari
const avainpari = await crypto.subtle.generateKey(
{ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']
);
// Alustetaan salaus salasanasta
const { avain: salausAvain, suola } = await alustaSalausAvain('kestava-salasana-2026');
// Tallennetaan muistiinpano
const muistiinpano = 'Tärkeä salainen muistiinpano – päiväys 18.6.2026';
const salattuData = await salaa(salausAvain, muistiinpano);
const sig = await allekirjoita(avainpari.privateKey, muistiinpano);
console.log('Tallennettu:');
console.log(' Koko:', salattuData.byteLength, 'tavua');
console.log(' Suola:', Buffer.from(suola).toString('hex'));
// Ladataan muistiinpano (simuloi myöhempää lataamista samalla salasanalla)
const { avain: palautettuAvain } = await alustaSalausAvain('kestava-salasana-2026', suola);
const purettuTeksti = await pura(palautettuAvain, salattuData);
const allekirjoitusKelpaa = await verifioi(avainpari.publicKey, sig, purettuTeksti);
console.log('\nLadattu:');
console.log(' Sisältö:', purettuTeksti);
console.log(' Allekirjoitus kelvollinen:', allekirjoitusKelpaa);
node muistiinpano-api.js
# Tallennettu:
# Koko: 77 tavua
# Suola: 3a7f2c1d9e8b5f4a...
#
# Ladattu:
# Sisältö: Tärkeä salainen muistiinpano – päiväys 18.6.2026
# Allekirjoitus kelvollinen: true
WebCrypto API vs. node:crypto: milloin valita kumpi?
Valinta riippuu käyttötapauksesta. WebCrypto on parempi silloin, kun sama koodi täytyy toimia selaimessa ja palvelimella, tai kun haluat CryptoKey-abstraktion tuoman lisäturvallisuuden. node:crypto tarjoaa enemmän algoritmeja ja stream-tuen suurille tiedostoille.
| Käyttötapaus | Suositus | Perustelu |
|---|---|---|
| Selain + Node.js yhteinen koodi | WebCrypto API | W3C-standardi toimii molemmissa |
| Ed25519-allekirjoitukset | node:crypto | Ei WebCrypto-standardissa |
| PBKDF2 salasanoille | WebCrypto API | CryptoKey suojaa avainmateriaalin |
| Argon2 salasanoille (paras) | WebCrypto (Node.js 24.8+) | Muistivaikea, lisätty v24.8.0:ssa |
| X.509-sertifikaatit | node:crypto tai tls-moduuli | WebCryptolla ei suoraa tukea |
| ECDH-avaintenvaihto | WebCrypto API | X25519 ja P-256 tuettu |
| AES-GCM-salaus | WebCrypto API | CryptoKey estää IV:n uudelleenkäytön |
| Stream-salaus suurille tiedostoille | node:crypto | createCipheriv() tukee Node.js-streameja |
| HMAC-viestien autentikointi | Molemmat | Lue HMAC-oppaastamme lisätiedot |
WebCryptolla ei ole stream-tukea, mikä tekee suurten tiedostojen salaamisesta hankalaa: sinun täytyy ladata koko tiedosto muistiin ennen encrypt()-kutsua. Jos salaat tiedostoja joiden koko on yli 100 megatavua, käytä node:crypto:n createCipheriv()-streamia.
8 yleisintä sudenkuoppaa Node.js WebCrypto API:ssa
Seuraavat virheet ovat yleisimpiä WebCrypto-koodin kirjoittajien joukossa. Tunnista ne ennen kuin ne pääsevät tuotantoon:
- IV:n uudelleenkäyttö AES-GCM:ssä. Sama IV kahdesti saman avaimen kanssa romuttaa AES-GCM:n turvallisuuden täysin: hyökkääjä voi palauttaa plaintext-datan ja vaarantaa avaimen. Generoi aina uusi IV
getRandomValues(new Uint8Array(12)):lla jokaiselle salausoperaatiolle. - Liian pieni iteraatiomäärä PBKDF2:ssa. Vanhemmissa esimerkeissä näkyy 1 000 tai 10 000 iteraatiota. OWASP suosittelee 2026:lle vähintään 310 000 iteraatiota SHA-256:n kanssa. Pienemmät arvot tekevät raa'an voiman hyökkäyksestä triviaalia GPU-kiihdytyksellä.
- Suolan uudelleenkäyttö eri salasanoille. Jokainen käyttäjä tarvitsee ainutlaatuisen suolan. Yhteinen tai tyhjä suola mahdollistaa sateenkaaritaulukkohyökkäyksen.
- extractable: true tuotantoavaimille. Aseta
extractable: falsekaikille avaimille joita ei tarvitse viedä. Ei-extrahoitavat avaimet eivät voi vuotaa muistidumpissa tai virheenkäsittelykoodissa. - Ed25519:n odottaminen WebCryptolta. Ed25519 ei ole W3C WebCrypto -standardissa.
generateKey({name:'Ed25519',...})heittääDOMException: Unrecognized algorithm name. Käytänode:crypto:a. - TextEncoder puuttuu hyvin vanhassa Node.js:ssä.
TextEncoderon globaali Node.js 11+:ssa. Versioissa 10 ja vanhemmissa se on tuotava:const {TextEncoder} = require('util'). Versiossa 18+ tämä ei ole ongelma. - ArrayBuffer vs Buffer -sekaannus. WebCrypto palauttaa
ArrayBuffer-objekteja, ei Node.js:n omiaBuffer-objekteja. Muunna tarvittaessaBuffer.from(arrayBuffer):lla. - Unohdettu IV tai suola tallennuksessa. Salauksen purku on mahdotonta ilman oikeaa IV:tä. Tallenna IV salatun datan yhteydessä, esimerkiksi pakettimalla se alkuun. Sama koskee PBKDF2:n suolaa.
Vianetsintä: 10 yleisintä ongelmaa ratkaisuineen
| Virhe tai ongelma | Syy | Ratkaisu |
|---|---|---|
DOMException: Unrecognized algorithm name | Algoritmi ei tuettu tai kirjoitusvirhe | Tarkista nimi – se on merkkikokosensittiivinen: 'AES-GCM' ei 'aes-gcm'. Tarkista Node.js-versio. |
DOMException: The requested operation is not valid for the provided key | Avain ei tue pyydettävää operaatiota | Tarkista keyUsages-taulukko avaingeneroinnissa. AES-GCM vaatii ['encrypt', 'decrypt']. |
DOMException: Cannot extract a non-extractable key | Avain luotu extractable: false:lla | Luo avain extractable: true:lla tai älä yritä viedä sitä. Suunnittele elinkaari etukäteen. |
DOMException: The provided data is too small | IV on väärän kokoinen tai tyhjä | AES-GCM vaatii täsmälleen 12 tavun IV:n. Tarkista Uint8Array(12). |
| Decrypt heittää virheen | IV väärä, data muuttunut tai avain ei täsmää | AES-GCM:n autentikointi epäonnistuu, jos data on muuttunut. Tarkista IV:n tallennus ja palautus. |
TypeError: crypto.subtle is undefined | Node.js versio alle 15.0.0 | Päivitä Node.js 18+:aan tai käytä require('crypto').webcrypto.subtle. |
| PBKDF2 on hidas | 310 000 iteraatiota tarkoituksellisesti | Tämä on normaalia. Johda avain kerran kirjautumisessa ja pidä välimuistissa istunnon ajan. |
RangeError: ArrayBufferView's byte length exceeds limit of 65536 | getRandomValues():lle liian iso taulukko | Maksimi on 65 536 tavua per kutsu. Jaa useampaan kutsun. |
| Sama ECDSA-allekirjoitus toistuu eri ajokerroilla | Odotettu – ECDSA ei ole deterministinen | ECDSA käyttää satunnaista k-arvoa jokaisessa allekirjoituksessa. Verifiointi toimii silti. |
SyntaxError: Cannot use import statement | package.json puuttuu "type": "module" | Lisää "type": "module" tai käytä .mjs-päätettä tiedostolle. |
Edistyneet vinkit tuotantokäyttöön
Avainten kierrätys. Sisällytä HKDF:n info-parametriin versionumero ('salausavain-v1', 'salausavain-v2'), jotta vanhoilla avaimilla salattu data voidaan purkaa uuden avaimen käyttöönoton jälkeenkin. Merkitse salattu data versionumerolla, jotta tiedät, millä avaimella se on salattu.
Avainten säilytys palvelimilla. Älä tallenna salausavaimia tietokantaan selkokielisinä. Käytä AWS KMS:ää, Google Cloud KMS:ää tai HashiCorp Vaultia. Jos sinun on tallennettava avain itse, käytä wrapKey():ta ja pidä käärimisavain erillisessä paikassa kuin salattu data.
Vakioaikainen allekirjoitusten vertailu. Älä vertaa allekirjoituksia tai HMAC-koodeja suoraan ===-operaattorilla. Käytä crypto.subtle.verify():a, joka suorittaa vakioaikaisen vertailun. Vaihtoehtoisesti käytä node:crypto:n crypto.timingSafeEqual()-funktiota.
EU:n Cyber Resilience Act 2026. Kyberresilienssiasetus (CRA) asettaa syyskuussa 2026 pakollisiksi raportoitavaksi 24 tunnin ja 72 tunnin haavoittuvuusilmoitusvelvoitteet. Käyttämällä standardoituja algoritmeja (AES-GCM, ECDSA, PBKDF2) WebCrypto-rajapinnan kautta ja dokumentoimalla algoritmivalintasi SBOM:iin olet hyvässä asemassa vaatimustenmukaisuuden suhteen. MDN SubtleCrypto -dokumentaatio on paras yksittäinen viiteresurssi algoritmiparametreille.
Post-kvantti-valmius. NIST viimeisteli vuonna 2024 ensimmäiset post-kvantti-kryptografisstandardit (ML-KEM eli Kyber, ML-DSA eli Dilithium). Node.js WebCrypto ei vielä tue näitä algoritmeja, mutta tuki on odotettavissa lähivuosien versioissa. Tällä hetkellä parhaat käytännöt suosittelevat hybridilähestymistapaa: käytä sekä klassisia algoritmeja (ECDH, ECDSA) että post-kvantti-algoritmeja rinnakkain, jolloin turvallisuus on taattu molemmilla kentillä. SubtleCrypto.supports()-tarkistus mahdollistaa koodin, joka hyödyntää uusia algoritmeja heti kun Node.js lisää ne tuen piiriin. Lue lisää post-kvantti-kryptografiasta erillisestä artikkelistamme.
Virhelokin rakenne. Kun WebCrypto-metodi heittää poikkeuksen, varmista ettei loki paljasta arkaluonteisia tietoja. DOMException-virheen viesti on yleensä geneerinen ("The operation failed for an operation-specific reason"), mikä on hyvä: se ei paljasta yksityisavaimen sisältöä tai salausparametreja. Vältä catch-blokeissa arkaluonteisen datan tulostamista tai tallentamista logiin, myös debug-tarkoituksessa. Salausoperaatioiden epäonnistuminen on merkki joko ohjelmointivirheestä tai hyökkäysyrityksestä: molemmat ansaitsevat selkeän lokimerkinnän ilman herkkää dataa.
Testaus eri Node.js-versioilla. Koska WebCrypto on laajentunut merkittävästi versiosta 15 versioon 24, testaa koodi vähintään minimiversiollasi (Node.js 18 LTS) ja uusimmalla versiolla. Käytä nvm:ää (Node Version Manager) versioiden hallintaan kehitysympäristöissä.
Usein kysytyt kysymykset
Mistä Node.js-versiosta WebCrypto API on vakaa tuotantokäyttöön?
Node.js 18.x LTS on ensimmäinen versio, jossa WebCrypto API on täysin vakaa. SubtleCrypto lisättiin teknisesti versiossa 15.0.0, mutta sen toteutuksessa oli vielä puutteita. Node.js 18 on minimisuositus uusille projekteille.
Voiko WebCrypto-koodia käyttää suoraan selaimessa?
Kyllä, suurimmaksi osaksi. WebCrypto on W3C-standardi, jota kaikki nykyaikaiset selaimet tukevat. Selaimessa käytät window.crypto.subtle tai pelkkää crypto.subtle:a, Node.js:ssä sama rakenne toimii versiosta 18 alkaen. Jotkut algoritmit (kuten ChaCha20-Poly1305) ovat tulleet selaimiin ennen Node.js:ää.
Miksi PBKDF2 on niin hidas, onko se virhe?
Ei. Hidas toiminta on tarkoituksellinen turvaominaisuus: 310 000 iteraatiota hidastaa raa'an voiman hyökkäystä niin, että GPU:lla voidaan testata vain muutamia tuhansia salasanoja sekunnissa miljoonien sijaan. Hidas PBKDF2 on merkki oikeasta toteutuksesta. Johda avain kerran kirjautumisen yhteydessä ja pidä se välimuistissa istunnon ajan.
AES-GCM vai ChaCha20-Poly1305?
AES-GCM on parempi palvelimilla, joissa on AES-laitteistokiihdytys (kaikki nykyiset x86_64-prosessorit). ChaCha20-Poly1305 on parempi ilman AES-NI:tä. Turvallisuustasoltaan molemmat ovat vastaavia. ChaCha20-Poly1305:n WebCrypto-tuki vaatii Node.js 24.7.0+.
Miten WebCrypto API eroaa node:crypto:sta suorituskyvyn suhteen?
Yleinen havainto on, että WebCrypto on hieman hitaampi yksinkertaisissa symmetrisissä operaatioissa CryptoKey-abstraktion takia. Epäsymmetrisissä operaatioissa (RSA, ECDSA) ero on marginaalinen. Suurten volyymien skenaariot suosivat node:crypto:n stream-rajapintaa.
Tarvitseeko lisätä npm-paketteja WebCryptoa varten?
Ei. WebCrypto on sisäänrakennettu Node.js 15+:ssa. Poikkeus on Argon2, joka vaatii Node.js 24.8.0+ WebCrypton kautta tai npm-paketin vanhemmissa versioissa. PBKDF2, AES-GCM ja ECDSA eivät vaadi yhtään lisäpakettia.
Onko Node.js WebCrypto yhteensopiva EU:n NIS2- ja CRA-vaatimusten kanssa?
WebCrypto käyttää NIST-sertifioituja algoritmeja (AES-GCM, ECDSA, RSA-OAEP), jotka täyttävät NIS2-direktiivin ja CRA:n kryptografiasuositukset. Suomen kyberturvallisuuslaki (124/2025) perustuu NIS2:een. Dokumentoi algoritmivalintasi ja parametrisi SBOM:iin CRA-vaatimustenmukaisuuden osoittamiseksi. Lisätiedot Node.js WebCrypto-dokumentaatiosta ja W3C WebCrypto-standardista.
Mikä on paras tapa säilyttää CryptoKey-avaimet tietokantaan?
Älä tallenna avaimia suoraan. Johda avaimet tarvittaessa PBKDF2:lla tai HKDF:llä käyttäjän salasanasta, tai käytä wrapKey():ta salataksesi avaimet master key:lla. Master key:t kuuluvat laitteistosuojattuun avainvarastoon (HSM, AWS KMS, Google Cloud KMS), ei tietokantaan.
Aiheeseen liittyvää
Syvennä Node.js-kryptografiaosaamistasi:
- BLAKE3-hajautus Node.js:ssä: 10 vaihetta, 30 min [2026] – 3,5x SHA-256:ta nopeampi hajautusalgoritmi, saatavilla WebCryton ulkopuolella
- Ed25519 Node.js:ssä: 10 vaihetta, 30 min [2026] – Nopeat allekirjoitukset
node:crypto:lla, koska Ed25519 ei kuulu WebCrypto-standardiin - HMAC Node.js:ssä: 10 vaihetta, 30 min [2026] – HMAC-SHA256-viestien autentikointi, saatavilla myös WebCryton kautta
- JWT-todennus Node.js:ssä: 12 vaihetta, 40 min [2026] – JSON Web Token -todennus ECDSA- ja RSA-allekirjoitusten päällä
- Turvalliset sessiot Node.js:ssä: 10 vaihetta [2026] – Istunnonhallinta turvallisesti, mukaan lukien avainjohdannaiset




