commit 8e2ec2f9f14fc40455a6994227248df4d8e9067d
parent 08efd6d9f21b422d9c7770709267fb20f81254d1
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sat, 20 Sep 2014 13:35:49 +0200
read and write pem files
Diffstat:
| M | der.lisp |  |  | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- | 
1 file changed, 115 insertions(+), 2 deletions(-)
diff --git a/der.lisp b/der.lisp
@@ -34,6 +34,11 @@
 ;; http://tools.ietf.org/html/rfc5280
 ;; http://www.ietf.org/rfc/rfc3280.txt
 ;; https://en.wikipedia.org/wiki/X.509
+;; https://www.sslshopper.com/ssl-converter.html
+;; http://how2ssl.com/articles/working_with_pem_files/
+;; https://www.novell.com/support/kb/doc.php?id=7013103
+;; http://www.herongyang.com/Cryptography/Certificate-Format-PEM-on-Certificates.html
+;; http://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file
 
 (defun octets-to-utf8-string (x)
   #-(or sbcl)
@@ -371,6 +376,8 @@
 ;;(decode (rw:reader '(3 4 6 #x6e #x5d #xc0))) ; '(:bit-string "011011100101110111")
 ;;(encode w '(bit-string "011011100101110111")) ;; '(3 4 6 #x6e #x5d #xc0)
 
+;; http://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file
+
 (defun read-pem-key (reader)
   (let ((x (rw:till reader '(#\return #\newline))))
     (rw:skip reader)
@@ -382,7 +389,8 @@
                 #\P #\R #\I #\V #\A #\T #\E #\space
                 #\K #\E #\Y
                 #\- #\- #\- #\- #\-))
-       (prog1 (decode (rw.base64:decode-reader reader))
+       (prog1 (cons 'private-key (decode (rw.base64:decode-reader reader)))
+         ;;(rw:till (rw:peek-reader (rw.base64:decode-reader reader)))
          (rw:skip reader)
          (assert
           (equal '(#\- #\- #\- #\- #\-
@@ -397,7 +405,8 @@
                 #\B #\E #\G #\I #\N #\space
                 #\C #\E #\R #\T #\I #\F #\I #\C #\A #\T #\E
                 #\- #\- #\- #\- #\-))
-       (prog1 (decode (rw.base64:decode-reader reader))
+       (prog1 (cons 'certificate (decode (rw.base64:decode-reader reader)))
+         ;;(rw:till (rw:peek-reader (rw.base64:decode-reader reader)))
          (rw:skip reader)
          (assert
           (equal '(#\- #\- #\- #\- #\-
@@ -416,5 +425,109 @@
          collect (read-pem-key r)))))
 
 ;;(read-pem-file "~/sw/gvfs/test/files/testcert.pem")
+;;(read-pem-file "/tmp/a.pem")
+;;(read-pem-file "/tmp/b.pem")
 ;;openssl x509 -in ~/sw/gvfs/test/files/testcert.pem -noout -text
 
+(defun wrap-line-writer (writer columns)
+  (let ((n 0))
+    (lambda (x)
+      (if (<= (incf n) columns)
+          (funcall writer x)
+          (progn
+            (setq n 0)
+            (funcall writer #\newline))))))
+
+(defun wrap-line-reader (reader columns)
+  (let ((n 0))
+    (lambda ()
+      (cond
+        ((<= (incf n) columns)
+         (rw:next reader))
+        (t
+         (setq n 0)
+         #\newline)))))
+
+;; TODO write pem, wrap-line-writer 64 (76 normal)
+
+(defun decode-reader (reader)
+  (lambda ()
+    (decode reader)))
+
+(defun encode-writer (writer)
+  (lambda (x)
+    (encode writer x)))
+
+(defun encode-reader (reader &optional buffer)
+  (let* (done
+         (n 0)
+         (b (or buffer
+                (make-array 42 :fill-pointer 0 :adjustable t)))
+         (w (rw:writer b)))
+    (lambda ()
+      (unless done
+        (if (< n (length b))
+            (prog1 (aref b n)
+              (incf n))
+            (let ((x (rw:next reader)))
+              (cond
+                (x
+                 (setq n 0)
+                 (setf (fill-pointer b) 0)
+                 (encode w x)
+                 ;;(print b)
+                 (prog1 (aref b n)
+                   (incf n)))
+                (t
+                 (setq done t)
+                 nil))))))))
+
+(defun write-pem-key (writer x)
+  (destructuring-bind (tag &rest data) x
+    (rw:copy (rw:reader (ecase tag
+                          (private-key "-----BEGIN PRIVATE KEY-----")
+                          (certificate "-----BEGIN CERTIFICATE-----")))
+             writer)
+    (funcall writer #\newline)
+    (rw:copy (wrap-line-reader
+              (rw.base64:encode-reader
+               (rw:peek-reader (encode-reader (rw:reader (list data)))))
+              64)
+             writer)
+    #+nil
+    (rw:copy (rw.base64:encode-reader
+              (rw:peek-reader (encode-reader (rw:reader x))))
+             (wrap-line-writer (rw:char-writer s) 64))
+    (funcall writer #\newline)
+    (rw:copy (rw:reader (ecase tag
+                          (private-key "-----END PRIVATE KEY-----")
+                          (certificate "-----END CERTIFICATE-----")))
+             writer)
+    (funcall writer #\newline)))
+
+(defun write-pem-file (keys pathname &key if-does-not-exist if-exists)
+  (with-open-file (s pathname
+                     :direction :output
+                     :if-does-not-exist if-does-not-exist
+                     :if-exists if-exists)
+    (dolist (x keys)
+      (write-pem-key (rw:char-writer s) x))))
+
+;; diff -du ~/sw/gvfs/test/files/testcert.pem /tmp/b.pem
+#+nil
+(write-pem-file (read-pem-file "~/sw/gvfs/test/files/testcert.pem")
+                "/tmp/b.pem"
+                :if-does-not-exist :create
+                :if-exists :supersede)
+
+;;(print (read-pem-file "~/sw/gvfs/test/files/testcert.pem"))
+;;(print (read-pem-file "/tmp/b.pem"))
+;;(equalp (read-pem-file "~/sw/gvfs/test/files/testcert.pem") (read-pem-file "/tmp/b.pem"))
+
+;;(mapc 'read-pem-file (directory "/home/tomas/**/*.pem"))
+
+;; TODO some crt binary, some PEM
+#+nil
+(with-open-file (s "/usr/share/doc/dirmngr/examples/extra-certs/S-TRUSTQualSigOCSP2008-022.final.v3.509.crt"
+                   :element-type '(unsigned-byte 8))
+  (decode (rw:byte-reader s)))