File: client-certificates-example-static.lisp

package info (click to toggle)
acl2 8.6%2Bdfsg-2
  • links: PTS
  • area: main
  • in suites: trixie
  • size: 1,111,420 kB
  • sloc: lisp: 17,818,294; java: 125,359; python: 28,122; javascript: 23,458; cpp: 18,851; ansic: 11,569; perl: 7,678; xml: 5,591; sh: 3,976; makefile: 3,833; ruby: 2,633; yacc: 1,126; ml: 763; awk: 295; csh: 233; lex: 197; php: 178; tcl: 49; asm: 23; haskell: 17
file content (107 lines) | stat: -rw-r--r-- 5,173 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
;;;; The code contained in this file implements a trivial server and a
;;;; client  that connects  to the  former and  provide a  self signed
;;;; certificate.  The server  is  able to  implement  a procedure  to
;;;; reject or  accept the  client connection,  based on  the client's
;;;; certificate, and using some  form of authentication.  For example
;;;; matching  the   certificate  fingerprint   with  a   database  of
;;;; certificates stored on disk, for example.

;; To generate both the keys and certificates, a command line like the
;; one below could be used:

;; openssl req -new -nodes -x509 -days 365 -subj / -keyout private-key -outform PEM -out certificate

;; The key points here are:

;; For the server
;; - create a context using :verify-mode cl+ssl:+ssl-verify-peer+

;; Optional only if you plan to use self signed client certificates

;; - save all the trusted client's  certificates in a directory of the
;;   server's filesystem (for example: "/certs/trusted-clients/") ;
;; - generate symbolic links to such certificates using this command

;;   # cd /certs/trusted-clients && c_rehash .

;; the step above is needed by libssl to match the certificate sent by
;; the client  with one  of those  saved on  the filesystem,  idf this
;; matching fails the connection is rejected.

;; For the client

;; - pass  certificate and key when  generating the stream as  you did
;; for the server

(ql:quickload "cl+ssl")

(ql:quickload "bordeaux-threads")

(ql:quickload "trivial-sockets")

(defun hash-array->string (array)
  (let ((res ""))
    (loop for i across array do
      (setf res
            (concatenate 'string
                         res
                         (format nil "~2,'0x" i))))
    res))

(defun start-trivial-server (port certificate key
                             &optional (client-certificates-directory :default))
 "Start a trivial server listening on `PORT' using the certificate
and key stored in the file pointed by the filesystem path
`CERTIFICATE' and `KEY' respectively. The argument
`CLIENT-CERTIFICATES-DIRECTORY' could be either a filesystem directory
containing the list of trusted client certificates or any legal value
for `CL+SSL:MAKE-CONTEXT'.

If the client certificates are self signed the aforementioned
directory must be passed as value for argument
`CLIENT-CERTIFICATES-DIRECTORY'."
  (format t "~&SSL server listening on port ~d~%" port)
  (bt:make-thread
   (lambda ()
     (trivial-sockets:with-server (server (:port port))
       (let* ((socket (trivial-sockets:accept-connection server
                                                         :element-type '(unsigned-byte 8)))
              (ctx (cl+ssl:make-context :verify-mode cl+ssl:+ssl-verify-peer+
                                        :verify-location client-certificates-directory)))
         (cl+ssl:with-global-context (ctx :auto-free-p t)
           (let* ((client-stream (cl+ssl:make-ssl-server-stream socket
                                                                :external-format nil
                                                                :certificate     certificate
                                                                :key             key))
                  (client-certificate      (cl+ssl:ssl-stream-x509-certificate client-stream))
                  (client-cert-fingerprint (cl+ssl:certificate-fingerprint client-certificate
                                                                           :sha256)))
             (let ((data (make-list 2)))
               (read-sequence data client-stream)
               (format t
                       "Server got from client (identified by ~s): ~s~%"
                       (hash-array->string client-cert-fingerprint)
                       (coerce (mapcar #'code-char data)
                               'string))
               (cl+ssl:x509-free client-certificate)
               (close socket)))))))))

(defun start-trivial-client (host port data certificate key)
  "Start a  client to connect with  the server as specified  by `HOST'
and  `PORT', sending  the first  two  characters of  the data  string:
`DATA' and using the certificate and key stored in the file pointed by
the filesystem path `CERTIFICATE' and `KEY' respectively"
  (let ((ctx (cl+ssl:make-context :verify-mode cl+ssl:+ssl-verify-none+)))
    (cl+ssl:with-global-context (ctx :auto-free-p t)
      (let* ((stream       (trivial-sockets:open-stream host port))
             (ssl-stream   (cl+ssl:make-ssl-client-stream stream
                                                          :certificate certificate
                                                          :key         key
                                                          :external-format nil
                                                          :unwrap-stream-p t
                                                          :verify          nil
                                                          :hostname        host)))
        (format t "sending ~a~%" data)
        (write-sequence (map 'vector #'char-code data) ssl-stream)
        (finish-output ssl-stream)
        (close stream)))))