sharplispers / ironclad

A cryptographic toolkit written in Common Lisp
BSD 3-Clause "New" or "Revised" License
166 stars 28 forks source link

Unable to verify signatures using `openssl dgst -verify` #11

Closed lambdadog closed 4 years ago

lambdadog commented 5 years ago

This may just be my own error, so please let me know if so, but I'm completely unable to get any signatures to verify using openssl dgst -verify

I'm using asn1 to load the private key, using

(defun load-privkey (filename)
  (with-open-file (stream filename)
    (apply 'concatenate 'string
       (loop for line = (read-line stream nil)
         while line
         when (not (search "-----" line))
           collect line))))

;; using optima because the asn1 library i'm using is built with it in mind
;; https://github.com/fukamachi/asn1
(defvar *privkey* (optima:match (asn1:decode (base64-string-to-usb8-array (load-privkey (merge-pathnames #P"private.pem" #P"~/sigtest/"))))
            ((asn1:rsa-private-key :private-exponent d :modulus n)
                     (ironclad:make-private-key :rsa :d d :n n))))

and signing a file with

(base64:usb8-array-to-base64-string 
  (ironclad:sign-message *privkey* (ironclad:digest-file :sha256 #P"~/sigtest/in.txt")))

then copying the output of that and using

$ xclip -o | base64 -d > in.txt.rsa
$ openssl dgst -sha256 -verify public.pem -signature in.txt.rsa in.txt

but it always returns "Verification Failure"

I've also tried

(base64:usb8-array-to-base64-string 
  (ironclad:sign-message *privkey* (ironclad:digest-file :sha256 #P"~/sigtest/in.txt") :pss t))
(base64:usb8-array-to-base64-string 
  (ironclad:sign-message *privkey* (ironclad:digest-file :sha256 #P"~/sigtest/in.txt") :pss :sha256))
(base64:usb8-array-to-base64-string 
  (ironclad:sign-message *privkey* (ironclad:digest-file :sha1 #P"~/sigtest/in.txt")))

and

(base64:usb8-array-to-base64-string 
  (ironclad:sign-message *privkey* (ironclad:digest-file :sha1 #P"~/sigtest/in.txt") :pss t))

and have had no success with any of them.

This may just be personal error, so apologies if so, but as far as I can tell I'm doing this correctly.

Using CCL 1.11.5


Keys were generated with

$ openssl genrsa -out private.pem 2048
$ openssl rsa -in private.pem -outform PEM -pubout -out public.pem
glv2 commented 5 years ago

I suspect that the openssl command is using the older PKCS1-v1.5 padding which is not implemented in Ironclad. Currently only no padding or PSS (PKCS1-v2.1) padding are supported.

If you do the PKCS1-v1.5 padding of the hash by hand and pass that to sign-message, it should work.

lambdadog commented 5 years ago

@glv2 I see, thank you, I'll test that and get back to the issue once that can be confirmed

glv2 commented 5 years ago

Apparently the openssl command also doesn't sign the message directly. It first make an ASN1 structure containing some metadata and the message, then the PKCS1-v1.5 padding is added and the padded ASN1 data is signed.

Something like:

(defun add-openssl-padding (message modulus)
  (let* ((nbytes (ceiling (integer-length modulus) 8))
         (asn1-message (asn1:encode
                        (list (list :sequence
                                    (list :sequence
                                          (cons :object-identifier #(2 16 840 1 101 3 4 2 1))
                                          (list :null))
                                    (cons :octet-string message)))))
         (padding-length (- nbytes (length asn1-message)))
         (padding (make-array padding-length
                              :element-type '(unsigned-byte 8)
                              :initial-element #xff)))
    (setf (aref padding 0) 0)
    (setf (aref padding 1) 1)
    (setf (aref padding (1- padding-length)) 0)
    (concatenate '(simple-array (unsigned-byte 8) (*))
                 padding
                 asn1-message)))

I think the added metadata (here #(2 16 840 1 101 3 4 2 1)) indicates what the message was hashed with (here SHA256), but I don't know what its format is.

rayslava commented 1 year ago

I met the same problem and found a working solution by @dnaeon: http://dnaeon.github.io/rsassa-pkcs1-1_5-signature-common-lisp/

dnaeon commented 1 year ago

Hey @rayslava ,

You can grab the latest code here as well: https://github.com/dnaeon/cl-ssh-keys/blob/master/src/rfc8017.lisp