No inverse error during encryption (node-rsa) - node.js

I am trying to convert https://clover.app.box.com/s/rz18bni3bpmdrc8wc92xm4w8h9grrtty from Java to node.js
I am using the node-rsa package and getting error:0306E06C:bignum routines:BN_mod_inverse:no inverse when trying to encrypt using an imported public key.
According to the clover example, I want to
Parse the Base64 public key string (returned by the CDN). Obtain the modulus and exponent.
Generate an RSA public key using the modulus and exponent values.
Prepend the prefix value to the card number.
Using the public key, encrypt the combined prefix and card number.
Base64 encode the resulting encrypted data into a string.
I was able to get the modulus and exponent, and it matches the result in Java:
modulus: 24130287975021244042702223805688721202041521798556826651085672609155097623636349771918006235309701436638877260677191655500886975872679820355397440672922966114867081224266610192869324297514124544273216940817802300465149818663693299511097403105193694390420041695022375597863889602539348837984499566822859405785094021038882988619692110445776031330831112388738257159574572412058904373392173474311363017975036752132291687352767767085957596076340458420658040735725435536120045045686926201660857778184633632435163609220055250478625974096455522280609375267155256216043291335838965519403970406995613301546002859220118001163241
exponent: 415029
Now I want to create a public key with it, and encrypt a message:
const key = new NodeRSA();
// generate RSA public key using mod and exp values
key.importKey({
n: Buffer.from('24130287975021244042702223805688721202041521798556826651085672609155097623636349771918006235309701436638877260677191655500886975872679820355397440672922966114867081224266610192869324297514124544273216940817802300465149818663693299511097403105193694390420041695022375597863889602539348837984499566822859405785094021038882988619692110445776031330831112388738257159574572412058904373392173474311363017975036752132291687352767767085957596076340458420658040735725435536120045045686926201660857778184633632435163609220055250478625974096455522280609375267155256216043291335838965519403970406995613301546002859220118001163241', 'hex'),
e: Buffer.from('415029', 'hex')
}, 'components-public');
// using public key, encrypt message
// base64 encode encrypted data to string
const encryptedString = key.encrypt(Buffer.from('message', 'hex'), 'base64', 'hex');
However, I am getting the no inverse error mentioned above. It seems to be caused by the modulus. If I change it to something shorter, the error goes away.

Related

Extract public key from PEM encoded x509 public key certificate

I have been attempting to retrieve the raw public key and place it in an object of type bytearray without success.
from cryptography import x509
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization import PublicFormat
cert = x509.load_pem_x509_certifcate(pem_encoded_cert)
pk = cert.public_key()
I have been attempting to retrieve the raw public key and place it in an object of type bytearray without success; e.g.
in_buf = bytearray(pk.public_bytes(Encoding.OpenSSH, PublicFormat.OpenSSH))
I cannot find a combination of Encoding and PublicFormat that provides the raw public key bytes; is the above the correct approach or is there a better solution?
Thanks in advance.

Nodejs crypto: Elliptic Curve to sign message and export public key as text

I want to achieve the following with the Nodejs crypto module:
I want to sign a message with my private key on a defined EC and have the signature as raw buffer/hex.
I want to have the respective public key as raw buffer/hex.
I can achieve both goals individually, but I can not achieve them together currently and it seems strange, that this is so hard to achieve with the node crypto module.
With the following code it is easy to generate a signature on a curve, but I cannot manage to decode the publicKey:
//Get pub and priv key from curve
const {publicKey, privateKey} = crypto.generateKeyPairSync('ec', {'namedCurve' : 'secp128r1'});
var message = "Hello";
var signer = crypto.createSign('sha256');
signer.update(message);
// Signature as raw hex. That's what I want.
var sigString = signer.sign(privateKey, 'hex'); // Needs a proper KeyObject
And with this code it is easy to extract the public key for a private key from a curve but the key returned by curve.getPrivateKey is not allowed for signing:
refCurve = crypto.createECDH('secp128r1');
//Public key split into x and y components. That's what I want.
refPubKey = {
x: '0x' + curve.getPublicKey('hex').slice(2,34),
y: '0x' + curve.getPublicKey('hex').slice(-32)
}
It is not possible for me to achieve both at the same time. The problem is, that the curve object can ONLY export as buffer/hex, while all the signature functions in crypto ONLY accept proper KeyObjects. And at the same time it seems not possible to convert both into each other. The KeyObjects have no functionality to export to buffer and it is not possible to create a KeyObject from the raw key, exported from the curve. What am I missing? I was also trying to set the respective private Key on the curve with curve.setPrivateKey(privateKey) but even this does not work.
Alternatively, I would also be open to use another Node library, but those I found do not seem to support the curve that I want to use (SECP128R1)
For example, this is how easy it is with python and the ecdsa library:
sk = SigningKey.generate(curve=SECP128r1, hashfunc=sha256)
vk = sk.verifying_key
message = b"Hello"
m = sha256(message)
//Signature
signature = sk.sign(message)
//Public Key
[vk.to_string()[:16], vk.to_string()[16:]]
Thankful for any help!
You can export the public key with export() in X.509/SPKI format and DER encoded. For a secp128r1 key the last 32 bytes are the concatenation of x and y:
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', { 'namedCurve': 'secp128r1' });
// ...
var key = publicKey.export({ type: 'spki', format: 'der' });
var rawX = key.subarray(-32, -16);
var rawY = key.subarray(-16);
console.log(key.toString('hex')); // e.g. 3036301006072a8648ce3d020106052b8104001c03220004d15885cfb3c75417bbeb95625da313dcb4d27ecb6f89923ae539faa7c09b4797
console.log(rawX.toString('hex')); // e.g. d15885cfb3c75417bbeb95625da313dc
console.log(rawY.toString('hex')); // e.g. b4d27ecb6f89923ae539faa7c09b4797
This can be checked in an ASN.1 parser, e.g. https://lapo.it/asn1js/.
Note that this curve should not be used because of its length according to NIST.
Another convenient option is the export as JWK, because here x and y can be extracted directly (Base64url encoded). However, only the curves supported by JWK can be exported this way, secp128r1 is not one of them.

how convert a public key from string type to PEM

I have a problem and I want some help.
I need to send a message from sender to receiver, and this message content signed data and public key and other things. note that I use the certificate x509 (the public key is from the certificate). On the receiver side, I must verify the signature of the data ( true or not). So, I use the public key of the sender for verification. But I had a problem, the public key is a string format. and i try to convert it . but unfortunately, i don't found any solution.
this the message that will be send:
data['message'] = data
data['_signature'] = self.sign_data(data)
data['_public_key'] = str ( self.certificate.public_key())
You cannot convert the public key (certificate.public_key()) into string by using str, because this method will return key object.
if you need to encode the public key as PEM string, then you have to do it correctly, by serialization into PEM:
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
Then from other side, you could load the PEM key and use it:
from cryptography.hazmat.primitives import serialization
public_key2 = serialization.load_pem_public_key(public_pem, default_backend())

How to create java.security.PublicKey from AsymmetricKeyParameter

Hi I'm trying to write a public key to a PEM file.
org.bouncycastle.openssl.jcajce.JcaPEMWriter seem to be the right tool, but it uses org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator which expects keys that is an instance of java.security.PublicKey.
Unfortunately, all the rest of the project I'm working on uses AsymmetricKeyParameter for public keys. I checked inside it has everything you would want to know from a public key, including point G and curve order, but it's not compatible with java.security.PublicKey.
Is there a way to use JcaPEMWriter if what I have is instances of AsymmetricKeyParameter? I use ECDSA key by the way.
(I am at first surprised at this because BouncyCastle's JcaPEMWriter isn't compatible with their own class, AsymmetricKeyParameter)
It doesn't look like the JcaPEMWriter can directly handle an AsymmetricKeyParameter object. But it is relatively easy to convert an AsymmetricKeyParameter object to a PublicKey object.
For this BouncyCastle's EC5Util class can be applied which handles the conversion from ECDomainParameters to ECParameterSpec and from BouncyCastle's ECPoint to Java's ECPoint.
The PublicKey object can then be exported as before with the JcaPEMWriter as an X.509/SPKI PEM encoded public key:
// AsymmetricKeyParameter object containing the public EC key
AsymmetricKeyParameter asymmetricKeyParameter = ...
// Convert AsymmetricKeyParameter object to PublicKey object
ECPublicKeyParameters ecPublicKeyParameters = (ECPublicKeyParameters)asymmetricKeyParameter;
ECParameterSpec ecParameterSpec = EC5Util.convertToSpec(ecPublicKeyParameters.getParameters());
ECPoint ecPoint = EC5Util.convertPoint(ecPublicKeyParameters.getQ());
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(ecPublicKeySpec);
// Export PublicKey object with JcaPEMWriter in X.509/SPKI format, PEM encoded
...

get »fingerprint« from rsa key

By using the crypto module, it is easy to create a private/public key Pair in node. But how can I compute the »fingerprint« of a key?
OpenCrypto has something like that:
crypt.getFingerprint(key, options).then(function (fingerprint) {
console.log(fingerprint)
})
What is the equivalent of nodes crypto module for that?
OpenCrypto.getFingerprint exports a public RSA key in the X509 DER format and creates a hash for these data. The same applies to a private RSA key with the difference that the private key is exported in the Pkcs8 DER format. The digest can be specified in the options (default: SHA 512) and also whether the data are returned as buffer or as hexadecimal string (default: hexadecimal string).
In the NodeJS code, keys can be generated with crypto.generateKeyPair, whereby the key format can be specified explicitly. If the keys aren't already in the appropriate formats (X509 DER and Pkcs8 DER) key conversions can be performed to produce the same fingerprints that OpenCrypto.getFingerprint creates. Suitable functions for these operations are crypto.createPublicKey or crypto.createPrivateKey. Finally the hash has to be generated with crypto.createHash.
Update:
The fingerprint is nothing else than a hash value, e.g. with the digest SHA-512 (as in OpenCrypto.getFingerprint):
var fingerprint = crypto.createHash('sha512').update(key).digest('hex'); // Fingerprint (hash) as hexadecimal string
where key is a public or private key in any format (string, Buffer, ...). If the key is given as X509 DER (public) or as PKCS8 DER (private), then the fingerprint matches that of OpenCrypto.getFingerprint.
If the key is in another format, the fingerprint can also be determined in this way. However, if the fingerprint should match the value provided by OpenCrypto.getFingerprint, the key must of course be converted into the formats used by OpenCrypto.getFingerprint before the hash is generated. This conversion isn't very complex, e.g. the conversion of a public PKCS1 PEM key (publicKey) into a X509 DER key (publicKeyDER) including the generation of the hash:
var publicKeyDER = crypto.createPublicKey(publicKey, { type: 'pkcs1', format: 'pem' }).export({ type: 'spki', format: 'der' }); // Convert a public PKCS1 PEM key into a X509 DER key
var fingerprint = crypto.createHash('sha512').update(publicKeyDER).digest('hex'); // Fingerprint (hash) as hexadecimal string
The bottom line is that these are functionally the same operations that are performed in OpenCrypto.getFingerprint. To my knowledge, there is no OpenCrypto.getFingerprint counterpart in the NodeJS crypto module. But with only little effort you can write your own function using the above crypto functions.

Resources