Reputation: 3222
I have an EVP_PKEY with only the public part of a RSA key. I extracted the public part from a SubjectPublicKeyInfo structure in DER encoding. This is what I have now:
unsigned char publicKey[] = {0x30, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ...}
size_t publicKeyLength = 92;
unsigned char* publicKeyCopy = new unsigned char[publicKeyLength];
memcpy(publicKeyCopy, publicKey, publicKeyLength);
RSA *rsa;
rsa = d2i_RSA_PUBKEY(NULL, (unsigned char const **) &pubKey, pubKeyLen);
EVP_PKEY *pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
I know that you can use RSA_check_key to verify a RSA private key but the docs say that "It does not work on RSA public keys that have only the modulus and public exponent elements populated".
So, is it possible to verify a key without the private part? Because as you can see I have only the public part of the EVP_PKEY. I wonder, is this even possible? What would you verify in a public part of an EVP_PKEY?
You can see the answer for this question Programmatically verify a X509 certificate and private key match but there the full key is validated (private and public parts).
Beware
The original code posted in this question has a BUG. This is because internally d2i_RSA_PUBKEY
uses d2i_PUBKEY
and d2i_PUBKEY
uses d2i_X509_PUBKEY
(in x_pubkey.c). If you read the documentation for d2i_X509 you will see the next "WARNING: The use of temporary variable is mandatory. A common mistake is to attempt to use a buffer directly...".
So the corrected code will have to use a temporary copy of publicKeyCopy
and after the use you could safely delete publicKeyCopy
:
Upvotes: 2
Views: 5678
Reputation: 49
I had a similiar problem and I thought it may be prudent to display my solution to this issue. Unlike lmiguelmh's solution, this one does work in C.
int checkRsaPublic(RSA *rsa, int debug) {
if (!rsa) {
printf("ERROR: RSA key not defined!\n");
return 0;
}
//key
const BIGNUM *n;
const BIGNUM *e;
const BIGNUM *d;
//factors
const BIGNUM *p;
const BIGNUM *q;
//crt_params
const BIGNUM *dmp1;
const BIGNUM *dmq1;
const BIGNUM *iqmp;
RSA_get0_key(rsa, &n, &e, &d);
RSA_get0_factors(rsa, &p, &q);
RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
if (debug) {
if (n) {
printf("n is %s\n", BN_bn2hex(n));
}
if (e) {
printf("e is %s\n", BN_bn2hex(e));
}
if (d) {
printf("d is %s\n", BN_bn2hex(d));
}
if (p) {
printf("p is %s\n", BN_bn2hex(p));
}
if (q) {
printf("q is %s\n", BN_bn2hex(q));
}
if (dmp1) {
printf("dmp1 is %s\n", BN_bn2hex(dmp1));
}
if (dmq1) {
printf("dmq1 is %s\n", BN_bn2hex(dmq1));
}
if (iqmp) {
printf("iqmp is %s\n", BN_bn2hex(iqmp));
}
}
//RSA_check_key : doesn't have n (modulus) and e (public exponent)
if (d || !n || !e) {
printf("ERROR: RSA public key not well defined!\n");
return 0;
}
if (BN_is_odd(e) && !BN_is_one(e)) {
return 1;
}
printf("ERROR: Invalid public exponent.");
return 0;
}
Upvotes: 0
Reputation: 102396
Beware The original code posted in this question has a BUG...
I'm just going to comment on this, and show you how to handle it.
unsigned char publicKey[] = {0x30, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, ...}
size_t publicKeyLength = sizeof(publicKey);
unsigned char* t = publicKey;
rsa = d2i_RSA_PUBKEY(NULL, &t, pubKeyLen);
Internally, the temporary pointer t
is incremented, so its wasted. It will point to some place after the buffer if everything works as expected. What you should find is (size_t)t - (size_t)publicKey == publicKeyLength
after the function executes.
Because you used a temporary pointer, the original pointer publicKey
is still good. And you can use t
to parse the next key if there are consecutive keys in memory.
There's no need to copy the data.
I think a second option is to use a memory BIO
and d2i_RSA_PUBKEY_bio
. Something like:
BIO* bio = BIO_new_mem_buf(publicKey, (int)publicKeyLength);
ASSERT(bio != NULL);
RSA* rsa = d2i_RSA_PUBKEY_bio(bio, NULL);
ASSERT(rsa != NULL);
/* ... */
RSA_free(rsa);
BIO_free(bio);
The get1
bumps the reference count, so you need to call free
on both the EVP_PKEY*
and RSA*
.
Upvotes: 1
Reputation: 3222
With the help of @jww in this answer https://stackoverflow.com/a/29885771/2692914. I came up with this solution, I hope it is ok:
bool isValidPublicKeyOnly(EVP_PKEY *pkey) {
//EVP_PKEY_get_type from https://stackoverflow.com/a/29885771/2692914
int type = EVP_PKEY_get_type(pkey); //checks nullptr
if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA2) {
//not RSA
return false;
}
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
if (!rsa) {
return false;
}
bool isValid = isValidRSAPublicKeyOnly(rsa);
RSA_free(rsa);
return isValid;
}
bool isValidRSAPublicKeyOnly(RSA *rsa) {
//from rsa_ameth.c do_rsa_print : has a private key
//from rsa_chk.c RSA_check_key : doesn't have n (modulus) and e (public exponent)
if (!rsa || rsa->d || !rsa->n || !rsa->e) {
return false;
}
//from http://rt.openssl.org/Ticket/Display.html?user=guest&pass=guest&id=1454
//doesnt have a valid public exponent
return BN_is_odd(rsa->e) && !BN_is_one(rsa->e);
}
Upvotes: 0