I have to send some encrypted data throught the network (websocket)
I generated a key pair with the the following node.js module :
https://github.com/juliangruber/keypair
My public key looks like this:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAlUiMDQsBgj5P/T86w/eg9MXUj8M4WMVihP8YzmDxMqCFb7D+w4N/1XcxWxQT
....
Wo+SRCsr6npfp1ctDhMtkXIeNT4lKf3qUGhP5tbx/TreaNF/d8zCeinGR/KeBGadMwIDAQAB
-----END RSA PUBLIC KEY-----
In the C++ code, I generated a RSA class with read the public key via a char*
const char rsaKey1[] = "-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAlUiMDQsBgj5P/T86w/eg9MXUj8M4WMVihP8YzmDxMqCFb7D+w4N/1XcxWxQT\n"
....
"Wo+SRCsr6npfp1ctDhMtkXIeNT4lKf3qUGhP5tbx/TreaNF/d8zCeinGR/KeBGadMwIDAQAB\n"
"-----END RSA PUBLIC KEY-----\n";
BIO* bio = BIO_new_mem_buf( rsaKey1, strlen(rsaKey1));
m_rsaPubKey = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
usigned the m_rsaPubKey , I have able to generate a std::vector of unsigned char with encrypted data
std::vector<u8> Rsa::encrypt(std::string & msg)
{
std::vector<u8> encryptedData;
char *encrypt = new char[RSA_size(m_rsaPubKey)];
int encryptLen;
if (encryptLen = RSA_public_encrypt(msg.size() + 1, (unsigned
char*)msg.c_str(), (unsigned char*)encrypt, m_rsaPubKey,
RSA_PKCS1_OAEP_PADDING) == -1)
{
LogOutSys("error encoding string");
}
for (u32 i = 0; i < strlen(encrypt); i++)
{
encryptedData.push_back(encrypt[i]);
}
delete encrypt;
return encryptedData;
}
I don't get any errors while reading the public key or encrypting my data so I assume the encryption went ok.
then the data went throught a websocket and is received with node.js
the private key is read like this:
var rsa = new RSA(fs.readFileSync("./rsa-keys/sj_private_1.pem"),
{encryptionScheme :'pkcs8'})
and decoding
var decrypted = rsa.decrypt(data)
where data is a buffer of same length and content (no corruption while sending via the websocket)
c++ side:
encrypted len 256, first bytes 117 125 58 109
node size :
Buffer(256) [117, 125, 58, 109, 38, 229, 7, 189, …]
the rsa.decrypt generated an exception :
TypeError: Cannot read property 'length' of null
I tried several encryptionScheme option (including the default , but always getting the same error or Incorrect key or data
Because of the random padding in OAEP, troubleshooting encryption issues with it can sometimes be a bit tricky.
For further troubleshooting use the following checklist to shoot down potential issues:
Make sure you use the same crypto mechanism on both ends. In your C++ code you are using RSA_PKCS1_OAEP_PADDING but the JavaScript lines in your question does not tell what mechanism you use there.
Make sure that the mechanisms are implemented the same ways in both C++ and Node libraries. It is crucial you have the same hashing method and MGF1 (mask generation function) in both implementations. This is one of the most typical failing points that I've seen in my career.
Since you are working with byte arrays, make sure you are not having any issues in the byte order. In other words, make sure both ends talks the same language in regard of endianness (For self-study: https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Data/endian.html).
Related
IdPs like Okta are providing public key and exponent in this format.
"n":"sNwO2gzGvmmQH8BKFa--JbsaQrEY7hg9YrJ3lqs_t36cd6EJInE3W0EbmdAHWbZC4-AeMS73BZQsaJqa2UvqWfUTwpVrEVlPOHc0_Tc4VTqLsmuoPaByYOgz5hn3Z_0gYfPq8eGIYuh6QLvKkuYdAWr5yMK0xDof2eFmQ-BoSMjiB4id_c2BjX_TlqxHCDoXtwCD-51R2ZFTNP9PW2ivunDmAD4RCuLjHxnjiB-GmJFGX0KwTp71Ppyd8MYcUFi_ExxOFDWtOqyPzBhWVX0NmxvvujTAUdTa90u29UE0g59W1tbhKQH8LzakWlpaopkqhfDZxeKCo9dDrYhw5NQ1ow"
"e":"AQAB"
Can someone tell me how do I decode the exponent 'AQAB' programmatically to a valid value which can be given as input to RSA structure in C/C++ language? I know AQAB decodes to 010001 in hex but I am looking for a sample in c/c++ sample to do that.
After that I can do:
RSA* rsa = RSA_new();
rsa->e = e;
rsa->n = n;
char* lPublic = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPublicKey(lPublic, rsa);
I'm trying to Store ECIES num0 PrivateKey with DEREncodePrivateKey to a std::string and reload it in num1 PrivateKey Object for testing.
Problem is when key is loaded with BERDecodePrivateKey in second PrivateKey object it can't be validated (also tested encryption and decrypting without validation and didn't decrypt )
here's the code
using namespace CryptoPP;
CryptoPP::AutoSeededRandomPool prng;
ECIES<ECP>::PrivateKey pp;
pp.Initialize(prng, ASN1::secp256k1());
/* returns true*/
bool val=pp.Validate(prng, 3);
std::string saves;
StringSink savesink(saves);
pp.DEREncodePrivateKey(savesink);
/*additional unnecessary steps to make sure the key is written completely */
savesink.MessageEnd();
savesink.Flush(true);
ECIES<ECP>::PrivateKey pro;
StringSource savesSource(saves, true);
pro.BERDecodePrivateKey(savesSource,true,savesSource.MaxRetrievable());
/*here the exception is thrown */
pro.ThrowIfInvalid(prng, 3);
finally found what the problem is
as #maarten-bodewes mentioned in comment the DER encoded private exponent doesn't determine the curve OID for the privateKey Object , so before BER Decoding and importing key we need to somehow determine the OID for the Object;
the simplest way is to determine it when Initializing new Object
above code changes to :
ECIES<ECP>::PrivateKey pro;
StringSource savesSource(saves, true);
auto rett = savesSource.MaxRetrievable();
pro.Initialize(prng, ASN1::secp256k1());
pro.BERDecodePrivateKey(savesSource,true,savesSource.MaxRetrievable());
also you AccessGroupParameters().Initialize(/*OID*/); or Initialize(/*OID*/) for existing object
I am generating ECDSA Prime256 keypair using OpenSSL with C++ and trying to import the hex version of the public key using Java. I pass the byte array I obtain from C++ to the following function in java which expects the byte array to be in an X.509 encoded format.
public static PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
KeyFactory kf = KeyFactory.getInstance(Constant.KEY_FACTORY_TYPE);
PublicKey pub = kf.generatePublic(publicKeySpec);
return pub;
}
I create an elliptic curve key pair using the following function which retuns an EC_KEY*
EC_KEY* generate_keypair() {
EC_KEY *eckey = EC_KEY_new();
EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY_set_group(eckey, ecgroup);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
int kpGenerationStatus = EC_KEY_generate_key(eckey);
if (kpGenerationStatus) {
return eckey;
}
return nullptr;
}
Given the keypair returned by the function above, I want to export the public key to an ASN1.DER format which can be imported using the java method above.
I convert the public key which is of type EC_POINT* to its hex form using EC_POINT_point2hex() by doing the following:
EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY *keypair = generate_keypair();
char *result = NULL;
BN_CTX *ctx;
ctx = BN_CTX_new();
const EC_POINT *pub = EC_KEY_get0_public_key(keypair);
result = EC_POINT_point2hex(ecgroup, pub, POINT_CONVERSION_UNCOMPRESSED, ctx);
printf("%s\n", result);
Which return the following:
04F588CD1D7103A993D47E53D58C3F40BE8F570604CF2EA01A7657C1423EB19C51BC379F0BEE1FAA60BB9A07DE73EA9BEF7709C1C6429D4051B44F73A458FFB80D
When I inspect this with the ASN.1 decoder I see a message which says Length over 48 bits not supported at position 1 and trying to import it using the java method I receive an error as follows:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): Should use short form for length
Is there something I am missing while exporting the public key from EC_POINT* to a X.509 Encoded hex string that I can import for validating any signatures?
You are going in the incorrect direction as you want ASN1 base64 value.
EC_POINT_point2hex is converting the internal public key value to hex. It's not in ASN1 format.
You can produce what you want from the command line like so:
Generate EC private key:
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
Extra public key in DER(ASN1) format:
openssl ec -in key.pem -pubout -outform der -out public.cer
Convert to base64
openssl base64 -in .\public.cer
If you take that output and paste it into ASN.1 decoder link it works fine.
Now to turn this into code, you have the EC key generation, but what you want is the steps to:
Generate ASN1 formatted public key
Convert it to base64
To generate the ASN1 formatted public key you want to use the i2d_EC_PUBKEY set of methods and then convert to base64 using BIO_f_base64 filter.
So here is an example problem that when I copy the output to ASN.1 decoder link it works fine.
#include <openssl/bio.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
EC_KEY* generate_keypair() {
EC_KEY *eckey = EC_KEY_new();
EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY_set_group(eckey, ecgroup);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
int kpGenerationStatus = EC_KEY_generate_key(eckey);
if (kpGenerationStatus) {
return eckey;
}
return nullptr;
}
int main()
{
EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY *keypair = generate_keypair();
BIO* out = BIO_new(BIO_s_mem());
BIO* b64 = BIO_new(BIO_f_base64());
BIO_push(b64, out);
i2d_EC_PUBKEY_bio(b64, keypair);
BIO_flush(b64);
// do what you want this the output in out memory BIO
char* p;
long length = BIO_get_mem_data(out, &p);
// ensure null terminated but copying the buffer into a string to output...
puts(std::string(p, length).c_str());
BIO_free_all(out);
}
I can't complete on the Java side, but if it works with the manual openssl generated base64 string then it will work with the sample application.
I need to store certificates and their private key in memory.
Certificates can be in the 4 following formats : PEM, PKCS12, PKCS7, DER.
I'ill need to write them back as PEM later.
All the snippets i see are storing only the public certificate in a X509 struct.
What about the private part ??
I've found a way using X509_INFO, but i got a major problem with it :
I haven't find a way to get a X509_INFO from DER/PKCS7/PKCS12 files
For the moment i got the following code :
QList<X509_INFO*>* Certificat::stringPEMToX509_INFO(QString stringPem)
{
QList <X509_INFO*>* liste_certificats = new QList<X509_INFO*>;
STACK_OF(X509_INFO)* pile_certificats = NULL;
X509_INFO* certificat;
BIO* bio = BIO_new(BIO_s_mem());
const char* pem = stringPem.toAscii().constData();
BIO_puts(bio, pem);
//https://github.com/openssl/openssl/blob/master/crypto/pem/pem_info.c
pile_certificats = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
for (int i = 0; i < sk_X509_INFO_num(pile_certificats); i++)
{
certificat = sk_X509_INFO_value(pile_certificats, i);
liste_certificats->push_back(certificat);
}
sk_X509_INFO_pop_free(pile_certificats, X509_INFO_free);
BIO_free_all(bio);
return liste_certificats;
}
My goal would be to have the same function but for DER, PKCS12 and PKCS7.
I tried to get a X509_INFO from a DER like this :
p12 = d2i_PKCS12_bio(bio, NULL);
certificat = X509_INFO_new();
certificat->x509 = cert;
certificat->x_pkey = pkey;
But x_pkey is a X509_PKEY and pkey an EVP_PKEY...
If there is no way to store it as a single struct, would it be possible to store my certificates as X509 + a EVP_PKEY for the private key, and still output both private and public part in a PEM ?
PKCS7 is only meant for public keys. DER and PEM are simply ways of encoding a PKCS (and many other) objects. Since you want to store everything into a single structure, you would probably most benefit from PKCS12. OpenSSL provides functions to parse PKCS12 data and get both the cert and key out of it.
I'm using C++ with nss and nspr libraries on 64 bit Ubuntu Linux and am trying to convert CERTCertificate derCert to SECKEYPublicKey but SECKEY_ImportDERPublicKey keeps returning -8183:
Security library: improperly formatted DER-encoded message.
I have also tried to use SECKEY_ImportDERPublicKey with CERTCertificate derPublicKey but I got the same response.
Which function pair should be used for derCert and which for derPublicKey conversion to SECItem and back to SECKEYPublicKey or CERTCertificate?
To answer my own question...
CERTCertificate contains two member variables derCert and derPublicKey (both of type SECItem) that I was interested in.
Save/Load public key
To get the public key you can either save CERTCertificate derPublicKey value or get the same value from SECKEYPublicKey:
// cert is of type CERTCertificate
SECKEYPublicKey* publicKey = CERT_ExtractPublicKey( cert );
SECItem* derPublicKey = SECKEY_EncodeDERSubjectPublicKeyInfo( publicKey );
// put the key into string
std::string keyString( (char*)derPublicKey->data, derPublicKey->len );
To decode the public key from string you use:
SECItem derKeyItem = {
.type = siBuffer,
.data = (unsigned char*)keyString.c_str(),
.len = (unsigned int)keyString.size()
};
CERTSubjectPublicKeyInfo* pubInf = SECKEY_DecodeDERSubjectPublicKeyInfo( &derKeyItem );
SECKEYPublicKey* publicKey = SECKEY_ExtractPublicKey( pubInf );
Save/Load certificate and get public key
To save certificate you save derCert.
To load certificate and get public key:
SECItem derCertItem = {
.type = siBuffer,
.data = (unsigned char*)certStr.c_str(),
.len = (unsigned int)certStr.size()
};
CERTCertificate cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCertItem, nullptr, false, false);
SECKEYPublicKey* publicKey = CERT_ExtractPublicKey(cert);
Note
The above code is sample code. For production code smart pointers (unique/shared) should be used and their destructors should call the appropriate nss destroy functions.