{"id":142,"date":"2026-06-20T17:00:21","date_gmt":"2026-06-20T17:00:21","guid":{"rendered":"https:\/\/shattered.io\/no\/2026\/06\/20\/openssl-certificate-authority\/"},"modified":"2026-06-20T17:02:15","modified_gmt":"2026-06-20T17:02:15","slug":"openssl-certificate-authority","status":"publish","type":"post","link":"https:\/\/shattered.io\/no\/openssl-certificate-authority\/","title":{"rendered":"Privat Certificate Authority med OpenSSL: 12 steg, 30 min [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En privat <strong>certificate authority<\/strong> lar deg signere dine egne TLS-sertifikater uten \u00e5 betale for hvert enkelt, og uten \u00e5 stole p\u00e5 en tredjepart. For interne API-er, mikrotjenester, utviklingsmilj\u00f8er og IoT-fl\u00e5ter er dette standardtiln\u00e6rmingen i 2026. I denne guiden setter du opp en fullstendig PKI-hierarki fra rot-CA til sluttsertifikat med OpenSSL 3.5 og integrerer det i Node.js p\u00e5 under 30 minutter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"hva-er-en-certificate-authority-og-hvorfor-lage-din-egen\">Hva er en Certificate Authority og hvorfor lage din egen?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En <strong>certificate authority<\/strong> (CA) er en enhet som signerer digitale sertifikater og dermed bekrefter identiteten til en server, klient eller person. Nettleseren din stoler p\u00e5 omtrent 150 offentlige CA-er som er forh\u00e5ndsinstallert i operativsystemet. Disse passer til offentlige nettsider, men for intern infrastruktur har de tre store ulemper: de koster penger per sertifikat, de krever at domenet er offentlig resolverbart, og de eksponerer intern infrastruktur mot offentlig Certificate Transparency-logging.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En privat CA l\u00f8ser alle tre: du signerer sertifikater gratis, for vilk\u00e5rlige interne domener som <code>api.intern.local<\/code>, og uten at de havner i offentlige CT-logger. Prisen er at du selv m\u00e5 distribuere rot-sertifikatet til alle klienter og vedlikeholde n\u00f8kkelinfrastrukturen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I 2026 er dette standardpraksis for norske teknologiselskaper, banker og offentlige etater som h\u00e5ndterer intern tjenestekommunikasjon. Alternativet, Let&#8217;s Encrypt, krever offentlig DNS og fungerer ikke for private nett. HashiCorp Vault PKI og AWS Private CA gir mer funksjonalitet, men for de fleste brukstilfeller er OpenSSL mer enn nok.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"forutsetninger-og-versjoner\">Forutsetninger og versjoner<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00f8r du starter, s\u00f8rg for at du har f\u00f8lgende p\u00e5 plass:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>OpenSSL 3.5.x<\/strong> (LTS, st\u00f8ttet til april 2030). OpenSSL 4.0.1 er den nyeste utgivelsen per juni 2026, men 3.5 er LTS-linjen som anbefales for langlivet CA-infrastruktur. Sjekk versjon med <code>openssl version<\/code>.<\/li><li><strong>Node.js 22 LTS<\/strong> eller nyere. TLS-modulen i Node.js bruker OpenSSL internt og er fullt kompatibel med sertifikater generert av OpenSSL 3.5.<\/li><li><strong>Linux eller macOS<\/strong> med tilgang til terminalen. Windows-brukere kan bruke WSL 2.<\/li><li>Root-tilgang eller <code>sudo<\/code>-rettigheter for \u00e5 installere rot-sertifikatet i systemets truststore.<\/li><li>Grunnleggende kjennskap til kommandolinjen og konseptet offentlig-privat n\u00f8kkelpar.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Installer OpenSSL 3.5 p\u00e5 Ubuntu\/Debian:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Sjekk eksisterende versjon\nopenssl version\n# Output: OpenSSL 3.x.x  (versjon avhengig av distro)\n\n# Oppdater til 3.5 via kompilering (Ubuntu 24.04 har 3.0 som standard)\n# Alternativt, bruk snap: snap install openssl --classic\n\n# Ubuntu 24.04+ pakkeversjon\nsudo apt update && sudo apt install openssl libssl-dev\nopenssl version<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pki-hierarki-rot-ca-mellom-ca-og-sluttsertifikat\">PKI-hierarki: Rot-CA, mellom-CA og sluttsertifikat<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00f8r du skriver en eneste kommando, er det viktig \u00e5 forst\u00e5 strukturen du skal sette opp. Et riktig PKI-hierarki best\u00e5r av tre lag:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Lag<\/th><th>Navn<\/th><th>Funksjon<\/th><th>N\u00f8kkelst\u00f8rrelse<\/th><th>Gyldighetsperiode<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td>Rot-CA (Root CA)<\/td><td>Signerer mellom-CA. Holdes frakoblet (offline).<\/td><td>RSA 4096 eller P-384<\/td><td>20 \u00e5r<\/td><\/tr><tr><td>2<\/td><td>Mellom-CA (Intermediate CA)<\/td><td>Signerer sluttsertifikater. Er online.<\/td><td>RSA 4096 eller P-256<\/td><td>10 \u00e5r<\/td><\/tr><tr><td>3<\/td><td>Sluttsertifikat<\/td><td>Identifiserer server eller klient. Distribueres med applikasjon.<\/td><td>RSA 2048 eller P-256<\/td><td>1 \u00e5r<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Grunnen til at rot-CA holdes offline er enkel: hvis mellom-CA-n\u00f8kkelen kompromitteres, kan du tilbakekalle mellom-CA-en ved hjelp av rot-CA og utstede en ny. Hadde rot-CA-n\u00f8kkelen blitt kompromittert, m\u00e5tte du startet helt fra scratch og distribuert et nytt rot-sertifikat til alle klienter. Dette er den grunnleggende sikkerhetsfordelingen i et veldefinert PKI-hierarki.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I denne guiden lager vi et to-lags hierarki (rot-CA og mellom-CA) og viser deretter hvordan mellom-CA signerer sluttsertifikater for dine tjenester.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-1-opprett-mappestruktur-for-ca\">Steg 1: Opprett mappestruktur for CA<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OpenSSL sin CA-funksjonalitet forutsetter en bestemt mappestruktur for \u00e5 holde orden p\u00e5 sertifikater, private n\u00f8kler, serienumre og tilbakekallelseslister. Opprett denne strukturen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir -p ~\/myca\/{root-ca\/{certs,crl,newcerts,private},intermediate\/{certs,crl,csr,newcerts,private},endcerts}\ncd ~\/myca\n\n# Root CA filer\ntouch root-ca\/index.txt\necho 1000 > root-ca\/serial\necho 1000 > root-ca\/crlnumber\n\n# Intermediate CA filer\ntouch intermediate\/index.txt\necho 1000 > intermediate\/serial\necho 1000 > intermediate\/crlnumber\n\n# Sett riktige tilganger p\u00e5 private mapper\nchmod 700 root-ca\/private intermediate\/private\n\n# Bekreft strukturen\nfind ~\/myca -type d | sort<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>index.txt<\/code> er CA-ens database over utstedte sertifikater. <code>serial<\/code> holder l\u00f8pende serienummer som \u00f8kes for hvert nytt sertifikat. <code>crlnumber<\/code> brukes for Certificate Revocation Lists. Del aldri innholdet i <code>private\/<\/code>-mappene med andre systemer eller prosesser.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-2-konfigurer-openssl-for-rot-ca\">Steg 2: Konfigurer OpenSSL for rot-CA<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OpenSSL bruker en konfigurasjonsfil (<code>openssl.cnf<\/code>) for \u00e5 styre CA-atferd. Lag \u00e9n for rot-CA. Legg merke til <code>pathlen:0<\/code> i <code>v3_intermediate_ca<\/code> som hindrer mellom-CA fra \u00e5 utstede nye CA-sertifikater:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat > ~\/myca\/root-ca\/openssl.cnf << 'EOF'\n[ ca ]\ndefault_ca = CA_default\n\n[ CA_default ]\ndir               = \/root\/myca\/root-ca\ncerts             = $dir\/certs\ncrl_dir           = $dir\/crl\nnew_certs_dir     = $dir\/newcerts\ndatabase          = $dir\/index.txt\nserial            = $dir\/serial\nRANDFILE          = $dir\/private\/.rand\nprivate_key       = $dir\/private\/ca.key.pem\ncertificate       = $dir\/certs\/ca.cert.pem\ncrlnumber         = $dir\/crlnumber\ncrl               = $dir\/crl\/ca.crl.pem\ncrl_extensions    = crl_ext\ndefault_crl_days  = 30\ndefault_md        = sha256\nname_opt          = ca_default\ncert_opt          = ca_default\ndefault_days      = 375\npreserve          = no\npolicy            = policy_strict\n\n[ policy_strict ]\ncountryName             = match\nstateOrProvinceName     = match\norganizationName        = match\norganizationalUnitName  = optional\ncommonName              = supplied\nemailAddress            = optional\n\n[ policy_loose ]\ncountryName             = optional\nstateOrProvinceName     = optional\nlocalityName            = optional\norganizationName        = optional\norganizationalUnitName  = optional\ncommonName              = supplied\nemailAddress            = optional\n\n[ req ]\ndefault_bits        = 4096\ndistinguished_name  = req_distinguished_name\nstring_mask         = utf8only\ndefault_md          = sha256\nx509_extensions     = v3_ca\n\n[ req_distinguished_name ]\ncountryName                     = Landkode (2 bokstaver)\nstateOrProvinceName             = Fylke\nlocalityName                    = By\n0.organizationName              = Organisasjon\norganizationalUnitName          = Avdeling\ncommonName                      = Felles navn\nemailAddress                    = E-postadresse\ncountryName_default             = NO\nstateOrProvinceName_default     = Oslo\n0.organizationName_default      = MinBedrift CA\n\n[ v3_ca ]\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid:always,issuer\nbasicConstraints        = critical, CA:true\nkeyUsage                = critical, digitalSignature, cRLSign, keyCertSign\n\n[ v3_intermediate_ca ]\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid:always,issuer\nbasicConstraints        = critical, CA:true, pathlen:0\nkeyUsage                = critical, digitalSignature, cRLSign, keyCertSign\n\n[ usr_cert ]\nbasicConstraints        = CA:FALSE\nnsCertType              = client, email\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid,issuer\nkeyUsage                = critical, nonRepudiation, digitalSignature, keyEncipherment\nextendedKeyUsage        = clientAuth, emailProtection\n\n[ server_cert ]\nbasicConstraints        = CA:FALSE\nnsCertType              = server\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid,issuer:always\nkeyUsage                = critical, digitalSignature, keyEncipherment\nextendedKeyUsage        = serverAuth\n\n[ crl_ext ]\nauthorityKeyIdentifier  = keyid:always\n\n[ ocsp ]\nbasicConstraints        = CA:FALSE\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid,issuer\nkeyUsage                = critical, digitalSignature\nextendedKeyUsage        = critical, OCSPSigning\nEOF<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-3-generer-rot-ca-nokkel-og-selvsignert-sertifikat\">Steg 3: Generer rot-CA-n\u00f8kkel og selvsignert sertifikat<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00e5 lager vi selve hjertet i PKI-en: rot-CA-n\u00f8kkelen og det tilh\u00f8rende selvsignerte sertifikatet. Bruk RSA 4096 for maksimal kompatibilitet, eller P-384 (ECDSA) for bedre ytelse. Begge gir tilsvarende sikkerhetsniv\u00e5 i 2026:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd ~\/myca\n\n# Alternativ A: RSA 4096 (h\u00f8yest kompatibilitet)\nopenssl genpkey -algorithm RSA \\\n  -pkeyopt rsa_keygen_bits:4096 \\\n  -aes256 \\\n  -out root-ca\/private\/ca.key.pem\nchmod 400 root-ca\/private\/ca.key.pem\n\n# Alternativ B: ECDSA P-384 (raskere, mindre n\u00f8kler)\n# openssl genpkey -algorithm EC \\\n#   -pkeyopt ec_paramgen_curve:P-384 \\\n#   -aes256 \\\n#   -out root-ca\/private\/ca.key.pem\n\n# Selvsignert rot-CA-sertifikat (20 \u00e5r = 7300 dager)\nopenssl req -config root-ca\/openssl.cnf \\\n  -key root-ca\/private\/ca.key.pem \\\n  -new -x509 -days 7300 \\\n  -sha256 \\\n  -extensions v3_ca \\\n  -out root-ca\/certs\/ca.cert.pem\nchmod 444 root-ca\/certs\/ca.cert.pem\n\n# Verifiser rot-CA-sertifikatet\nopenssl x509 -noout -text -in root-ca\/certs\/ca.cert.pem | grep -E \"(Issuer|Subject|Not Before|Not After|CA:)\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Du vil bli bedt om \u00e5 angi et passord for n\u00f8kkelen (bruk et sterkt passord og lagre det sikkert), og deretter fylle ut Distinguished Name-feltene. Bruk <code>NO<\/code> for landkode og ditt organisasjonsnavn. Forventet output fra verifikasjonskommandoen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Forventet output:\n# Issuer: C=NO, ST=Oslo, O=MinBedrift CA, CN=MinBedrift Root CA\n# Subject: C=NO, ST=Oslo, O=MinBedrift CA, CN=MinBedrift Root CA\n# Not Before: Jun 20 10:00:00 2026 GMT\n# Not After : Jun 18 10:00:00 2046 GMT\n# CA:TRUE<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-4-opprett-og-signer-mellom-ca\">Steg 4: Opprett og signer mellom-CA<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mellom-CA er den operative CA-en som utsteder sertifikater til daglig. Rot-CA brukes bare for \u00e5 signere mellom-CA og holdes ellers offline. Dette lagdelingen er kritisk for sikkerheten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Generer mellom-CA-n\u00f8kkel\nopenssl genpkey -algorithm RSA \\\n  -pkeyopt rsa_keygen_bits:4096 \\\n  -aes256 \\\n  -out intermediate\/private\/intermediate.key.pem\nchmod 400 intermediate\/private\/intermediate.key.pem\n\n# Lag Certificate Signing Request (CSR) for mellom-CA\nopenssl req -config root-ca\/openssl.cnf \\\n  -new -sha256 \\\n  -key intermediate\/private\/intermediate.key.pem \\\n  -out intermediate\/csr\/intermediate.csr.pem\n\n# Signer mellom-CA med rot-CA (10 \u00e5r = 3650 dager)\nopenssl ca -config root-ca\/openssl.cnf \\\n  -extensions v3_intermediate_ca \\\n  -days 3650 \\\n  -notext \\\n  -md sha256 \\\n  -in intermediate\/csr\/intermediate.csr.pem \\\n  -out intermediate\/certs\/intermediate.cert.pem\nchmod 444 intermediate\/certs\/intermediate.cert.pem\n\n# Lag sertifikatkjede (chain) - brukes av klienter\ncat intermediate\/certs\/intermediate.cert.pem \\\n    root-ca\/certs\/ca.cert.pem > \\\n    intermediate\/certs\/ca-chain.cert.pem\nchmod 444 intermediate\/certs\/ca-chain.cert.pem\n\n# Verifiser mellom-CA mot rot-CA\nopenssl verify -CAfile root-ca\/certs\/ca.cert.pem \\\n  intermediate\/certs\/intermediate.cert.pem\n# Forventet: intermediate\/certs\/intermediate.cert.pem: OK<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-5-konfigurer-mellom-ca-for-sertifikatutstedelse\">Steg 5: Konfigurer mellom-CA for sertifikatutstedelse<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mellom-CA trenger sin egen konfigurasjonsfil som peker til riktige stier og bruker <code>policy_loose<\/code> slik at den kan utstede sertifikater til ulike organisasjoner og domener:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat > ~\/myca\/intermediate\/openssl.cnf << 'EOF'\n[ ca ]\ndefault_ca = CA_default\n\n[ CA_default ]\ndir               = \/root\/myca\/intermediate\ncerts             = $dir\/certs\ncrl_dir           = $dir\/crl\nnew_certs_dir     = $dir\/newcerts\ndatabase          = $dir\/index.txt\nserial            = $dir\/serial\nRANDFILE          = $dir\/private\/.rand\nprivate_key       = $dir\/private\/intermediate.key.pem\ncertificate       = $dir\/certs\/intermediate.cert.pem\ncrlnumber         = $dir\/crlnumber\ncrl               = $dir\/crl\/intermediate.crl.pem\ncrl_extensions    = crl_ext\ndefault_crl_days  = 30\ndefault_md        = sha256\nname_opt          = ca_default\ncert_opt          = ca_default\ndefault_days      = 375\npreserve          = no\npolicy            = policy_loose\n\n[ policy_loose ]\ncountryName             = optional\nstateOrProvinceName     = optional\nlocalityName            = optional\norganizationName        = optional\norganizationalUnitName  = optional\ncommonName              = supplied\nemailAddress            = optional\n\n[ req ]\ndefault_bits        = 2048\ndistinguished_name  = req_distinguished_name\nstring_mask         = utf8only\ndefault_md          = sha256\n\n[ req_distinguished_name ]\ncountryName                     = Landkode\nstateOrProvinceName             = Fylke\nlocalityName                    = By\n0.organizationName              = Organisasjon\ncommonName                      = Felles navn\n\n[ server_cert ]\nbasicConstraints        = CA:FALSE\nnsCertType              = server\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid,issuer:always\nkeyUsage                = critical, digitalSignature, keyEncipherment\nextendedKeyUsage        = serverAuth\n\n[ usr_cert ]\nbasicConstraints        = CA:FALSE\nnsCertType              = client, email\nsubjectKeyIdentifier    = hash\nauthorityKeyIdentifier  = keyid,issuer\nkeyUsage                = critical, nonRepudiation, digitalSignature, keyEncipherment\nextendedKeyUsage        = clientAuth, emailProtection\n\n[ crl_ext ]\nauthorityKeyIdentifier  = keyid:always\nEOF<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-6-utsted-et-serversertifikat-med-subject-alternative-names\">Steg 6: Utsted et serversertifikat med Subject Alternative Names<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Subject Alternative Names (SAN) er obligatorisk siden 2017 og kreves av alle moderne nettlesere og Node.js. Sertifikater som bare bruker Common Name (CN) uten SAN avvises. Her er det korrekte oppsettet for <code>api.intern.local<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Generer servern\u00f8kkel (RSA 2048 er tilstrekkelig for sluttsertifikater)\nopenssl genpkey -algorithm RSA \\\n  -pkeyopt rsa_keygen_bits:2048 \\\n  -out endcerts\/api.intern.local.key.pem\n\n# Lag CSR med Subject Alternative Names\nopenssl req -new -sha256 \\\n  -key endcerts\/api.intern.local.key.pem \\\n  -subj \"\/C=NO\/ST=Oslo\/O=MinBedrift\/CN=api.intern.local\" \\\n  -addext \"subjectAltName=DNS:api.intern.local,DNS:*.api.intern.local,IP:192.168.1.10\" \\\n  -out endcerts\/api.intern.local.csr.pem\n\n# Signer med mellom-CA\nopenssl ca -config intermediate\/openssl.cnf \\\n  -extensions server_cert \\\n  -days 365 \\\n  -notext \\\n  -md sha256 \\\n  -in endcerts\/api.intern.local.csr.pem \\\n  -out endcerts\/api.intern.local.cert.pem\n\n# Verifiser sertifikatkjeden\nopenssl verify -CAfile intermediate\/certs\/ca-chain.cert.pem \\\n  endcerts\/api.intern.local.cert.pem\n\n# Forventet output:\n# endcerts\/api.intern.local.cert.pem: OK\n\n# Vis SAN-feltet for \u00e5 bekrefte\nopenssl x509 -noout -text -in endcerts\/api.intern.local.cert.pem | grep -A1 \"Subject Alternative\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Alternativet <code>-addext<\/code> krever OpenSSL 1.1.1 eller nyere. Med eldre versjoner m\u00e5 du bruke en ekstern konfigurasjonsfil med <code>[SAN]<\/code>-seksjonen. OpenSSL 3.5 st\u00f8tter <code>-addext<\/code> fullt ut.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-7-installer-rot-sertifikatet-i-systemets-truststore\">Steg 7: Installer rot-sertifikatet i systemets truststore<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For at klienter skal stole p\u00e5 sertifikatene du utsteder, m\u00e5 rot-CA-sertifikatet installeres i systemets truststore. Dette er et engangssteg per klientmaskin:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Ubuntu \/ Debian\nsudo cp root-ca\/certs\/ca.cert.pem \/usr\/local\/share\/ca-certificates\/minbedrift-ca.crt\nsudo update-ca-certificates\n# Output: 1 added, 0 removed; done.\n\n# RHEL \/ Fedora \/ CentOS\nsudo cp root-ca\/certs\/ca.cert.pem \/etc\/pki\/ca-trust\/source\/anchors\/minbedrift-ca.crt\nsudo update-ca-trust\n\n# macOS\nsudo security add-trusted-cert -d -r trustRoot \\\n  -k \/Library\/Keychains\/System.keychain \\\n  root-ca\/certs\/ca.cert.pem\n\n# Verifiser at systemet stoler p\u00e5 CA-en\necho | openssl s_client -connect api.intern.local:8443 \\\n  -CApath \/etc\/ssl\/certs\/ 2>&1 | grep \"Verify return code\"\n# Forventet: Verify return code: 0 (ok)\n\n# Firefox bruker egen truststore\n# Innstillinger > Personvern og sikkerhet > Vis sertifikater > Importer\n# Velg rot-CA\/certs\/ca.cert.pem og huk av for \"Stol p\u00e5 denne CA for nettsteder\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-8-konfigurer-node-js-tls-med-privat-ca\">Steg 8: Konfigurer Node.js TLS med privat CA<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Node.js sitt TLS-lag aksepterer egendefinerte CA-sertifikater via <code>ca<\/code>-parameteret i <code>tls.createSecureContext()<\/code>. Du har to tiln\u00e6rminger: direkte i koden via <code>ca<\/code>-parameteret, eller via milj\u00f8variabelen <code>NODE_EXTRA_CA_CERTS<\/code> som er den enkleste metoden for containerbaserte milj\u00f8er.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"https-server-med-privat-ca-sertifikat\">HTTPS-server med privat CA-sertifikat<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ server.js - HTTPS-server med sertifikat fra privat CA\nconst https = require('https');\nconst fs = require('fs');\n\nconst options = {\n  \/\/ Serversertifikat og n\u00f8kkel fra privat CA\n  cert: fs.readFileSync('\/root\/myca\/endcerts\/api.intern.local.cert.pem'),\n  key: fs.readFileSync('\/root\/myca\/endcerts\/api.intern.local.key.pem'),\n  \/\/ Tilbud CA-kjeden for klientvalidering\n  ca: fs.readFileSync('\/root\/myca\/intermediate\/certs\/ca-chain.cert.pem'),\n  \/\/ For mTLS: krev klientsertifikat\n  requestCert: false,\n  rejectUnauthorized: false,\n  \/\/ Minste TLS-versjon: 1.2\n  minVersion: 'TLSv1.2',\n};\n\nconst server = https.createServer(options, (req, res) => {\n  res.writeHead(200, { 'Content-Type': 'application\/json' });\n  res.end(JSON.stringify({ status: 'OK', tls: req.socket.getCipher() }));\n});\n\nserver.listen(8443, () => {\n  console.log('HTTPS-server kj\u00f8rer p\u00e5 https:\/\/api.intern.local:8443');\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"https-klient-som-stoler-pa-privat-ca\">HTTPS-klient som stoler p\u00e5 privat CA<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ client.js - Koble til intern API med privat CA\nconst https = require('https');\nconst fs = require('fs');\nconst tls = require('tls');\n\n\/\/ Metode 1: Via createSecureContext (anbefalt for gjenbruk)\nconst secureContext = tls.createSecureContext({\n  ca: fs.readFileSync('\/root\/myca\/intermediate\/certs\/ca-chain.cert.pem'),\n});\n\nconst options = {\n  hostname: 'api.intern.local',\n  port: 8443,\n  path: '\/health',\n  method: 'GET',\n  secureContext: secureContext,\n  rejectUnauthorized: true, \/\/ ALDRI false i produksjon\n};\n\nconst req = https.request(options, (res) => {\n  console.log(`Status: ${res.statusCode}`);\n  let data = '';\n  res.on('data', (chunk) => data += chunk);\n  res.on('end', () => console.log(JSON.parse(data)));\n});\n\nreq.on('error', (err) => {\n  \/\/ Vanlige feil:\n  \/\/ CERT_HAS_EXPIRED: Sertifikatet er utl\u00f8pt - forny det\n  \/\/ UNABLE_TO_VERIFY_LEAF_SIGNATURE: CA-kjeden er ufullstendig\n  \/\/ ERR_TLS_CERT_ALTNAME_INVALID: SAN mangler hostnavn\n  console.error('TLS-feil:', err.code, err.message);\n});\n\nreq.end();\n\n\/\/ Metode 2: Via NODE_EXTRA_CA_CERTS (ingen kodeendringer)\n\/\/ Sett i terminal: export NODE_EXTRA_CA_CERTS=\/root\/myca\/intermediate\/certs\/ca-chain.cert.pem\n\/\/ Da bruker Node.js den private CA automatisk for alle HTTPS-foresp\u00f8rsler<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-9-mtls-for-tjeneste-til-tjeneste-autentisering\">Steg 9: mTLS for tjeneste-til-tjeneste-autentisering<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Gjensidig TLS (mTLS) krever at b\u00e5de server og klient presenterer gyldige sertifikater fra din private CA. Dette er standard i nulltillitsarkitekturer og Kubernetes-milj\u00f8er med service meshes som Istio eller Linkerd. Med din private CA er mTLS enkelt \u00e5 implementere:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Utsted klientsertifikat for tjeneste-A\nopenssl genpkey -algorithm RSA \\\n  -pkeyopt rsa_keygen_bits:2048 \\\n  -out endcerts\/tjeneste-a.key.pem\n\nopenssl req -new -sha256 \\\n  -key endcerts\/tjeneste-a.key.pem \\\n  -subj \"\/C=NO\/ST=Oslo\/O=MinBedrift\/CN=tjeneste-a\" \\\n  -out endcerts\/tjeneste-a.csr.pem\n\nopenssl ca -config intermediate\/openssl.cnf \\\n  -extensions usr_cert \\\n  -days 365 -notext -md sha256 \\\n  -in endcerts\/tjeneste-a.csr.pem \\\n  -out endcerts\/tjeneste-a.cert.pem<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ mtls-server.js - Server som krever klientsertifikat\nconst https = require('https');\nconst fs = require('fs');\n\nconst server = https.createServer({\n  cert: fs.readFileSync('\/root\/myca\/endcerts\/api.intern.local.cert.pem'),\n  key: fs.readFileSync('\/root\/myca\/endcerts\/api.intern.local.key.pem'),\n  ca: fs.readFileSync('\/root\/myca\/intermediate\/certs\/ca-chain.cert.pem'),\n  requestCert: true,       \/\/ Krev klientsertifikat\n  rejectUnauthorized: true, \/\/ Avvis klienter uten gyldig sertifikat\n}, (req, res) => {\n  const cert = req.socket.getPeerCertificate();\n  if (!cert || !cert.subject) {\n    res.writeHead(401, { 'Content-Type': 'application\/json' });\n    return res.end(JSON.stringify({ error: 'Klientsertifikat mangler' }));\n  }\n  \/\/ Sjekk at klienten er fra din egen CA\n  console.log('Autentisert klient:', cert.subject.CN);\n  res.writeHead(200, { 'Content-Type': 'application\/json' });\n  res.end(JSON.stringify({ message: `Velkommen, ${cert.subject.CN}` }));\n});\n\nserver.listen(8443, () => console.log('mTLS-server klar p\u00e5 port 8443'));<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-10-sertifikatfornying-og-tilbakekalling\">Steg 10: Sertifikatfornying og tilbakekalling<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sertifikater med 365 dagers levetid m\u00e5 fornyes. Sett opp automatisk overv\u00e5king og CRL-generering:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Sjekk utl\u00f8psdato\nopenssl x509 -noout -enddate -in endcerts\/api.intern.local.cert.pem\n# Forventet: notAfter=Jun 20 10:00:00 2027 GMT\n\n# Sjekk antall dager til utl\u00f8p\npython3 -c \"\nfrom datetime import datetime\nimport subprocess\nresult = subprocess.run(['openssl','x509','-noout','-enddate','-in',\n  'endcerts\/api.intern.local.cert.pem'], capture_output=True, text=True)\ndate_str = result.stdout.strip().split('=')[1]\nexp = datetime.strptime(date_str, '%b %d %H:%M:%S %Y %Z')\ndays = (exp - datetime.utcnow()).days\nprint(f'Utl\u00f8per om {days} dager')\n\"\n\n# Tilbakekall et kompromittert sertifikat\nopenssl ca -config intermediate\/openssl.cnf \\\n  -revoke intermediate\/newcerts\/1000.pem\n\n# Generer ny Certificate Revocation List (CRL)\nopenssl ca -config intermediate\/openssl.cnf \\\n  -gencrl \\\n  -out intermediate\/crl\/intermediate.crl.pem\n\n# Valider sertifikat mot CRL\ncat intermediate\/certs\/ca-chain.cert.pem \\\n    intermediate\/crl\/intermediate.crl.pem > \/tmp\/chain-crl.pem\nopenssl verify -crl_check \\\n  -CAfile \/tmp\/chain-crl.pem \\\n  endcerts\/api.intern.local.cert.pem<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">CRL er gyldig i 30 dager som standard (satt i <code>default_crl_days<\/code>). Distribuer CRL til et internt endepunkt og oppdater den automatisk via cron. For sanntidsstatus er OCSP en bedre l\u00f8sning enn CRL, men krever en OCSP-responder-tjeneste.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-11-automatiser-med-bash-skript\">Steg 11: Automatiser med bash-skript<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For produksjonsbruk l\u00f8nner det seg \u00e5 automatisere sertifikatutstedelse. Her er et skript som h\u00e5ndterer n\u00f8kkel, CSR og sertifikat i \u00e9n operasjon:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# issue-cert.sh - Automatisk sertifikatutstedelse fra privat CA\n# Bruk: .\/issue-cert.sh <domenenavn> [ip-adresse]\n\nset -euo pipefail\n\nDOMAIN=\"${1:?Bruk: $0 <domenenavn> [ip]}\"\nIP=\"${2:-}\"\nCA_DIR=\"\/root\/myca\"\nCERT_DIR=\"$CA_DIR\/endcerts\"\nDAYS=365\n\n# Generer n\u00f8kkel (ukryptert for automatisering)\nopenssl genpkey -algorithm RSA \\\n  -pkeyopt rsa_keygen_bits:2048 \\\n  -out \"$CERT_DIR\/$DOMAIN.key.pem\" 2>\/dev\/null\nchmod 400 \"$CERT_DIR\/$DOMAIN.key.pem\"\n\n# Bygg SAN-streng\nSAN=\"DNS:$DOMAIN\"\n[ -n \"$IP\" ] && SAN=\"$SAN,IP:$IP\"\n\n# Lag CSR med SAN\nopenssl req -new -sha256 \\\n  -key \"$CERT_DIR\/$DOMAIN.key.pem\" \\\n  -subj \"\/C=NO\/ST=Oslo\/O=MinBedrift\/CN=$DOMAIN\" \\\n  -addext \"subjectAltName=$SAN\" \\\n  -out \"$CERT_DIR\/$DOMAIN.csr.pem\" 2>\/dev\/null\n\n# Signer med mellom-CA (batch-modus, ingen brukerinteraksjon)\nopenssl ca -config \"$CA_DIR\/intermediate\/openssl.cnf\" \\\n  -extensions server_cert \\\n  -days \"$DAYS\" -notext -md sha256 -batch \\\n  -in \"$CERT_DIR\/$DOMAIN.csr.pem\" \\\n  -out \"$CERT_DIR\/$DOMAIN.cert.pem\" 2>\/dev\/null\n\n# Verifiser\nopenssl verify \\\n  -CAfile \"$CA_DIR\/intermediate\/certs\/ca-chain.cert.pem\" \\\n  \"$CERT_DIR\/$DOMAIN.cert.pem\" || { echo \"FEIL: Sertifikatvalidering feilet\"; exit 1; }\n\necho \"OK: Sertifikat utstedt for $DOMAIN (gyldig $DAYS dager)\"\necho \"  N\u00f8kkel:      $CERT_DIR\/$DOMAIN.key.pem\"\necho \"  Sertifikat:  $CERT_DIR\/$DOMAIN.cert.pem\"\necho \"  CA-kjede:    $CA_DIR\/intermediate\/certs\/ca-chain.cert.pem\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"steg-12-integrer-i-docker-og-kubernetes\">Steg 12: Integrer i Docker og Kubernetes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I containerbaserte milj\u00f8er er det standard \u00e5 montere CA-sertifikater som ConfigMap-ressurser eller volumes. Her er de to vanligste tiln\u00e6rmingene:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Kubernetes: opprett ConfigMap med CA-kjeden\nkubectl create configmap intern-ca \\\n  --from-file=ca.crt=\/root\/myca\/intermediate\/certs\/ca-chain.cert.pem\n\n# Bruk ConfigMap i Pod-spesifikasjon\nkubectl apply -f - <<'EOF'\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: api-server\nspec:\n  template:\n    spec:\n      containers:\n      - name: api\n        image: node:22-alpine\n        env:\n        - name: NODE_EXTRA_CA_CERTS\n          value: \/etc\/ssl\/custom\/ca.crt\n        volumeMounts:\n        - name: intern-ca\n          mountPath: \/etc\/ssl\/custom\n      volumes:\n      - name: intern-ca\n        configMap:\n          name: intern-ca\nEOF\n\n# Docker: legg til CA i basis-image\n# Dockerfile:\n# FROM node:22-alpine\n# COPY intermediate\/certs\/ca-chain.cert.pem \/usr\/local\/share\/ca-certificates\/intern-ca.crt\n# RUN apk add --no-cache ca-certificates &#038;&#038; update-ca-certificates\n# ENV NODE_EXTRA_CA_CERTS=\/usr\/local\/share\/ca-certificates\/intern-ca.crt\n\n# Test TLS fra container\ndocker run --rm -it \\\n  -v \/root\/myca\/intermediate\/certs\/ca-chain.cert.pem:\/certs\/ca.crt \\\n  -e NODE_EXTRA_CA_CERTS=\/certs\/ca.crt \\\n  node:22-alpine \\\n  node -e \"const https=require('https'); https.get('https:\/\/api.intern.local:8443', r=>console.log(r.statusCode))\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vanlige-fallgruver-og-feilsoking\">Vanlige fallgruver og feils\u00f8king<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Her er de 8 mest vanlige feilene ved oppsett av privat certificate authority, og n\u00f8yaktig hva du gj\u00f8r med dem:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Feil \/ Feilkode<\/th><th>\u00c5rsak<\/th><th>L\u00f8sning<\/th><\/tr><\/thead><tbody><tr><td><code>CERT_HAS_EXPIRED<\/code><\/td><td>Sertifikatet er utl\u00f8pt<\/td><td>Utsted nytt sertifikat. Sett opp cron-jobb for varsling 30 dager f\u00f8r utl\u00f8p.<\/td><\/tr><tr><td><code>UNABLE_TO_VERIFY_LEAF_SIGNATURE<\/code><\/td><td>CA-kjeden er ufullstendig<\/td><td>Bruk <code>ca-chain.cert.pem<\/code> (mellom-CA + rot-CA), ikke bare ett av dem.<\/td><\/tr><tr><td><code>ERR_TLS_CERT_ALTNAME_INVALID<\/code><\/td><td>SAN mangler domenenavnet<\/td><td>Legg til korrekt DNS-navn i <code>subjectAltName<\/code> ved utstedelse. CN alene holder ikke.<\/td><\/tr><tr><td><code>HOSTNAME_MISMATCH<\/code><\/td><td>Hostnavn matcher ikke SAN<\/td><td>Sertifikatet ble utstedt for feil domene. Utsted nytt med riktig SAN.<\/td><\/tr><tr><td><code>CA_E_KEYFILE_ERROR<\/code><\/td><td>Feil passord p\u00e5 CA-n\u00f8kkel<\/td><td>CA-n\u00f8kkelpassordet er case-sensitivt. Bruk en passordbehandler for \u00e5 lagre det.<\/td><\/tr><tr><td>Avvist av Firefox<\/td><td>Firefox har egen truststore<\/td><td>Importer CA manuelt: Innstillinger > Personvern > Sertifikater > Importer.<\/td><\/tr><tr><td><code>index.txt<\/code>-konflikt<\/td><td>Samme subjekt allerede i databasen<\/td><td>Revoke det gamle sertifikatet, eller legg til <code>unique_subject = no<\/code> i openssl.cnf.<\/td><\/tr><tr><td>OpenSSL 3.x n\u00f8kkelformat<\/td><td>Ulike standarder fra OpenSSL 1.x<\/td><td>Bruk <code>openssl pkey<\/code> i stedet for <code>openssl rsa<\/code> for n\u00f8kkeloperasjoner i 3.x.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"nyttige-feilsokingskommandoer\">Nyttige feils\u00f8kingskommandoer<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Vis fullstendig sertifikatinfo\nopenssl x509 -noout -text -in endcerts\/api.intern.local.cert.pem\n\n# Test TLS-tilkobling direkte\nopenssl s_client -connect api.intern.local:8443 \\\n  -CAfile intermediate\/certs\/ca-chain.cert.pem \\\n  -showcerts 2>&1 | head -30\n\n# Sjekk at n\u00f8kkel og sertifikat matcher (md5 M\u00c5 v\u00e6re identisk)\nopenssl x509 -noout -modulus -in endcerts\/api.intern.local.cert.pem | md5sum\nopenssl pkey -noout -modulus -in endcerts\/api.intern.local.key.pem | md5sum\n\n# Vis CA-databasen\ncat intermediate\/index.txt\n\n# Dekod en CSR\nopenssl req -noout -text -in endcerts\/api.intern.local.csr.pem\n\n# Test med curl mot intern server\ncurl -v --cacert intermediate\/certs\/ca-chain.cert.pem \\\n  https:\/\/api.intern.local:8443\/health<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"avanserte-tips-ocsp-og-step-ca\">Avanserte tips: OCSP og Step CA<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00e5r basisoppsettet fungerer, er det to avanserte funksjoner som er s\u00e6rlig verdifulle for norske produksjonsmilj\u00f8er:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OCSP (Online Certificate Status Protocol)<\/strong> gir sertifikatstatus i sanntid, i motsetning til CRL som bufres og kan v\u00e6re opptil 30 dager gammel. OpenSSL har en innebygd OCSP-responder for test og utvikling:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Start OCSP-responder (for utvikling)\nopenssl ocsp \\\n  -index intermediate\/index.txt \\\n  -CA intermediate\/certs\/ca-chain.cert.pem \\\n  -rsigner intermediate\/certs\/intermediate.cert.pem \\\n  -rkey intermediate\/private\/intermediate.key.pem \\\n  -port 2560 &\n\n# Sjekk status for et sertifikat\nopenssl ocsp \\\n  -CAfile intermediate\/certs\/ca-chain.cert.pem \\\n  -url http:\/\/localhost:2560 \\\n  -issuer intermediate\/certs\/intermediate.cert.pem \\\n  -cert endcerts\/api.intern.local.cert.pem\n\n# Forventet output for gyldig sertifikat:\n# endcerts\/api.intern.local.cert.pem: good\n# This Update: Jun 20 10:00:00 2026 GMT<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For produksjon anbefales <strong>Step CA<\/strong> fra Smallstep som et moderne alternativ. Step CA gir automatisk sertifikatfornying via ACME-protokollen, et REST-API, fullstendig auditlogg og innebygd OCSP. Se <a href=\"https:\/\/smallstep.com\/docs\/step-cli\/reference\/certificate\/create\/\" rel=\"noopener\" target=\"_blank\">Smallstep sin dokumentasjon for step certificate create<\/a> for oppsett.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Wildcard-sertifikater<\/strong> (<code>*.api.intern.local<\/code>) st\u00f8ttes fullt ut med privat CA. Legg til <code>DNS:*.api.intern.local<\/code> i SAN-feltet. Et wildcard dekker bare ett domenelagniv\u00e5: <code>*.api.intern.local<\/code> dekker <code>v1.api.intern.local<\/code> men ikke <code>v1.v2.api.intern.local<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"sikkerhetshensyn-og-beste-praksis-2026\">Sikkerhetshensyn og beste praksis 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En privat certificate authority er et kritisk sikkerhetsm\u00e5l. Kompromittering av rot-CA-n\u00f8kkelen krever full re-distribusjon av tillitsankere til alle klienter. Her er de viktigste tiltakene:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Tiltak<\/th><th>Prioritet<\/th><th>Beskrivelse<\/th><\/tr><\/thead><tbody><tr><td>Hold rot-CA offline<\/td><td>Kritisk<\/td><td>Lagre rot-CA-n\u00f8kkelen p\u00e5 kryptert USB eller HSM. Koble til kun for \u00e5 signere mellom-CA.<\/td><\/tr><tr><td>Krypter alle private n\u00f8kler<\/td><td>Kritisk<\/td><td>Bruk alltid <code>-aes256<\/code> for CA-n\u00f8kler. Ukrypterte CA-n\u00f8kler er ikke akseptable.<\/td><\/tr><tr><td>Bruk OpenSSL 3.5 LTS<\/td><td>H\u00f8y<\/td><td>OpenSSL 3.1 er end-of-life (mars 2025). OpenSSL 3.3 er end-of-life (april 2026). Bruk 3.5 LTS.<\/td><\/tr><tr><td>Sett korte levetider<\/td><td>H\u00f8y<\/td><td>Maksimalt 365 dager for sluttsertifikater. 90 dager med autorenewing er ideelt.<\/td><\/tr><tr><td>Audit CA-databasen<\/td><td>Medium<\/td><td>Sjekk <code>index.txt<\/code> regelmessig for ukjente sertifikater. Revoke umiddelbart ved mistanke.<\/td><\/tr><tr><td>Sikkerhetskopier CA<\/td><td>H\u00f8y<\/td><td>Kryptert backup av hele CA-katalogen. Test gjenoppretting kvartalsvis.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">RFC 5280 (<a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc5280\" rel=\"noopener\" target=\"_blank\">Internet X.509 Public Key Infrastructure Certificate and CRL Profile<\/a>) er den tekniske referansestandarden for X.509-sertifikater. Bruk alltid <code>sha256<\/code> eller sterkere. OpenSSL 3.5 avviser <code>sha1<\/code> for ny sertifikatutstedelse som standard. Hold OpenSSL oppdatert ved \u00e5 f\u00f8lge <a href=\"https:\/\/www.openssl.org\/source\/\" rel=\"noopener\" target=\"_blank\">OpenSSL release-side<\/a>, der LTS-linjens siste versjon (3.5.7 per juni 2026) dokumenteres.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"privat-ca-vs-lets-encrypt-vs-kommersiell-ca\">Privat CA vs Let's Encrypt vs kommersiell CA<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Funksjon<\/th><th>Privat CA (OpenSSL)<\/th><th>Let's Encrypt<\/th><th>Kommersiell CA<\/th><\/tr><\/thead><tbody><tr><td>Pris<\/td><td>Gratis<\/td><td>Gratis<\/td><td>$10\u2013$1.000+\/\u00e5r<\/td><\/tr><tr><td>Krever offentlig DNS<\/td><td>Nei<\/td><td>Ja<\/td><td>Ja<\/td><\/tr><tr><td>Gyldighetstid<\/td><td>Valgfri<\/td><td>90 dager<\/td><td>1 \u00e5r (maks)<\/td><\/tr><tr><td>Automatisk fornying<\/td><td>Manuelt\/skript<\/td><td>ACME (certbot)<\/td><td>Avhengig av leverand\u00f8r<\/td><\/tr><tr><td>Intern bruk uten DNS<\/td><td>Ja<\/td><td>Nei<\/td><td>Nei<\/td><\/tr><tr><td>Wildcard<\/td><td>Ja, gratis<\/td><td>Ja (DNS-01)<\/td><td>Ja (ekstra kostnad)<\/td><\/tr><tr><td>CT-logging<\/td><td>Nei<\/td><td>Ja (alltid)<\/td><td>Ja (alltid)<\/td><\/tr><tr><td>Nettlesertillit uten installasjon<\/td><td>Nei<\/td><td>Ja<\/td><td>Ja<\/td><\/tr><tr><td>mTLS klientsertifikater<\/td><td>Ja<\/td><td>Nei<\/td><td>Avhengig<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For utviklingsmilj\u00f8er er <code>mkcert<\/code> et alternativ til full OpenSSL-oppsett. <code>mkcert<\/code> oppretter automatisk en lokal CA og installerer den i systemets truststore med \u00e9n kommando. Les om <a href=\"https:\/\/letsencrypt.org\/docs\/certificates-for-localhost\/\" rel=\"noopener\" target=\"_blank\">Let's Encrypt sine anbefalinger for localhost-sertifikater<\/a> for \u00e5 se n\u00e5r privat CA er bedre enn Let's Encrypt for lokalt utviklingsarbeid.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"node-js-tls-api-nokkelparametere\">Node.js TLS API: n\u00f8kkelparametere<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Her er en komplett oversikt over de viktigste TLS-parameterne i Node.js for arbeid med privat CA. Se den offisielle <a href=\"https:\/\/nodejs.org\/api\/tls.html\" rel=\"noopener\" target=\"_blank\">Node.js TLS-dokumentasjonen<\/a> for fullstendige detaljer:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Parameter<\/th><th>Type<\/th><th>Bruk<\/th><\/tr><\/thead><tbody><tr><td><code>ca<\/code><\/td><td>string\/Buffer\/array<\/td><td>PEM-kodet CA-sertifikat(er). Overskriver Node.js sine innebygde CA-er hvis angitt.<\/td><\/tr><tr><td><code>cert<\/code><\/td><td>string\/Buffer\/array<\/td><td>PEM-kodet sertifikat for serveren eller klienten.<\/td><\/tr><tr><td><code>key<\/code><\/td><td>string\/Buffer\/array<\/td><td>PEM-kodet privat n\u00f8kkel. Separate fra sertifikatet.<\/td><\/tr><tr><td><code>passphrase<\/code><\/td><td>string<\/td><td>Passord for kryptert privat n\u00f8kkel.<\/td><\/tr><tr><td><code>rejectUnauthorized<\/code><\/td><td>boolean<\/td><td>Avvis tilkoblinger med ugyldig sertifikat. Alltid <code>true<\/code> i produksjon.<\/td><\/tr><tr><td><code>requestCert<\/code><\/td><td>boolean<\/td><td>Krev klientsertifikat (mTLS). Kombineres med <code>rejectUnauthorized: true<\/code>.<\/td><\/tr><tr><td><code>minVersion<\/code><\/td><td>string<\/td><td>Minimum TLS-versjon. Sett til <code>'TLSv1.2'<\/code> eller <code>'TLSv1.3'<\/code>.<\/td><\/tr><tr><td><code>secureContext<\/code><\/td><td>object<\/td><td>Gjenbrukbar TLS-kontekst fra <code>tls.createSecureContext()<\/code> for ytelsesoptimering.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"relatert-innhold\">Relatert innhold<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"relatert-dekning\">Relatert dekning<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"\/no\/https-tls-13-nodejs\/\">HTTPS og TLS 1.3 i Node.js: 12 steg for sikker kommunikasjon<\/a><\/li><li><a href=\"\/no\/ed25519-vs-rsa\/\">Ed25519 vs RSA: 33x raskere signaturer, 128-bit sikkerhet [2026]<\/a><\/li><li><a href=\"\/no\/ssh-nokkel-linux\/\">SSH-n\u00f8kkel i Linux: sikker innlogging uten passord<\/a><\/li><li><a href=\"\/no\/digitale-signaturer\/\">Digitale signaturer: hashfunksjoner og asymmetriske n\u00f8kler<\/a><\/li><li><a href=\"\/no\/oauth-2-0-nodejs\/\">OAuth 2.0 i Node.js: Sikker autorisasjon i 12 steg<\/a><\/li><li><a href=\"\/no\/cryptography\/\">Kryptografi: hashfunksjoner, SHA og digital tillit<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ofte-stilte-sporsmal\">Ofte stilte sp\u00f8rsm\u00e5l<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"kan-jeg-bruke-en-privat-ca-for-offentlige-nettsider\">Kan jeg bruke en privat CA for offentlige nettsider?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Nei. Sertifikater fra en privat certificate authority er bare betrodd av klienter der du har installert rot-sertifikatet manuelt. For offentlige nettsider trenger du en offentlig betrodd CA som Let's Encrypt, DigiCert eller Sectigo. Privat CA egner seg utelukkende for intern infrastruktur.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"hvilken-openssl-versjon-bor-jeg-bruke-i-2026\">Hvilken OpenSSL-versjon b\u00f8r jeg bruke i 2026?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Bruk OpenSSL 3.5 (LTS), st\u00f8ttet til april 2030. OpenSSL 4.0.1 er den nyeste utgivelsen fra juni 2026, men er ikke LTS. Unng\u00e5 OpenSSL 3.0 (end-of-life september 2026), 3.1 (end-of-life mars 2025) og 3.3 (end-of-life april 2026) for ny CA-infrastruktur.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"hva-er-forskjellen-pa-rsa-og-ecdsa-for-ca-nokler\">Hva er forskjellen p\u00e5 RSA og ECDSA for CA-n\u00f8kler?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">RSA 4096 gir h\u00f8yest kompatibilitet med eldre systemer og er det sikreste valget for heterogene klientfl\u00e5ter. ECDSA P-384 gir tilsvarende sikkerhet med 3\u20134 ganger raskere operasjoner og markant mindre n\u00f8kler og sertifikater. For nye CA-installasjoner i 2026 med moderne klienter er ECDSA P-384 foretrukket.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"hvor-lenge-bor-sertifikater-vaere-gyldige\">Hvor lenge b\u00f8r sertifikater v\u00e6re gyldige?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sluttsertifikater: maks 365 dager, ideelt 90 dager med automatisk fornying. Mellom-CA: 10 \u00e5r er standard. Rot-CA: 20 \u00e5r er vanlig. Kortere gyldighetsperioder reduserer skadeomfanget ved kompromittering. CA-browser Forum vurderer \u00e5 senke maksimum til 90 dager for offentlige CA-er i 2026.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"kan-node-js-bruke-privat-ca-uten-kodeendringer\">Kan Node.js bruke privat CA uten kodeendringer?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ja. Sett milj\u00f8variabelen <code>NODE_EXTRA_CA_CERTS=\/sti\/til\/ca-chain.cert.pem<\/code> f\u00f8r du starter Node.js. Da legges CA-en automatisk til Node.js sine tillitsankere uten \u00e5 endre koden. Alternativt kan du installere CA i systemets truststore via <code>update-ca-certificates<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"hva-gjor-jeg-hvis-rot-ca-nokkelen-kompromitteres\">Hva gj\u00f8r jeg hvis rot-CA-n\u00f8kkelen kompromitteres?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Du m\u00e5 distribuere et nytt rot-sertifikat til alle klienter, tilbakekalle alle eksisterende sertifikater og starte PKI-hierarkiet p\u00e5 nytt. Nettopp derfor skal rot-CA alltid holdes offline, og mellom-CA-laget er kritisk for \u00e5 begrense eksponering.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"er-det-trygt-a-bruke-rejectunauthorized-false-i-node-js\">Er det trygt \u00e5 bruke rejectUnauthorized: false i Node.js?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Aldri i produksjon. Dette deaktiverer all TLS-sertifikatvalidering og gj\u00f8r applikasjonen s\u00e5rbar for man-in-the-middle-angrep. Bruk det kun i isolerte utviklingsmilj\u00f8er, og fjern det alltid f\u00f8r koden g\u00e5r til produksjon. Den korrekte l\u00f8sningen er <code>NODE_EXTRA_CA_CERTS<\/code> eller <code>ca<\/code>-parameteret.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"fungerer-wildcard-sertifikater-med-privat-ca\">Fungerer wildcard-sertifikater med privat CA?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ja, og gratis. Legg til <code>DNS:*.domene.intern<\/code> i <code>subjectAltName<\/code> ved utstedelse. Et wildcard dekker bare ett domenelagniv\u00e5. <code>*.api.intern<\/code> dekker <code>v1.api.intern<\/code> men ikke <code>v1.v2.api.intern<\/code>. For dypere hierarkier trenger du separate SAN-oppf\u00f8ringer.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En privat certificate authority lar deg signere dine egne TLS-sertifikater uten \u00e5 betale for hvert enkelt, og uten \u00e5 stole p\u00e5 en tredjepart. For interne API-er, mikrotjenester, utviklingsmilj\u00f8er og IoT-fl\u00e5ter\u2026<\/p>\n","protected":false},"author":4,"featured_media":143,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-142","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cryptography"],"_links":{"self":[{"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/posts\/142","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/comments?post=142"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/posts\/142\/revisions"}],"predecessor-version":[{"id":144,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/posts\/142\/revisions\/144"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/media\/143"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/media?parent=142"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/categories?post=142"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/no\/wp-json\/wp\/v2\/tags?post=142"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}