Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RSA Sign using a PrivateKey from a file

Using Haskell, how can you sign using an existing private key from a file?

In Python, it is as simple as -

import M2Crypto
rsa = M2Crypto.RSA.load_key("path/to/key")
result = rsa.sign("foo")

It seems you can sign using the Codec.Crypto.RSA module -

http://hackage.haskell.org/package/RSA-1.0.6.2/docs/Codec-Crypto-RSA.html#g:2

But I only see how to generate new private keys from that module, not use an existing one. It seems that the Network.TLS.Extra module provides reading a private key from a file -

http://hackage.haskell.org/package/tls-extra-0.6.1/docs/Network-TLS-Extra.html#g:6

Unfortunately, the PrivateKey type exported from each of the modules are not compatible with each other -

Couldn't match expected type `crypto-pubkey-types-0.4.0:Crypto.Types.PubKey.RSA.PrivateKey'
            with actual type `tls-1.1.5:Network.TLS.Crypto.PrivateKey'
like image 346
pyrospade Avatar asked Dec 01 '13 23:12

pyrospade


People also ask

Can we use RSA for digital signature?

RSA : It is the most popular asymmetric cryptographic algorithm. It is primarily used for encrypting message s but can also be used for performing digital signature over a message.

How does signature verification work RSA?

The signature gets attached to the message and both are transferred to the recipient. The recipient recalculates the hash of the message and then uses my public key to verify the signature he received.

Which command do you use to sign a file with a private key?

When you have the private and public key you can use OpenSSL to sign the file. The default output format of the OpenSSL signature is binary. If you need to share the signature over internet you cannot use a binary format. You can use for instance Base64 format for file exchange.


1 Answers

I needed something similar, so I added a bit of serialization/deserialization to the package crypto-pubkey-openssh a while back. It doesn't handle encrypted or ECC keys, but suffices for most needs.

As my comment said, you want something like:

import Codec.Crypto.RSA (sign)
import Crypto.PubKey.OpenSsh (decodePrivate, OpenSshPrivateKey)
import Crypto.Types.PubKey.RSA (PrivateKey)
import Data.ByteString (ByteString)

throwLeft :: Either String OpenSshPrivateKey -> PrivateKey
throwLeft (Right (OpenSshPrivateKeyRsa k)) = k
throwLeft (Right _) = error "Wrong key type"
throwLeft (Left s)  = error $ "Error reading keys: " ++ s

readAndSign :: FilePath -> ByteString -> IO ByteString
readAndSign file msg = (flip sign msg . throwLeft . decodePrivate) `fmap` readFile file

Notice this code is untested, but the building blocks should be correct. You want to read in a private key (readFile, decodePrivate). Perform some error checking (throwLeft) and sign the message (sign).

EDIT: I felt it was productive to identify in what way this is longer than the Python example in the question. It appears as though the building blocks are the same but the level of abstraction exposed by the library is very different along with the error handling (explicit vs exceptions). If we assume the library authors did a little more work by re-exporting everything from one module and defined a helper:

loadKey :: FilePath -> IO PrivateKey
loadKey p = (throwLeft . decodePrivate) `fmap` readFile p

Then the code is nearly identical:

k <-loadKey keyFile
let result = sign k msg
like image 195
Thomas M. DuBuisson Avatar answered Nov 15 '22 03:11

Thomas M. DuBuisson