Create EC private key from input keying material in NodeJS - node.js

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.

Related

How do you decrypt a file that has been encrypted with openssl using nodejs javascript

I have a file that has been encrypted using openssl using the following command:
openssl enc -in data -out encrypted -e -aes256 -k myverystrongpassword
Where data is the original file and encrypted is the encrypted file.
I tried various ways using crypto library but nothing seems to work. I understand that the password needs to be converted into a key so maybe I am doing something wrong there. Looked all over for a solution but nothing seems to work.
The posted OpenSSL statement uses a key derivation function EVP_BytesToKey() to derive a 32 bytes key and a 16 bytes IV from the password in combination with a random 8 bytes salt.
The ciphertext corresponds to the concatenation of the ASCII encoding of Salted__, followed by the salt and finally by the actual ciphertext.
As you already know according to your comment, EVP_BytesToKey() uses a digest for which OpenSSL applied MD5 by default in earlier versions and SHA-256 as of version v1.1.0 (the default value can be overridden in the OpenSSL statement with the -md option).
Decryption is possible e.g. with CryptoJS: Due to its OpenSSL compatibility (s. sec. Interoperability) CryptoJS has a built-in implementation of an accessible EVP_BytesToKey() function and additionally allows to explicitly set the digest in the internal EVP_BytesToKey() call during key derivation. This makes it possible to decrypt encryptions that used SHA-256 or MD5 in key derivation.
The following data is the Base64 encoding of a ciphertext generated with the posted OpenSSL statement. The plaintext used was The quick brown fox jumps over the lazy dog. The OpenSSL version applied is v1.1.1i (i.e. SHA-256 is implicitly used in the key derivation):
U2FsdGVkX19W4wmC9dD35X4J66cSvaRaIQpvjDKHrLF9+qYg5VTo5urvExHLXhwf/bE8FXJTQZmKN8ITMJVdqQ==
This ciphertext can be successfully decrypted using the following CryptoJS implementation:
const password = 'myverystrongpassword';
const saltCiphertextB64 = 'U2FsdGVkX19W4wmC9dD35X4J66cSvaRaIQpvjDKHrLF9+qYg5VTo5urvExHLXhwf/bE8FXJTQZmKN8ITMJVdqQ==';
CryptoJS.algo.EvpKDF.cfg.hasher = CryptoJS.algo.SHA256.create(); // default: MD5
const decryptedData = CryptoJS.AES.decrypt(saltCiphertextB64, password);
console.log(decryptedData.toString(CryptoJS.enc.Utf8)); // The quick brown fox jumps over the lazy dog
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Note that the digest in the code must be explicitly specified as SHA-256 since OpenSSL v1.1.1i was used for encryption.
If the encryption was done with an OpenSSL version that uses MD5, the digest in the code must be modified accordingly.
Edit: As noted in the comment, the crypto functions createCipher()/createDecipher() also use EVP_BytesToKey() as key derivation.
However, the following should be noted:
Unlike CryptoJS, it is not possible to specify the digest, i.e. MD5 is used unchangeably. Thus, encryptions that applied SHA-256 for key derivation cannot be decrypted (what applies to the encryptions here).
In contrast to CryptoJS, no salt is used by default. Therefore, salt creation and concatenation (Salted__|<salt>|<cipherext>) during encryption and separation during decryption would have to be implemented additionally. createCipher()/createDecipher() then has to be passed the concatenation of passphrase and salt.
Both functions are deprecated since version 10.0.0 and should actually not be used.
A more robust approach to decrypt encryptions (with arbitrary digests in key derivation) using the crypto module is to apply createCipheriv()/createDecipheriv() and a port of the required functionality of EVP_BytesToKey() to derive key and IV (various implementations can be found on the net).
Security: EVP_BytesToKey() is deemed to be a vulnerability these days. This is worsened by a low iteration count (like 1, which is used by OpenSSL), a broken digest (like MD5) or a missing salt (as is the default for crypto). Ultimately, this is why createCipher()/createDecipher() are deprecated. Instead of EVP_BytesToKey(), a more reliable key derivation function such as PBKDF2 or the more modern scrypt or Argon2 should be used.

How do I load DSA Parameters from a PEM file in Bouncycastle?

I have a PEM file for DSA Parameters. In other words, the content has header/footer like this:
-----BEGIN DSA PARAMETERS-----
.......
-----END DSA PARAMETERS-----
I see that bouncycastle has classes for DSAParameters and DSAKeyGenerationParmaters. I suspect that this PEM is a representation of those parameters, but I can't figure out how to load it from a PEM format.
(Background: from legacy projects, I expected the signature of a DSA to be 40-bytes, but I am getting 46-bytes instead. I suspect that the DSA parameters are to blame, and I have this old dsa1024.pem as described, that I am trying to load and use to generate keys/generate signatures...)
At least through 1.57 (I don't have 1.58 installed yet) BCpkix org.bouncycastle.openssl.PEMParser doesn't implement DSA PARAMETERS (although it DOES do EC PARAMETERS) so this won't be easy.
To just look at your parameters it will be (much) easier to use
openssl dsaparam -in file -noout -text
But I guarantee you won't find anything wrong in your parameters. Classic DSA parameters have been 1024 bit group and 160 bit subgroup without any variation or change for 20 years and I've never seen any implementation get that wrong.
The usual reason a DSA-1024/160 signature occupies more than 40 octets is because by convention (though not required by FIPS 186) it is encoded in ASN.1 DER. If you had asked a question about that, I could point to you to existing Qs for ECDSA, which has exactly the same issue:
https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-ASN.1
https://crypto.stackexchange.com/questions/33095/shouldnt-a-signature-using-ecdsa-be-exactly-96-bytes-not-102-or-103
https://crypto.stackexchange.com/questions/37528/why-do-openssl-elliptic-curve-digital-signatures-differ-by-one-byte
https://crypto.stackexchange.com/questions/44988/length-of-ecdsa-signature
but since you asked a question that isn't about your actual problem it's against Stack policy to give you a solution to your actual problem.
FWIW if you generate a DSA key(pair) in OpenSSL using the parameters, all 4 PEM formats for privatekey (pkcs8 clear and encrypted, 'legacy' clear and encrypted) and the usual PEM format for publickey (SPKI) are readable by PEMParser and can produce key objects from which the parameters can be extracted. (Technically the specification for SPKI allows DSA parameters to be omitted in certain situations, but OpenSSL never does so.)

Space constrained asymmetric signature

I need to sign a small string with an asymmetric key encryption scheme.
The signature will be stored on a small chip together with the signed string. I have very little space to spare (about 60bytes for signature + string), so the generated signature should be as small as possible.
I looked around for how to do it, and what I found is that I could use RSA-SHA1, but the generated signature with a 512 bit key is 64 bytes. That is a bit much.
What secure algorithm could I use to generate a small asymmetric signature?
Would it still be secure if I store the SHA1 sum of the RSA-SHA1 signature, and later verify that instead?
What you're bumping up against here is one of the properties of a good hash function - the return value should be long to protect against birthday attacks (where two different inputs result in the same output hash). Generally 128-512 bits is preferred hence the SHA-1 signature gives you 512 bits.
As with all things in cryptography security is a trade off. As you are using asymmetric signing have you considered using RSA-MD5 as your signature option? This will give you a far shorter return of 128 bits but this comes with the caveat that MD5 is considered broken and is generally being moved away from.

Secret vs. Non-secret Initialization Vector

Today I was doing some leisurely reading and stumbled upon Section 5.8 (on page 45) of Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography (Revised) (NIST Special Publication 800-56A). I was very confused by this:
An Approved key derivation function
(KDF) shall be used to derive secret
keying material from a shared secret.
The output from a KDF shall only be
used for secret keying material, such
as a symmetric key used for data
encryption or message integrity, a
secret initialization vector, or a
master key that will be used to
generate other keys (possibly using a
different process). Nonsecret keying
material (such as a non-secret
initialization vector) shall not be
generated using the shared secret.
Now I'm no Alan Turing, but I thought that initialization vectors need not be kept secret. Under what circumstances would one want a "secret initialization vector?"
Thomas Pornin says that IVs are public and he seems well-versed in cryptography. Likewise with caf.
An initialization vector needs not be secret (it is not a key) but it needs not be public either (sender and receiver must know it, but it is not necessary that the Queen of England also knows it).
A typical key establishment protocol will result in both involve parties computing a piece of data which they, but only they, both know. With Diffie-Hellman (or any Elliptic Curve variant thereof), the said shared piece of data has a fixed length and they have no control over its value (they just both get the same seemingly random sequence of bits). In order to use that shared secret for symmetric encryption, they must derive that shared data into a sequence of bits of the appropriate length for whatever symmetric encryption algorithm they are about to use.
In a protocol in which you use a key establishment algorithm to obtain a shared secret between the sender and the receiver, and will use that secret to symmetrically encrypt a message (possibly a very long streamed message), it is possible to use the KDF to produce the key and the IV in one go. This is how it goes in, for instance, SSL: from the shared secret (called "pre-master secret" in the SSL spec) is computed a big block of derived secret data, which is then split into symmetric keys and initialization vectors for both directions of encryption. You could do otherwise, and, for instance, generate random IV and send them along with the encrypted data, instead of using an IV obtained through the KDF (that's how it goes in recent versions of TLS, the successor to SSL). Both strategies are equally valid (TLS uses external random IV because they want a fresh random IV for each "record" -- a packet of data within a TLS connection -- which is why using the KDF was not deemed appropriate anymore).
Well, consider that if two parties have the same cryptographic function, but don't have the same IV, they won't get the same results. So then, it seems like the proposal there is that the two parties get the same shared secret, and each generate, deterministically, an IV (that will be the same) and then they can communicate. That's just how I read it; but I've not actually read the document, and I'm not completely sure that my description is accurate; but it's how I'd start investigating.
IV is public or private, it doesn't matter
let's consider IV is known to attacker, now by looking at encrypted packet/data,
and knowledge of IV and no knowledge on encryption key, can he/she can guess about input data ? (think for a while)
let's go slightly backwards, let's say there is no IV in used in encryption
AES (input, K)= E1
Same input will always produce the same encrypted text.
Attacker can guess Key "K" by looking at encrypted text and some prior knowledge of input data(i.e. initial exchange of some protocols)
So, here is what IV helps. its added with input value , your encrypted text changes even for same input data.
i.e. AES (input, IV, K)= E1
Hence, attacker sees encrypted packets are different (even with same input data) and can't guess easily. (even having IV knowledge)
The starting value of the counter in CTR mode encryption can be thought of as an IV. If you make it secret, you end up with some amount of added security over the security granted by the key length of the cipher you're using. How much extra is hard to say, but not knowing it does increase the work required to figure out how to decrypt a given message.

How should I create my DES key? Why is an 7-character string not enough?

I'm having a bit of difficulty getting an understand of key length requirements in cryptography. I'm currently using DES which I believe is 56 bits... now, by converting an 8 character password to a byte[] my cryptography works. If I use a 7 digit password, it doesn't.
Now, forgive me if I'm wrong, but is that because ASCII characters are 7 bits, therefor 8 * 7 = 56bits?
That just doesn't seem right to me. If I want to use a key, why can I not just pass in a salted hash of my secret key, i.e. an MD5 hash?
I'm sure this is very simple, but I can't get a clear understanding of what's going on.
DES uses a 56-bit key: 8 bytes where one bit in each byte is a parity bit.
In general, however, it is recommended to use an accepted, well-known key derivation algorithm to convert a text password to a symmetric cipher key, regardless of the algorithm.
The PBKDF2 algorithm described in PKCS #5 (RFC 2898) is a widely-used key derivation function that can generate a key of any length. At its heart, PBKDF2 is combining salt and the password through via a hash function to produce the actual key. The hash is repeated many times so that it will be expensive and slow for an attacker to try each entry in her "dictionary" of most common passwords.
The older version, PBKDF1, can generate keys for DES encryption, but DES and PBKDF1 aren't recommended for new applications.
Most platforms with cryptographic support include PKCS #5 key-derivation algorithms in their API.
Each algorithm is designed to accept a certain key length. The key is used as part of the algorithm, and as such, can't be whatever your heart desires.
Common key sizes are:
DES: 56bit key
AES: 128-256bit key (commonly used values are 128, 192 and 256)
RSA (assymetric cryptography): 1024, 2048, 4096 bit key
A number, such as 1234567 is only a 4-byte variable. The key is expected to be a byte array, such as "1234567" (implicitly convertible to one in C) or `{ '1', '2', '3', '4', '5', '6', '7' }.
If you wish to pass the MD5 hash of your salted key to DES, you should use some key compression technique. For instance, you could take the top 7 bytes (somewhat undesirable), or perform DES encryption on the MD5 hash (with a known constant key), and take all but the last byte as the key for DES operation.
edit: The DES I'm talking about here is the implementation per the standard released by NIST. It could so be (as noted above), that your specific API expects different requirements on the length of the key, and derives the final 7-byte key from it.
The key must have size 64-bits but only 56-bits are used from the key. The other 8-bits are parity bits (internal use).
ASCII chars have 8-bit size.
You shouldn't pass you passwords straight into the algorithm. Use for instance the Rfc2898DeriveBytes class that will salt your passwords, too. It will work with any length.
Have a look here for an example.
EDIT: D'Oh - your question is not C# or .Net tagged :/
According to MSDN DES supports a key length of 64 bits.
To avoid this issue and increase the overall security of one's implementation, typically we'll pass some hashed variant of the key to crypto functions, rather than the key itself.
Also, it's good practice to 'salt' the hash with a value which is particular to the operation you are doing and won't change (e.g., internal userid). This assures you that for any two instances of the key, the resulting has will be different.
Once you have your derived key, you can pull off the first n-bites of it as required by your particular crypto function.
DES requires a 64 bit key. 56 bits for data and 8 bits for parity.
Each byte contains a parity bit at the last index. This bit is used to check for errors that may have occurred.
If the sum of the 7 data bits add up to an even number the correct parity bit is 0, for an odd number it's 1.
ASCII chars contain 8 bits, 8 chars can be used as a key if error correction is not necessary. If EC is necessary, use 7 chars and insert parity bits at indices (0 based) 7,15,23,31,39,47,55,63.
sources:
Wikipedia: https://en.m.wikipedia.org/wiki/Data_Encryption_Standard
“The key ostensibly consists of 64 bits; however, only 56 of these are actually used by the algorithm. Eight bits are used solely for checking parity, and are thereafter discarded. Hence the effective key length is 56 bits.”
“The key is nominally stored or transmitted as 8 bytes, each with odd parity. According to ANSI X3.92-1981 (Now, known as ANSI INCITS 92-1981), section 3.5:
One bit in each 8-bit byte of the KEY may be utilized for error detection in key generation, distribution, and storage. Bits 8, 16,..., 64 are for use in ensuring that each byte is of odd parity.”

Resources