I'm writing a routine in C that reads a base64 string with the public key and proceeds to encrypt a string. I also test the same string's decryption but I'm getting error 0x0407106B when trying to do the decoding:
$ openssl errstr 0x0407106B
error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02
Here's the code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>
//#define PADDING RSA_PKCS1_OAEP_PADDING
#define PADDING RSA_PKCS1_PADDING
//#define PADDING RSA_NO_PADDING
main() {
// public key
char *b64_pKey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp2w+8HUdECo8V5yuKYrWJmUbL\ntD6nSyVifN543axXvNSFzQfWNOGVkMsCo6W4hpl5eHv1p9Hqdcf/ZYQDWCK726u6\nhsZA81AblAOOXKaUaxvFC+ZKRJf+MtUGnv0v7CrGoblm1mMC/OQI1JfSsYi68Epn\naOLepTZw+GLTnusQgwIDAQAB\n-----END PUBLIC KEY-----\n";
// private key
char *b64priv_key = "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCp2w+8HUdECo8V5yuKYrWJmUbLtD6nSyVifN543axXvNSFzQfW\nNOGVkMsCo6W4hpl5eHv1p9Hqdcf/ZYQDWCK726u6hsZA81AblAOOXKaUaxvFC+ZK\nRJf+MtUGnv0v7CrGoblm1mMC/OQI1JfSsYi68EpnaOLepTZw+GLTnusQgwIDAQAB\nAoGBAKDuq3PikblH/9YS11AgwjwC++7ZcltzeZJdGTSPY1El2n6Dip9ML0hUjeSM\nROIWtac/nsNcJCnvOnUjK/c3NIAaGJcfRPiH/S0Ga6ROiDfFj2UXAmk/v4wRRUzr\n5lsA0jgEt5qcq2Xr/JPQVGB4wUgL/yQK0dDhW0EdrJ707e3BAkEA1aIHbmcVfCP8\nY/uWuK0lvWxrIWfR5MlHhI8tD9lvkot2kyXiV+jB6/gktwk1QaFsy7dCXn7w03+k\nxrjEGGN+kQJBAMuKf55lDtU9K2Js3YSStTZAXP+Hz7XpoLxmbWFyGvBx806WjgAD\n624irwS+0tBxkERbRcisfb2cXmAx8earT9MCQDZuVCpjBWxd1t66qYpgQ29iAmG+\njBIY3qn9uOOC6RSTiCCx1FvFqDMxRFmGdRVFxeyZwsVE3qNksF0Zko0MPKECQCEe\noDV97DP2iCCz5je0R5hUUM2jo8DOC0GcyR+aGZgWcqjPBrwp5x08t43mHxeb4wW8\ndFZ6+trnntO4TMxkA9ECQB+yCPgO1zisJWYuD46KISoesYhwHe5C1BQElQgi9bio\nU39fFo88w1pok23a2CZBEXguSvCvexeB68OggdDXvy0=\n-----END RSA PRIVATE KEY-----\n";
// String to encrypt
char *str = "1234";
ERR_load_crypto_strings();
BIO *bpo = BIO_new_mem_buf(b64_pKey, -1);
RSA *pubKey = PEM_read_bio_RSA_PUBKEY(bpo, NULL, NULL, NULL);
if ( !pubKey ) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return;
}
int rsa_length = RSA_size(pubKey);
BIO *b64 = NULL;
BIO *bmem = NULL;
BUF_MEM *bptr = NULL;
unsigned char encrypted[2560] = { 0 };
unsigned char retencrypted[2560] = { 0 };
int resultEncrypt = RSA_public_encrypt(PADDING, str, encrypted, pubKey, PADDING);
if ( resultEncrypt == -1 ) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return;
}
/*
* Show base 64 encrypted string
*/
b64 = BIO_new((BIO_METHOD *)BIO_f_base64());
BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, encrypted, resultEncrypt);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
memcpy(retencrypted, bptr->data, bptr->length);
BIO_free(b64);
BIO_free(bpo);
RSA_free(pubKey);
printf("Encrypted string:%s\n",retencrypted);
/*
* Now decrypt this very string with the private key
*/
BIO *bpop = BIO_new_mem_buf(b64priv_key, -1);
RSA *privKey = PEM_read_bio_RSAPrivateKey(bpop, NULL, NULL, NULL);
if ( !privKey ) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return;
}
rsa_length = RSA_size(privKey);
unsigned char decrypted[2560] = { 0 };
int resultDecrypt = RSA_private_decrypt( RSA_size(privKey), retencrypted, decrypted, privKey, PADDING);
if ( resultDecrypt == -1 ) {
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
return;
}
printf("resultDecrypt=%d\ndecrypted string: %s\n",resultDecrypt,decrypted);
BIO_free(bpop);
RSA_free(privKey);
ERR_free_strings();
}
Note: I exported the private key using
openssl rsa -in rsa_privatekey.pem -check
and the public key:
openssl rsa -in rsa_privatekey.pem -pubout
Why am I getting the error?
The problem is that you're trying to decrypt the base64 encoded result.
You should try to decrypt the result of the encryption.
That is, instead of:
int resultDecrypt = RSA_private_decrypt( RSA_size(privKey), retencrypted, decrypted, privKey, PADDING);
You should call:
int resultDecrypt = RSA_private_decrypt( RSA_size(privKey), encrypted, decrypted, privKey, PADDING);
Also, there is a problem in the encryption call:
int resultEncrypt = RSA_public_encrypt(PADDING, str, encrypted, pubKey, PADDING);
Why are you passing PADDING as flen? This should be the length of the string (i.e. 4 or 5, depending on whether you want to encrypt the null character).
If you want to write the encrypted string as ASCII (encoded using base64), that's fine. But you have to decode it back before you decrypt it.
The error you're getting is block type is not 02.
Although Omri is correct that you're passing the wrong data, and you are only going to encrypt 1 byte, the error is because the sizeof( encrypted ) is way too large (2560). In other words your data receiver for RSA_public_encrypt must be a regular unsigned char* pointer, not an unsigned char[2560].
Where you have
unsigned char encrypted[2560] = { 0 }; //X 2560?? RSA_public_encrypt fails.
You should be using
unsigned char *encrypted = (unsigned char*)malloc( rsa_length ) ;
RSA_public_encrypt( DATALEN, (const unsigned char*)str, encrypted, pubKey, PADDING ) ;
Notice the error Omri pointed out, that you used PADDING as the first arg to RSA_public_encrypt, while it should be the DATALEN data length.
If you fix that you'll get a similar error later with the private key decrypt. Fix it and you're on your way.
Related
I generate my EC private key as random number and my EC public key as the multiplication of the generator for the random number.
Now I would to use these keys to sign and verify a message, but the operation about the verification fails. Do you have any hint to solve this problem?
I'm using OpenSSL 1.0.0 for some constraints. I cannot switch the new one.
#include <openssl/ec.h>
#include <openssl/rand.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <stdio.h>
#include <string.h>
int main()
{
BN_CTX *ctx;
EC_GROUP *curve;
BIGNUM *q = NULL;
EVP_MD_CTX *hashctx;
unsigned int ol;
unsigned char fh[EVP_MAX_MD_SIZE];
BIGNUM *priv = NULL;
EC_POINT *G, *pub;
EC_KEY *keypriv = EC_KEY_new();
EC_KEY *keypub = EC_KEY_new();
unsigned char *s1 = "hello";
// makes all algorithms available to the EVP* routines
OpenSSL_add_all_algorithms();
// load the error strings for ERR_error_string
ERR_load_crypto_strings();
hashctx = EVP_MD_CTX_new();
const EVP_MD *hashptr = EVP_get_digestbyname("SHA256");
EVP_DigestInit_ex(hashctx, hashptr, NULL);
EVP_DigestUpdate(hashctx, s1, strlen(s1));
EVP_DigestFinal_ex(hashctx, fh, &ol);
// seed PRNG
if (RAND_load_file("/dev/urandom", 256) < 64)
{
printf("Can't seed PRNG!\n");
abort();
}
ctx = BN_CTX_new();
q = BN_new();
// read the generator and order of the curve
curve = EC_GROUP_new_by_curve_name(NID_secp160r1);
G = EC_GROUP_get0_generator(curve);
EC_GROUP_get_order(curve, q, ctx);
// precompute multiples of g (faster multiplications)
EC_GROUP_precompute_mult(curve, ctx);
EC_KEY_set_group(keypriv, curve);
pub = EC_POINT_new(curve);
priv = BN_new();
BN_rand_range(priv, q);
EC_POINT_mul(curve, pub, NULL, G, priv, ctx);
EC_KEY_set_private_key(keypriv, priv);
EC_KEY_set_public_key(keypub, pub);
ECDSA_SIG *signature = ECDSA_do_sign(fh, 64, keypriv);
if (NULL == signature)
{
printf("Failed to generate EC Signature\n");
}
else
{
int verify_status = ECDSA_do_verify(fh, 64, signature, keypub);
const int verify_success = 1;
if (verify_success != verify_status)
{
printf("Failed to verify EC Signature\n");
}
else
{
printf("Verifed EC Signature\n");
}
ECDSA_SIG_free(signature);
}
return 0;
}
I solved just by setting the EC Group for the public key in my code as:
EC_KEY_set_group(keypub, curve);
By the way, I hope that this Q/A can answer also for the first comment in the accepted answer:
How do you verify with just the public part of the eckey?
Signing a message using ECDSA in OpenSSL
I am playing with OpenSSL 1.0.2o version. I compiled from OpenSSL only static libcrypto. I used this configuration flags:
no-demos, no-bugs, no-apps, no-ssl, no-test, no-shared, no-zlib, no-zlib-dynamic, no-ssl-trace, no-unit-test, no-ec_nistp_64_gcc_128, no-libunbound, no-ssl1, no-ssl2, no-ssl3, no-asm, no-dtls, no-dtls1, no-threads, no-npn, no-weak-ssl-ciphers, no-rfc3779, no-sctp, no-ui, no-async, no-dgram, no-posix-io, no-sock, no-des, no-dso, no-srp, no-store, no-ts, no-txt_db, no-hw, no-ec, no-gmp, -DOPENSSL_NO_STDIO, -DOPENSSL_NO_FP_API, -DOPENSSL_NO_DYNAMIC_ENGINE,-UOPENSSL_FIPS.
I use OpenSSL into small embedded device. (without file operations, without operating system and without libc).
I import RSA public and private keys from memory from PEM-strings and then I want to use it for sign/verify, but RSA_sign() function returns zero. May be am I do that wrong?
Import keys:
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/bn.h>
typedef RSA *(*read_bio2rsa_f)(BIO *, RSA **, pem_password_cb *, void *);
static BIO *pub_bio;
static RSA *pub_key;
static BIO *prv_bio;
static RSA *prv_key;
static RSA *openssl_read_key_rsa(int rsa_type, BIO **bio)
{
RSA *rsa;
char *pem_str;
int pem_str_len;
read_bio2rsa_f read_bio2rsa;
if (rsa_type == PUB_KEY_TYPE) {
pem_str = (char *)pem_pub_key;
pem_str_len = (int)sizeof(pem_pub_key);
read_bio2rsa = PEM_read_bio_RSA_PUBKEY;
} else {
pem_str = (char *)pem_prv_key;
pem_str_len = (int)sizeof(pem_prv_key);
read_bio2rsa = PEM_read_bio_RSAPrivateKey;
}
if ((*bio = BIO_new_mem_buf((const void *)pem_str,
pem_str_len)) == NULL) {
EMSG("BIO_new_mem_buf() FAILED read PEM key");
return NULL;
}
if ((rsa = RSA_new()) == NULL) {
EMSG("RSA_new() FAILED");
return NULL;
}
read_bio2rsa(*bio, &rsa, NULL, NULL);
return rsa;
}
static int check_rsa_key_pair(RSA *pub, RSA *priv)
{
if (BN_cmp(pub->n, priv->n) != 0)
return CRYPTO_ERR;
return CRYPTO_OK;
}
/* extrnal function for import RSA-keys */
int openssl_rsa_init_key(void)
{
ERR_load_crypto_strings();
OPENSSL_add_all_algorithms_noconf();
if ((prv_key = openssl_read_key_rsa(PRV_KEY_TYPE, &prv_bio)) == NULL) {
EMSG("Importing the private key FAILED!");
return CRYPTO_ERR;
}
if ((pub_key = openssl_read_key_rsa(PUB_KEY_TYPE, &pub_bio)) == NULL) {
EMSG("Importing the public key FAILED!");
return CRYPTO_ERR;
}
if (!check_rsa_key_pair(pub_key, prv_key)) {
EMSG("Key pair don't match");
return CRYPTO_ERR;
}
EMSG("Import KEYs is successful!");
return CRYPTO_OK;
}
All code above is executed successfully. After this, in theory, I can free use the rsa keys into any OpenSSL functions that expected RSA-type.
I tried to do signature like this:
int openssl_rsa_sign_hash(uint8_t *hash, unsigned int hash_len,
uint8_t *sig, int *sig_len)
{
if (!RSA_sign(NID_sha256, (const unsigned char *)hash, hash_len,
(unsigned char *)sig, (unsigned int *)sig_len,
prv_key)) {
EMSG("RSA signature FAILED with %s",
ERR_error_string(ERR_get_error(), NULL));
return CRYPTO_ERR;
}
EMSG("RSA signature success!");
return CRYPTO_OK;
}
But, I got "RSA signature FAILED with error:00000000:lib(0):func(0):reason(0)" this string into my error output.
Could anyone explain me the mistakes, please?
But, I got "RSA signature FAILED with
error:00000000:lib(0):func(0):reason(0)" this string into my error
output.
Could anyone explain me the mistakes, please?
I don't see a main function, so this is just speculation...
Add a call to SSL_library_init and ERR_load_crypto_strings in main. You may also need a call to OpenSSL_add_all_ciphers.
Since you are not getting a good error string, you might try printing the result of ERR_get_error(). Once you get the result of ERR_get_error(), you can run it through the openssl errstr command:
$ openssl errstr 0406506C
error:0406506C:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data greater than mod len
Also see Library Initialization on the OpenSSL wiki.
This question already has answers here:
Use OpenSSL RSA key with .Net
(1 answer)
How to generate RSA private key using OpenSSL?
(3 answers)
Closed 5 years ago.
I need to write a C program that generates an RSA key, and saves an X.509 public key in DER format and a PKCS#8 private key in DER format. I've used Google, but haven't really found much. What I have so far is this:
#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
void main() {
int ret = 0;
RSA *r = NULL;
BIGNUM *bne = NULL;
BIO *bp_public = NULL, *bp_private = NULL;
int bits = 2048;
unsigned long e = RSA_F4;
// Generate the RSA key
printf("Generating RSA key...\n");
bne = BN_new();
ret = BN_set_word(bne, e);
if(ret != 1) {
goto free_all;
}
r = RSA_new();
ret = RSA_generate_key_ex(r, bits, bne, NULL);
if(ret != 1) {
goto free_all;
}
// Save the public key in PEM format
printf("Writing key files...\n");
bp_public = BIO_new_file("public.pem", "w+");
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
if(ret != 1) {
goto free_all;
}
// Save the private key in PEM format
bp_private = BIO_new_file("private.pem", "w+");
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
// Free everything
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
printf("Done!\n");
}
This is obviously writing the keys in PEM format. I also need to be able to actually have the data in memory in the code, not just write it directly to a file, as there's some other stuff I need to do with the public key.
Thank you for any help
Your question is a little ambiguous in what you actually mean by "saves an X.509 public key in DER format". Assuming you actually mean "save it as a SubjectPublicKeyInfo structure" (which is the bit of an X.509 certificate that holds public keys) then you should use i2d_RSA_PUBKEY (or i2d_RSA_PUBKEY_fp or i2d_RSA_PUBKEY_bio) to write it out (no need to convert it to an EVP_PKEY first).
For the PKCS#8 private key in DER format, your current method is incorrect for PEM format. The PEM_write_bio_RSAPrivateKey() function will write this out in traditional format (not PKCS#8).
I assume you don't want to do anything complicated like encrypting the key first. For this one you will need to convert it to an EVP_PKEY (using EVP_PKEY_assign_RSA() as mentioned by #JawguyChooser). Next you obtain a PKCS8_PRIV_KEY_INFO structure using the (sadly undocumented) function EVP_PKEY2PKCS8.
PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey);
You need to free this structure when your done using PKCS8_PRIV_KEY_INFO_free(). Next write out the PKCS8 DER using i2d_PKCS8_PRIV_KEY_INFO() (or i2d_PKCS8_PRIV_KEY_INFO_fp() or i2d_PKCS8_PRIV_KEY_INFO_bio).
See the man page for info on various of these functions:
https://www.openssl.org/docs/man1.1.0/crypto/i2d_RSAPublicKey.html
I think you're going to need to convert your key to an EVP_PKEY using EVP_PKEY_assign_RSA. Then you can use i2d_PUBKEY_bio to write out to a bio.
The following modification of your code works for me:
include the header file <openssl/evp.h>
declare BIO for writing out the public_der
create EVP_PKEY structure with EVP_PKEY_new()
convert using EVP_PKEY_assign_RSA
write out to bio with i2d_PUBKEY_bio
In context:
#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
void main() {
int ret = 0;
RSA *r = NULL;
BIGNUM *bne = NULL;
BIO *bp_public = NULL, *bp_private = NULL, *bp_public_der = NULL;
int bits = 2048;
unsigned long e = RSA_F4;
// Generate the RSA key
printf("Generating RSA key...\n");
bne = BN_new();
ret = BN_set_word(bne, e);
if(ret != 1) {
goto free_all;
}
r = RSA_new();
ret = RSA_generate_key_ex(r, bits, bne, NULL);
if(ret != 1) {
goto free_all;
}
// Save the public key in PEM format
printf("Writing key files...\n");
bp_public = BIO_new_file("public.pem", "w+");
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
if(ret != 1) {
goto free_all;
}
// Save the private key in PEM format
bp_private = BIO_new_file("private.pem", "w+");
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
// Save in DER
EVP_PKEY *evp = EVP_PKEY_new();
ret = EVP_PKEY_assign_RSA(evp, r);
if(ret != 1){
printf("failure %i\n", ret);
}
bp_public_der = BIO_new_file("public.key", "w+");
ret = i2d_PUBKEY_bio(bp_public_der, evp);
// Free everything
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_public_der);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
printf("Done!\n");
}
You can now find the DER of the public key in public.key. And you should be able to do the same thing for the private.
Hope this helps.
I have the following program. It successfully obtains the raw 32 byte EC private key data and then creates a EC_KEY from it. But i2d_ECPrivateKey fails to give the size of the DER encoded private key, as it crashes. Does anyone know why and how to fix this?
#include "CBWIF.h"
#include <openssl/ssl.h>
int main(int argc, char * argv[]) {
CBWIF wif;
if (argc != 2)
return EXIT_FAILURE;
// Decode WIF string
CBByteArray str;
CBInitByteArrayFromString(&str, argv[1], false);
CBInitWIFFromString(&wif, &str, false);
CBDestroyByteArray(&str);
// Get key
uint8_t key[32];
CBWIFGetPrivateKey(&wif, key);
CBDestroyWIF(&wif);
// Create OpenSSL key
EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);
if (!EC_KEY_set_private_key(eckey, bn))
return EXIT_FAILURE;
// Convert key to DER format
int len = i2d_ECPrivateKey(eckey, NULL); // <-- CRASH HERE
unsigned char derkey[len];
i2d_ECPrivateKey(eckey, (unsigned char **)&derkey);
EC_KEY_free(eckey);
// Encode DER key as hex
char out[len*2+1];
CBBytesToString(derkey, 0, len, out, false);
// Print to stdout
puts(out);
return EXIT_SUCCESS;
}
CB_PRIVKEY_SIZE is 32. I verified that the key data from CBWIFGetPrivateKey is correct. The program crashes with the following stacktrace:
#0 0x00007ffff766cb03 in EC_POINT_point2oct () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#1 0x00007ffff7658124 in i2d_ECPrivateKey () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2 0x0000000000400bf6 in main (argc=2, argv=0x7fffffffe038) at examples/WIF2DER.c:46
The reason is that you cannot run i2d_ECPrivateKey without setting a public key, probably due to a bug in OpenSSL. I generated the public key and solved a few other issues an now the program works:
#include "CBWIF.h"
#include <openssl/ssl.h>
int main(int argc, char * argv[]) {
CBWIF wif;
if (argc != 2)
return EXIT_FAILURE;
// Decode WIF string
CBByteArray str;
CBInitByteArrayFromString(&str, argv[1], false);
CBInitWIFFromString(&wif, &str, false);
CBDestroyByteArray(&str);
// Get key
uint8_t key[32];
CBWIFGetPrivateKey(&wif, key);
CBDestroyWIF(&wif);
// Create OpenSSL key
EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);
if (!EC_KEY_set_private_key(eckey, bn))
return EXIT_FAILURE;
// Create public key as OpenSSL cannot do this easily
EC_GROUP * group = EC_GROUP_new_by_curve_name(NID_secp256k1);
EC_POINT * point = EC_POINT_new(group);
BN_CTX * ctx = BN_CTX_new();
EC_POINT_mul(group, point, bn, NULL, NULL, ctx);
BN_CTX_free(ctx);
EC_GROUP_free(group);
BN_free(bn);
if (!EC_KEY_set_public_key(eckey, point))
return EXIT_FAILURE;
EC_POINT_free(point);
// Check the key
if (!EC_KEY_check_key(eckey))
return EXIT_FAILURE;
// Convert key to DER format
int len = i2d_ECPrivateKey(eckey, NULL);
unsigned char derkey[len];
unsigned char * derkeyPtr = derkey;
i2d_ECPrivateKey(eckey, &derkeyPtr);
// Freeing the EC_KEY here crashes for some reason???
// Encode DER key as hex
char out[len*2+1];
CBBytesToString(derkey, 0, len, out, false);
// Print to stdout
puts(out);
return EXIT_SUCCESS;
}
Here's how OpenSSL uses it in <openssl src/crypto/ec/ec_ameth.c. All the other similar uses in the library utilize i2d_ECPrivateKey_bio. Also, you might take a quick look at how the OPENSSL_EC_NAMED_CURVE flag is used with V_ASN1_OBJECT.
unsigned char *ep, *p;
int eplen, ptype;
unsigned int tmp_flags, old_flags;
...
old_flags = EC_KEY_get_enc_flags(ec_key);
tmp_flags = old_flags | EC_PKEY_NO_PARAMETERS;
...
eplen = i2d_ECPrivateKey(ec_key, NULL);
if (!eplen)
{
EC_KEY_set_enc_flags(ec_key, old_flags);
ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
return 0;
}
ep = (unsigned char *) OPENSSL_malloc(eplen);
if (!ep)
{
EC_KEY_set_enc_flags(ec_key, old_flags);
ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
return 0;
}
p = ep;
if (!i2d_ECPrivateKey(ec_key, &p))
{
EC_KEY_set_enc_flags(ec_key, old_flags);
OPENSSL_free(ep);
ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
return 0;
}
...
Also take a look at Avoid a NULL dereference in i2d_ECPrivateKey() when an EC_KEY lacks the public key member from OpenBSD's LibReSSL project.
We wanted to find an OpenSSl method in C that gives us only the modulus from the public key using RSA. However we are not sure which method to use. Does d2i_RSAPublicKey function work, but we are not sure what arguments it takes in? '
What is the c method for the following open ssl command that only extracts the modulus:
$ openssl ssl rsa -inform der -pubin -text < 12120862.key
Public-Key: (1024 bit)
Modulus:
00:81:1f:1d:00:7e:d0:c7:e2:2f:31:3d:0d:f0:a8:
ab:c1:ea:66:ba:af:1d:a4:eb:b3:fd:51:58:1c:1d:
81:ae:f0:99:9e:5c:26:67:b5:41:14:28:79:c0:29:
e5:56:96:06:b7:4b:a0:c9:7f:41:46:9a:7e:85:10:
a0:91:ea:58:bd:78:78:6d:3c:07:2a:3d:61:f3:ed:
42:8b:1e:dc:6d:2d:21:41:7a:e8:15:51:0d:75:84:
be:20:8c:76:43:8b:4b:67:6b:49:09:e9:20:a1:11:
53:a0:d9:30:b1:c2:27:a6:09:e1:56:36:ed:7e:9b:
23:e2:df:5b:bd:c5:66:ca:c5
Exponent: 65537 (0x10001)
writing RSA key
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCBHx0AftDH4i8xPQ3wqKvB6ma6
rx2k67P9UVgcHYGu8JmeXCZntUEUKHnAKeVWlga3S6DJf0FGmn6FEKCR6li9eHht
PAcqPWHz7UKLHtxtLSFBeugVUQ11hL4gjHZDi0tna0kJ6SChEVOg2TCxwiemCeFW
Nu1+myPi31u9xWbKxQIDAQAB
-----END PUBLIC KEY-----
What is the c method for the following open ssl command that only extracts the modulus
Short answer... Given an RSA structure:
RSA* rsa = ...;
BIGNUM* n = rsa->n;
Then, use BN_print_fp:
BN_print_fp(stdout, n);
Or, use BN_bn2dec:
fprintf(stdout, "%s", BN_bn2dec(n));
Or, use ASN1_bn_print:
int ASN1_bn_print(BIO *bp, const char *number, const BIGNUM *num,
unsigned char *buf, int off)
ASN1_bn_print is from the long answer below, and it gives you the formatting you showed in your example.
Long answer... I believe the key is printed with RSA_print_fp, and it ultimately ends with calls to ASN1_bn_print for the various RSA parameters. Here's part of the trail:
$ grep -R RSA_print_fp *
crypto/rsa/rsa.h:int RSA_print_fp(FILE *fp, const RSA *r,int offset);
crypto/rsa/rsa_err.c:{ERR_FUNC(RSA_F_RSA_PRINT_FP), "RSA_print_fp"},
crypto/rsa/rsa_prn.c:int RSA_print_fp(FILE *fp, const RSA *x, int off)
...
Following RSA_print_fp:
int RSA_print_fp(FILE *fp, const RSA *x, int off)
{
BIO *b;
int ret;
if ((b=BIO_new(BIO_s_file())) == NULL)
{
RSAerr(RSA_F_RSA_PRINT_FP,ERR_R_BUF_LIB);
return(0);
}
BIO_set_fp(b,fp,BIO_NOCLOSE);
ret=RSA_print(b,x,off);
BIO_free(b);
return(ret);
}
And RSA_print:
int RSA_print(BIO *bp, const RSA *x, int off)
{
EVP_PKEY *pk;
int ret;
pk = EVP_PKEY_new();
if (!pk || !EVP_PKEY_set1_RSA(pk, (RSA *)x))
return 0;
ret = EVP_PKEY_print_private(bp, pk, off, NULL);
EVP_PKEY_free(pk);
return ret;
}
EVP_PKEY_print_private is in crypto/evp/p_lib.c:
int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey,
int indent, ASN1_PCTX *pctx)
{
if (pkey->ameth && pkey->ameth->priv_print)
return pkey->ameth->priv_print(out, pkey, indent, pctx);
return unsup_alg(out, pkey, indent, "Private Key");
}
Once in the "methods", don't follow RSA_get_default_method. Rather, find priv_print:
$ grep -R priv_print * | grep -i RSA
crypto/rsa/rsa_ameth.c:static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
crypto/rsa/rsa_ameth.c: rsa_priv_print,
And rsa_priv_print:
static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
ASN1_PCTX *ctx)
{
return do_rsa_print(bp, pkey->pkey.rsa, indent, 1);
}
Next, do_rsa_print:
static int do_rsa_print(BIO *bp, const RSA *x, int off, int priv)
{
...
str = "Modulus:";
s = "Exponent:";
if (!ASN1_bn_print(bp,str,x->n,m,off)) goto err;
if (!ASN1_bn_print(bp,s,x->e,m,off)) goto err;
if (priv)
{
if (!ASN1_bn_print(bp,"privateExponent:",x->d,m,off))
goto err;
if (!ASN1_bn_print(bp,"prime1:",x->p,m,off))
goto err;
if (!ASN1_bn_print(bp,"prime2:",x->q,m,off))
goto err;
if (!ASN1_bn_print(bp,"exponent1:",x->dmp1,m,off))
goto err;
if (!ASN1_bn_print(bp,"exponent2:",x->dmq1,m,off))
goto err;
if (!ASN1_bn_print(bp,"coefficient:",x->iqmp,m,off))
goto err;
}
...
}
I'll leave the final ASN1_bn_print trace to the reader. It adds the colons (:) and line breaks (\n). You can find it in crypto/asn1/t_pkey.c.
Here's how you would use ASN1_bn_print:
RSA* rsa = RSA_new();
...
BIO* bio = BIO_new_fp(stdout, BIO_NOCLOSE);
...
int req = BN_num_bytes(rsa->n) + 4;
ptr = OPENSSL_malloc(req);
rc = ASN1_bn_print(bio, "Modulus:", rsa->n, ptr, 0);
ASSERT(rc == 1);
...
Running a program like above will produce:
$ ./test-openssl.exe
Modulus:
00:bb:bb:cf:ac:58:a9:25:2c:08:37:4d:4d:1d:0c:
5b:7d:a7:ba:de:7b:31:9a:5e:40:61:1f:6d:de:f9:
b4:48:15:a3:8c:2a:12:a9:10:fb:66:12:a4:3f:9c:
0d:7f:80:94:b1:63:91:05:96:f0:48:e5:7d:76:8a:
d0:26:dc:54:43
I was looking for a c function that returns the struct rsa by taking in a public key
For that, you probably need PEM_read_RSAPrivateKey and PEM_read_PUBKEY. There are some bio reads, too. See pem(3).
PEM_read_RSAPrivateKey returns an RSA*.
PEM_read_PUBKEY returns an EVP_PKEY*. If its an RSA key, then you can get the RSA* with EVP_PKEY_get1_RSA. The get1 signifies the reference count was bumped, so be sure to call RSA_free on it.
To determine if the EVP_PKEY* is an RSA key, then use EVP_PKEY_get_type. You will receive EVP_PKEY_RSA or EVP_PKEY_RSA2.