Compare commits

...

7 Commits

4 changed files with 64 additions and 28 deletions

View File

@ -36,19 +36,24 @@ certificate: when prompted for "Common Name", say "Certificate
Authority" or something like that Authority" or something like that
``` ```
openssl genrsa -out ca.key 4096 openssl genrsa -out private/ca.key 4096
openssl req -addext basicConstraints=critical,CA:TRUE,pathlen:1 --x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt openssl req -addext basicConstraints=critical,CA:TRUE,pathlen:0 --x509 -new -nodes -key private/ca.key -sha256 -days 3650 -out certs/ca.crt
``` ```
### Server key and cert ### Server key and cert
The certifix service is exposed over HTTPS, so it needs its own The certifix service is exposed over HTTPS, so it needs its own
certificate signed by the CA. Use your hostname when prompted for certificate signed by the CA. Use your hostname when prompted for
Common Name Common Name. If your server host is reachable using different
names from different clients then you need to provide _all_ of them
as `subjectAltName` - as in the example below.
``` ```
openssl req -newkey rsa:2048 -nodes -keyout server.key --out server.csr # it's important to list all the hostnames of your server machine in the
openssl x509 -req -in server.csr -days 365 -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt # subjectAltName field
openssl req -newkey rsa:2048 -addext "subjectAltName = DNS:loaclhost.lan,DNS:localhost,DNS:loaclhost.telent.net" -nodes -keyout private/server.key --out certs/server.csr
openssl x509 -req -in certs/server.csr -days 365 -CA certs/ca.crt -CAkey private/ca.key -CAcreateserial -copy_extensions copyall -out certs/server.crt
``` ```
### Build and start the server ### Build and start the server
@ -61,9 +66,14 @@ it to check client certificate requests against.
echo 'loves labours lost' > psk echo 'loves labours lost' > psk
chmod 0700 psk chmod 0700 psk
nix-build nix-build
result/bin/certifix --challenge-password psk --ca-certificate ca.crt --ca-private-key --server-certificate server.crt --server-private-key server.key localhost:19613 result/bin/certifix --challenge-password psk --ca-certificate certs/ca.crt --ca-private-key private/ca.key --server-certificate certs/server.crt --server-private-key private/server.key localhost:19613
``` ```
* To listen on all interfaces, use a wildcard address instead of
`localhost` - e.g. `::0:19613' will (at least on my machine) accept
IPv4 and IPv6 connections on any interface
### Try it and see if it works ### Try it and see if it works
To set the `challengePassword` attribute in a CSR using OpenSSL, you To set the `challengePassword` attribute in a CSR using OpenSSL, you
@ -76,33 +86,37 @@ need to create a configuration file. Copy `openssl.cnf.example` to
``` ```
# make CSR # make CSR
CN=mydevice openssl req -config openssl.cnf -newkey rsa:2048 -nodes -keyout client.key -out client.csr CN=mydevice openssl req -config openssl.cnf -addext "subjectAltName = DNS:mydevice.lan" -newkey rsa:2048 -nodes -keyout client.key -out client.csr
# send it to certifix, should get a certificate in response # send it to certifix, should get a certificate in response
curl --cacert ca.crt -v -H 'content-type: application/x-pem-file' --data-binary @client.csr https://localhost:19613/sign curl --cacert certs/ca.crt -v -H 'content-type: application/x-pem-file' --data-binary @client.csr https://localhost:19613/sign
``` ```
## Reasons this is not secure ## Reasons this is not secure
* the CA key is readable by and present in the memory of the process * the CA key is readable by and present in the memory of the process
that reads and parses network requests. Bearing in mind the the whole that reads and parses network requests. Bearing in mind the the
point is to automate signing we can only do so much about this, but at whole point is to automate signing we can only do so much about
least we could move the actual signing to a separate process which is this, but at least we could move the actual signing to a separate
only invoked once an acceptable request has been received. process which is only invoked once an acceptable request has been
received.
* there is no intermediate key - the requests are signed directly by the root CA * on that note, in the setup that this README describes there is no
intermediate key - the requests are signed directly by the root
CA. For a robust setup you might consider ignoring my instructions
for generating a self-signed root cert, and instead create a CA
signed by an existing (offline?) root CA
* I haven't checked that the protocols or the ciphers are restricted * I haven't checked that the protocols or the ciphers are restricted
to modern and sensible defaults to modern and sensible defaults
* doesn't set 4.2.1.6. Subject Alternative Name * doesn't call SSL shutdown at the end of the response, so you may get
"unexpected EOF" errors
* doesn't set Key Usage extension (https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3) * it has _not_ been reviewed by anyone who knows about TLS best
practices. If you are that person, please look at the new-crt
* probably has wrong basicConstraints in CA cert function in [main.fnl](main.fnl) and let me know :-)
* likewise other TLS best practices
## Background ## Background

0
certs/.gitkeep Normal file
View File

View File

@ -6,6 +6,7 @@
(local ctx (require :openssl.ssl.context)) (local ctx (require :openssl.ssl.context))
(local csr (require :openssl.x509.csr)) (local csr (require :openssl.x509.csr))
(local x509 (require :openssl.x509)) (local x509 (require :openssl.x509))
(local extension (require :openssl.x509.extension))
(local pkey (require :openssl.pkey)) (local pkey (require :openssl.pkey))
(local bignum (require :openssl.bignum)) (local bignum (require :openssl.bignum))
@ -64,6 +65,8 @@
(assoc (parse-args rest) :server-private-key (slurp f)) (assoc (parse-args rest) :server-private-key (slurp f))
["--challenge-password" f & rest] ["--challenge-password" f & rest]
(assoc (parse-args rest) :challenge-password (read-line f)) (assoc (parse-args rest) :challenge-password (read-line f))
["--sign-file" f & rest]
(assoc (parse-args rest) :sign-and-exit (slurp f))
[bind-address] { : bind-address } [bind-address] { : bind-address }
_ {})) _ {}))
@ -71,6 +74,8 @@
(doto (doto
(parse-args arg) (parse-args arg)
(case (case
{:sign-and-exit pathname }
true
{: ca-certificate : ca-private-key {: ca-certificate : ca-private-key
: server-certificate : server-private-key : server-certificate : server-private-key
: challenge-password : bind-address} : challenge-password : bind-address}
@ -84,14 +89,21 @@
(fn new-crt [csr] (fn new-crt [csr]
(let [crt (let [crt
(doto (x509.new) (doto (x509.new)
(: :setVersion 2) (: :setVersion 3)
(: :setSerial (make-serial)) (: :setSerial (make-serial))
(: :setIssuer (ca-crt:getSubject)) (: :setIssuer (ca-crt:getSubject))
(: :setLifetime (os.time) (+ (* 365 86400) (os.time))) (: :setLifetime (os.time) (+ (* 365 86400) (os.time)))
(: :setSubject (csr:getSubject)) (: :setSubject (csr:getSubject))
(: :setPublicKey (csr:getPublicKey)) (: :setPublicKey (csr:getPublicKey)))]
(: :sign ca-key))] (for [i 1 (csr:getRequestedExtensionCount) 1]
(crt:toPEM))) (let [ext (csr:getRequestedExtension i)]
(crt:addExtension ext)))
;; https://www.golinuxcloud.com/add-x509-extensions-to-certificate-openssl/
(doto crt
(: :addExtension (extension.new "basicConstraints" "critical,CA:FALSE"))
(: :addExtension (extension.new "keyUsage" "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment")) ;; all of these?
(: :addExtension (extension.new "extendedKeyUsage" "clientAuth"))
(: :sign ca-key))))
(fn approve-request? [csr] (fn approve-request? [csr]
(let [challengePassword (csr:getAttribute :challengePassword)] (let [challengePassword (csr:getAttribute :challengePassword)]
@ -105,7 +117,7 @@
(if (approve-request? req) (if (approve-request? req)
(do (do
(out:write_headers (make-headers 200 { :content-type "text/plain" }) false) (out:write_headers (make-headers 200 { :content-type "text/plain" }) false)
(out:write_chunk (new-crt req) true)) (out:write_chunk (: (new-crt req) :toPEM) true))
(send-error out 400 "missing attributes in CSR")))) (send-error out 400 "missing attributes in CSR"))))
(fn on-stream [sv out] (fn on-stream [sv out]
@ -136,7 +148,17 @@
:ctx (ssl-context) :ctx (ssl-context)
})))) }))))
(let [s (new-server)] (fn run-server []
(ncall (s:listen)) (let [s (new-server)]
(print "server ready") (ncall (s:listen))
(ncall (s:loop))) (print "server ready")
(ncall (s:loop))))
(fn sign-text [s]
(let [crt (new-crt (csr.new s))]
(print (crt:toPEM))))
(if options.sign-and-exit
(sign-text options.sign-and-exit)
(run-server))

0
private/.gitkeep Normal file
View File