Verify RSA signature with public key in PEM format in Haskell - haskell

If I have a payload, signature, and a public key (all in bytestring or similar format), how can I verify the signature?
All of the PublicKey types I see on Hackage seem to represent keys purely via numbers, for instance:
PublicKey
public_size :: Int -- size of key in bytes
public_n :: Integer -- public p*q
public_e :: Integer -- public exponant e
How can I get a PublicKey from a PEM file, or simply perform verification directly from the PEM file?
[EDIT from the feedback that no solution attempt was made] - I looked around for solutions, but haven't been able to find anything at all on hoogle that satisfies any type signature I'd expect, like ByteString -> PublicKey. I don't want to reimplement this from scratch, as what I'm doing now is just calling out to a python script that performs all of the verification. It would be nice if I didn't need to call out to python though, but can't seem to find any existing code.

If a library exposes an interface like public_n :: Integer, it means that it's a library that illustrates the RSA operation, not a library for cryptography. A cryptography library would have interfaces like sign :: Key -> ByteString -> ByteString. Any cryptography library should be able to parse keys in PEM format.
OpenSSL is a popular library for cryptography. It isn't always ideal or easy to use, but it's widespread, and you won't be using its quirky C interface since you're using Haskell. So you can use HsOpenSSL, which is a Haskell binding over OpenSSL. (Note: I have never used HsOpenSSL, but it looks sensible.) Use OpenSSL.PEM.readPrivateKey to read a key in PEM format, OpenSSL.EVP.Digest.digestLBS to calculate a digest of the message you want to sign, and OpenSSL.EVP.Sign.signBS to sign the digest.

Related

Standard way of computing a single ECDSA signature hex string from its tuple form

I implemented ECDSA manually in rust, where the output is signature in the form of a tuple (r,s). I wanted to test my implementation, and therefore I was looking for functions in python or rust for verifying the signature (r,s) given the public key, message and the curve details. The Wiki article also shows the output signature in tuple form.
But, everywhere I looked, the signature is inputted as a single hex string or a byte array. Can someone please share any way of converting (r,s) pair to the hex string. I tried concatenation but got wrong result in my algo.
If it is helpful, I tried using the crate secp256k1 in rust for verification.

Implementing "openssl_private_encrypt" in latest Python 3 versions

I'm trying to maintain FastSpring e-commerce platform Secure Payload api implementation in Python.
Their documentation has examples for encrypting (or technically signing?) the payload with private key in Java and PHP: https://developer.fastspring.com/docs/pass-a-secure-request#locally-encrypt-the-session-object
And I have been previously using a Python "cryptography" library based implementation based on this repository:
https://github.com/klokantech/flask-fastspring/blob/0462833f67727cba9755a26c88941f11f0159a48/flask_fastspring.py#L247
However, that relies on undocumented openssl "_lib.RSA_private_encrypt()" function that is no longer exposed in cryptography versions higher than 2.9.2, which is already several years old. And with latest python versions it no longer includes binary packages and PIP must compile it from source.
PyCryptodome seems to include similar RSA private key signing with PKCS #1 v1.5 padding, but it requires payload to be a Hash object, so naturally the produced output doesn't match what FastSpring expects regardless of what Hash function I use: https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html?highlight=pkcs1_15#pkcs-1-v1-5-rsa
I have been trying to firure out any alternative ways to implement this kind of "private key encryption" without success. So my question is: Is there ANY way to do this with up-to-date python libararies or am I stuck to use an outdated cryptography library until it no longer is supported at all?
The two linked codes implement low level signing using RSASSA-PKCS1-v1_5, but a modified encoding is used for the message rather than EMSA-PKCS1-v1_5, and therefore the processing differs from the standard.
The two major Python crypto libraries PyCryptodome and Cryptography only support high level signing, which encapsulates the entire process, follows the standard and thus does not allow any modification of the encoding of the message.
The most efficient way to solve the problem would be to use a Python library that also supports a low level signing, so that the encoding of the message from the linked Java or Python code can be used. However, I am not aware of such a library.
If you don't know such a library either, there is the following alternative: Since RSASSA-PKCS1-v1_5 is pretty simple and Python supports large integers and their operations natively, a custom implementation in combination with the helper functions of e.g. PyCryptodome is easily possible. At least you wouldn't have to rely on the legacy library anymore:
from Crypto.Util import number
def customizedSign(key, msg):
modBits = number.size(key.n)
k = number.ceil_div(modBits, 8)
ps = b'\xFF' * (k - len(msg) - 3)
em = b'\x00\x01' + ps + b'\x00' + msg
em_int = number.bytes_to_long(em)
m_int = key._decrypt(em_int)
signature = number.long_to_bytes(m_int, k)
return signature
Explanation:
The implementation follows the PyCryptodome implementation of the sign() method. The only functional difference is that instead of EMSA-PKCS1-v1_5 the encoding of the linked codes is used.
EMSA-PKCS1-v1_5 is defined as:
EM = 0x00 || 0x01 || PS || 0x00 || T
where T is the concatenation of the DER encoded DigestInfo value and the hashed message, see here.
The encoding of the linked codes simply uses the message MSG instead of T:
EM = 0x00 || 0x01 || PS || 0x00 || MSG
In both cases, PS is a padding with 0xFF values up to the key size (i.e. size of the modulus).
Usage and test:
Since the signature is deterministic, the same key and the same message always provide the same signature. This way it is easy to show that the above function is equivalent to the linked Java or Python code:
from Crypto.PublicKey import RSA
import base64
# For simplicity, a 512 bits key is used. Note that a 512 bits key may only be used for testing, in practice the key size has to be >= 2048 bits for security reasons.
pkcs8 = """-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2gdsVIRmg5IH0rG3
u3w+gHCZq5o4OMQIeomC1NTeHgxbkrfznv7TgWVzrHpr3HHK8IpLlG04/aBo6U5W
2umHQQIDAQABAkEAu7wulGvZFat1Xv+19BMcgl3yhCdsB70Mi+7CH98XTwjACk4T
+IYv4N53j16gce7U5fJxmGkdq83+xAyeyw8U0QIhAPIMhbtXlRS7XpkB66l5DvN1
XrKRWeB3RtvcUSf30RyFAiEA5ph7eWXbXWpIhdWMoe50yffF7pW+C5z07tzAIH6D
Ko0CIQCyveSTr917bdIxk2V/xNHxnx7LJuMEC5DcExorNanKMQIgUxHRQU1hNgjI
sXXZoKgfaHaa1jUZbmOPlNDvYYVRyS0CIB9ZZee2zubyRla4qN8PQxCJb7DiICmH
7nWP7CIvcQwB
-----END PRIVATE KEY-----"""
key = RSA.import_key(pkcs8)
msg = 'The quick brown fox jumps over the lazy dog'.encode('utf8')
signature = customizedSign(key, msg)
print(base64.b64encode(signature).decode('utf8')) # OwpVG/nPmkIbVxONRwXHvOqLdYNnP67YtiWA+GcKBZ3rIzAJ+8izvmlqUQnzVp03Wrrzq2ogUmCMaLSPlInDNw==
The linked Java code provides the same signature for the same key and message.

Create EC private key from input keying material in NodeJS

I'd like to derive an elliptic curve private key from input keying material (a master key). Is this possible?
Attempts
Node's crypto function, crypto.generateKeyPair does not accept input keying material, and crypto.createPrivateKey only converts a .pem to Node's native KeyObject.
I also can't find a way to do this in OpenSSL using ecparam. The -rand flag seems promising but isn't widely available (it's not on my machine).
Why / Details
I need to create a number of secrets and want have all of them derived from a single master key. This is analogous to HKDF.
I'm using the keys for ECDSA with curve P-384 (secp384r1).
I'm surprised you think ecparam -rand file -genkey ... is rare; it's in every upstream version back at least to 0.9.8 in 2005, and it's not one of the things that can be omitted by a build (configure) option, so your machine must have one weird version. But it doesn't matter because -rand doesn't do what you want; it adds the file data to the RNG 'pool' but does not replace it, so it doesn't give you deterministic key generation.
As Woodstock commented, for all practical purposes a raw P-384 private key is just 384 bits from any good random generator, or deterministically from any uniform random function. Technically you should exclude zero and values greater than or equal to the (sub)group order n, but those exclusions are so small relative to 2^384 that there is essentially no chance a good random choice will hit them during the lifetime of the Earth, and perhaps of the universe. You might want to look at how Bitcoin 'hierarchical deterministic' key derivation aka BIP 32 works, although of course that does 256-bit keys for secp256k1.
That leaves you the problem of converting the raw key to a form usable by nodejs crypto (which is a fairly thin wrapping of openssl library) and/or openssl commandline. To do this follow the principles of How to convert an ECDSA key to PEM format which is in turn based on https://bitcoin.stackexchange.com/questions/66594/signing-transaction-with-ssl-private-key-to-pem except use the OID and size(s) for P-384 instead of secp256k1. Specifically, concatenate
the 7 bytes represented in hex by 303e0201010430
the 48 bytes (384 bits) of the raw private key
the 9 bytes represented in hex by a00706052b81040022 (for P-384 aka secp384r1)
Depending on your language(s) or tool(s) you might handle these values directly, or concatenate the hex representations and then convert to binary. The result is the 'DER' (binary) form of the algorithm-specific (SEC1) private key (only), which can be read by nodejs 11 or 12 crypto.createPrivateKey( {key:(data), format:'der', type:'sec1'} ) and also by commandline openssl ec -inform der.
If you prefer textlike things (e.g. for cut&paste), convert the DER above to base64, break into lines of 64 chars (other than the last), and add lines -----BEGIN EC PRIVATE KEY----- before and -----END EC PRIVATE KEY------ after. This is PEM format and can be read by createPrivateKey without any other options, and by openssl ec without any option.

Understanding BCryptSignHash output signature

I have signed a hash value in windows using BCryptSignHash with ECDSA algorithm. The output signature buffer is of length 64 bytes. I also generated the public and private key blobs using BCryptGenerateKeyPair function (BCRYPT_ECDSA_P256_ALGORITHM algorithm) with which i signed the hash.
I have to verify this signature with this key pair in linux. I am able to decipher the public-private key pair that got generated, using the link "http://msdn.microsoft.com/en-us/library/windows/desktop/aa375520%28v=vs.85%29.aspx" and able to use the same in linux.
The 64-byte signature generated should ideally be signature pair (r,s) (http://en.wikipedia.org/wiki/Elliptic_Curve_DSA).
Is there a way to understand the 64-bytes signature generated so that i can map the signature blob contents to (r,s) pair in linux and verify it?
Or is there a simpler way to verify the generated signature in linux?
Thanks,
F
Is there a way to understand the 64-bytes signature generated so that I can map the signature blob contents to (r,s) pair in linux and verify it?
The r and s are in P1363 format, which is simply a concatenation of r and s in a 2's compliment format. That is, the signature is simply r || s.
You need to know the hash to use this format. For example, SHA1 will create a r of 20 bytes and an s of 20 bytes. If r or s is "too short", then it is padded on the left with 0's.
Java and OpenPGP are different than P1363. Java and OpenPGP use an ASN.1 encoding:
SEQUENCE ::= {
r INTEGER,
s INTEGER
}
Depending what library you use on Linux, you may have to convert between the formats. Cryptographic Interoperability: Digital Signatures gives examples of signing and verifying using a few different libraries.
Or is there a simpler way to verify the generated signature in linux?
Try Crypto++. I believe Microsoft and Crypto++ uses the same signature format, so you won't need to convert. See Elliptic Curve Digital Signature Algorithm for details.

Managing cryptographic random number generators in a Haskell web application

I'm writing an application which I want to be able to supply RSA encrypted tokens to clients via a web API.
I'm using the crypto-pubkey library for RSA, for example:
encrypt :: CPRG g
=> g -- ^ random number generator.
-> OAEPParams -- ^ OAEP params to use for encryption.
-> PublicKey -- ^ Public key.
-> ByteString -- ^ Message to encrypt
-> (Either Error ByteString, g)
In my case, the message is the AES content key used to encrypt the token. I can create a CPRG instance using the cprng-aes library which provides an AES counter mode implementation:
makeSystem :: IO AESRNG
which is the same implementation that Yesod uses in its ClientSession module. I've taken a look at that and it stores a global instance behind an IORef and uses it to implement a function for generating initialization vectors inside an atomicModifyIORef call.
This is OK since the function just pulls some bytes out of the generator and returns them, writing the new CPRG instance back to the IORef. However the RSA API needs to be passed a CPRG instance directly, and even if I could carry out my token generation within a call to atomicModifyIORef, it's likely to be a much more costly operation and lead to contention issues.
One idea I had was to pull out adequate data from a global instance in advance before calling the encryption API, and wrap it up in a CPRG instance backed by a ByteString, but that's a bit of a fragile hack, as it requires prior knowledge of the internals of the token generation process -- the content key size, RSA padding and so on, which may vary depending on the parameters chosen.
What are the best options for managing the random number generators required by pure functions like the above RSA API when they are used in multi-threaded client-server applications?
I would recommend using a pool of CPRG instances, if the numbers say you need this. It's probably worth doing some basic profiling first to see if the simple atomicModifyIORef approach would be a bottleneck.
For pools, you can use http://hackage.haskell.org/package/resource-pool, or http://hackage.haskell.org/package/pool-conduit (which is based on resource-pool).

Resources