问题
I want to feed OpenSSL specific data for use as random seed during the signing of data with an EC key. I'm doing this to compare my application with another reference one (closed source). That utility takes the file with private key, file with data to sign and file with random data as parameters.
I've got the generation of EC keys, and signing of data down, but can't compare the two applications since I have no common ground. OpenSSL generates random data used in signing the data (probably from /dev/random) and thus gives me a different signature every run.
I've tried RAND_clear() in combination with RAND_add(), but keep getting changing signatures. Either I don't understand the whole ECDSA concept, or I'm doing something wrong.
My second option for comparing the applications is to import the public key and verify the signature generated by the reference program. This is the better option, but I'm unable to import the given example public key (83 character hex string). EC_POINT_oct2point() keeps giving me null results.
Any help/pointers/references would be greatly appreciated.
char * key_as_binary_data; //369368AF243193D001E39CE76BB1D5DA08A9BC0A63307AB352338E5EA5C0E05A0C2531866F3E3C2702
int data_size; //Size of the key buffer
EC_POINT * ecpoint = NULL;
EC_GROUP * ecgroup = NULL;
EC_KEY * eckey = NULL;
point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
int asn1_flag = OPENSSL_EC_NAMED_CURVE;
eckey = EC_KEY_new();
ecpoint = EC_POINT_new(ecgroup);
ecgroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("sect163k1"));
EC_GROUP_set_asn1_flag(ecgroup, asn1_flag);
EC_GROUP_set_point_conversion_form(ecgroup, form);
EC_KEY_set_group(eckey,ecgroup);
EC_KEY_generate_key(eckey);
//This gives me a null ecpoint
EC_POINT_oct2point(ecgroup,ecpoint,key_as_binary_data,data_size-1,ctx);
EC_KEY_set_public_key(eckey,ecpoint);
回答1:
This is how you should go about loading that public key:
EC_KEY *key = NULL;
EC_POINT *pub_key;
const EC_GROUP *group;
SSL_library_init();
SSL_load_error_strings();
key = EC_KEY_new_by_curve_name(NID_sect163k1);
group = EC_KEY_get0_group(key);
pub_key = EC_POINT_new(group);
EC_POINT_hex2point(group,
"369368AF243193D001E39CE76BB1D5DA08A9BC0A63307AB352338E5EA5C0E05A0C2531866F3E3C2702", pub_key, NULL);
EC_KEY_set_public_key(key, pub_key);
if (!EC_KEY_check_key(key)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n",ERR_error_string(ERR_get_error(),NULL));
} else {
printf("Public key verified OK\n");
}
It seems to verify OK, so it should work for checking a signature.
I think your bug might have just been passing a NULL (in ecgroup) to EC_POINT_new().
回答2:
The reason you get different results despite the fact that you are clearing the pool and resetting it is that by default OpenSSL's RAND implementation will hash the pid into the output block (precisely to ensure that even applications that use the same seed do not get the same PRNG output, since 99.9% of the time that happening is a Bad Thing).
In addition, even if this was not the case, it is unlikely that your reference application uses the same PRNG that OpenSSL uses to turn the seed file into a series of random bytes. (Unless your reference application actually uses OpenSSL as well, of course). What you would have to do is first figure out what kind of PRNG the reference app uses - this might be a standard PRNG design like the ones from X9.31 or FIPS-186, or might be something totally custom. Then reimplement that design for OpenSSL and plug it in via RAND_set_rand_method.
As to verification: it looks like you need to transpose the lines:
ecpoint = EC_POINT_new(ecgroup);
ecgroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("sect163k1"));
Otherwise ecpoint is set to NULL right from the start, and this causes EC_KEY_generate_key to fail, because the group is set to NULL. Quoting from openssl-0.9.8k's crypto/ec/ec_key.c:
if (!eckey || !eckey->group)
{
ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
回答3:
You can control the random data that OpenSSL produces during signing by using the method:
ECDSA_SIG* ECDSA_do_sign_ex(const unsigned char *dgst, int dgstlen, const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *eckey);
where kinv is the random number that is used during signing.
回答4:
The signing procedure is purely to allow someone else to confirm that you signed it ie. it was your private key that was used to sign the message (or any data) without actually having your private key.
The algorithm is outlined on wikipedia Elliptic_Curve_DSA Reading "Signature generation algorithm" it appears the random data is used to assist in the strength of the signature and to make it harder to attack to figure out the private key.
Therefore you should expect the signature to be different each time since this is not just simply a hash.
See the section "Signature verification algorithm" to see that the verification steps are the ones you wish to use to confirm that your openssl version is outputing valid signatures w.r.t. the ECDSA method and the closed source program.
回答5:
I can't find the docs for RAND_clear, so can't comment why it's not resulting in reproducible random numbers.
But even if you get that done, what you want may not be possible. ECDSA signature generation requires choosing a random integer in a particular range. Two different implementations of the algorithm might have completely different ideas about how to generate such an integer from their entropy source. AFAIK the means of transforming bits of entropy into the required number is not part of the ECDSA algorithm.
So for example (very bad examples of course), one implementation might do this:
int random_number = rand() % n;
and another might do this:
int random_number = (rand() * n) / RAND_MAX;
So even if you give them the same seed data, you might still get different signatures from different implementations. All you can do is validate whether you have generated a valid signature.
来源:https://stackoverflow.com/questions/1171207/how-do-i-feed-openssl-random-data-for-use-in-ecdsa-signing