I'll explain all the steps I've done so far and conclude with my question.
using OpenSSL 1.0.1e-fips 11 Feb 2013
Generating a private and public key
openssl genrsa -des3 -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
signing a message with digest and private key
openssl dgst -sha256 -sign private.pem -out message.secret message.txt
at this point I have a public key, a signed message ( with digest ) and the original message.
Part 1 - using CLI ( this one works )
Using the CLI I manage to verify the digest:
openssl dgst -sha256 -verify public.pem -signature message.secret message.txt
I get "Verified OK" as a return value.
Part 2 - Using C program
My program looks like this:
where:
msg is message.txt
signature is message.secret
pkey is the public key ( achieved using PEM_read_PUBKEY )
int verify_it(const byte* msg, size_t msg_len, byte* signature, EVP_PKEY* pkey) {
EVP_MD_CTX *ctx;
size_t sig_len;
int bool_ret;
int ret_val;
ret_val = EXIT_SUCCESS;
ctx = NULL;
do
{
ctx = EVP_MD_CTX_create();
const EVP_MD* md = EVP_get_digestbyname( "SHA256" );
EVP_DigestInit_ex( ctx, md, NULL );
EVP_DigestVerifyInit( ctx, NULL, md, NULL, pkey );
EVP_DigestVerifyUpdate(ctx, msg, msg_len);
sig_len = 256;
if ( !EVP_DigestVerifyFinal( ctx, signature, sig_len )
ERR_print_errors_fp( stdout )
);
} while(0);
return ret_val;
}
this code returns a verification failure ( value of 0 ).
also the function ERR_print_errors_fp( stdout ) prints the following message:
140332412258152:error:04091077:lib(4):func(145):reason(119):rsa_sign.c:176
EDIT
thanks to #deniss I've managed to fix this one problem ( signature length was bad - probably a '\0' mid signature - so i just edit the length to be 256 )
but now I'm getting another problem -
140195987986280:error:04091068:lib(4):func(145):reason(104):rsa_sign.c:293
checking it with openssl errstr I've got
error:04091068:rsa routines:INT_RSA_VERIFY:bad signature
the way I acquire my signature is like this:
secret_fp = fopen( "message.secret", "rb" );
fseek( secret_fp, 0, SEEK_END );
file_len = ftell( secret_fp );
fseek( secret_fp, 0, SEEK_SET );
signature = malloc( file_len );
fread( signature, file_len, 1, secret_fp );
You can always decode openssl error codes into meaningful messages with
openssl errstr <error-code>
Code 04091077 stands for error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length.
The most probable explanation:
message.secret is binary file
it has zero byte somewhere in the middle
strlen trims signature on this byte
Related
I'm developing an application based on OpenSSL version 0.9.8a API and I need to verify an RSA signature (4096 bit RSA key) using an RSA public key pubkey.
This is my code:
const EVP_MD* md = EVP_get_digestbyname("SHA512");
if (!md)
{
return false;
}
if(EVP_DigestInit_ex(ctx, md, NULL)<=0)
{
return false;
}
if(EVP_DigestVerifyInit( ctx, NULL, md, NULL, pubKey)<=0)
{
return false;
}
if (EVP_DigestUpdate(ctx, Msg, MsgLen) <= 0)
{
return false;
}
int res = EVP_DigestFinal_ex(ctx, MsgHash, &MsgHashLen);
The problem is that the EVP_DigestVerifyInit function is not defined in openssl0.9.8a.
Is there an other way to verify an RSA signature in the version that predate OpenSSL version 1.0.0?
Here are my inputs:
/* Text_message saved to "sample.txt" file (text ends with the last chacracter '\n') */
My secret message.\n
/* generated RSA public key */
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAl44E9YikPF9RzjAWxZXO
/GaJVCVdQOjp6pmSgL38WvS7glqMwQFwBzSqAiuvVkf3j9C+9Cgs2pypamfDBabC
CZX9+c4oE9zxW2wz0yT5dKL+lUuvIeKfDTMvttjHPtMn23S4ASqLZFAeJX0bCd9H
6l2/8KBjV+uinVlwvKGjXbE5ds2/8S2gei17WvdTb2ypvAUhdw7dc8IaSosG5lOa
EycLN6XKq0z8UX336PiuY0FzLa2B3FOOc6TuPpjkRuf73YKt/Xtm+78ztkfwHLUs
v+EX0TJpB8jE4DISiogNrceOPOnFipppxau2t0VjLivmYTnvSbJzIbOmutFGX8HV
17m3tUpATCdVDuMJ3ouCWOjIJSvw60gizOUHqmMBX+qG95mQ1h3QGP7SExYDKgfp
VpIEAVsnv+CvZTGOEIYcO9ROI2FM6fBgDxkHAdWFCkBVK6o5JxYmsHAXOfFVh+FD
K/pxCBuQINn5B6xanBJYmtpbtq7D0UcvVn6HP7w/9X/pvdU90+KMx1p5AZ6WFeNB
BWLp4whPnU6VEFo+UwXyrCWPIlq3vAz/TjxOXW0Plw+xtsoyw15C8+mS9/4x9XyV
O9v4vlvsRL46T/h4axVsbf2B5w4JC4VoNTuomObecQ0s3v+v54ZYJNsp/tLMa02n
+N9hImCjf+Y6HF8s6BKQ1B0CAwEAAQ==
-----END PUBLIC KEY-----
/* base64 encoded Signatue saved to "PKGC_sample_singed.enc64" file */
NIInzKblrnRZ3olbVUSJbYQhnn9VvDaadcsLNLAVRS/2rryzQr8FVo7ty4oQYqQT
KwRkwVakCxqs+Dg+wHCHQ+K6D6d43nGodC0kNgD1++vYeorUbpK7QNdMu+73g5pW
TiYCicKp6DfeYGWPLI0aYivQJqwNMxqrFd/CdNWhIap4f4fZb5WHcszqlsLCqtrr
qVLP3fXVlHzfiA6jqx2GVE5m43iXLB2H3DwXmjS8eX0veRTlcU5lOtoH/yAGRxmL
p3Mg/s3LSdiAL4Gki4nSORWVmsJ0IwtRttLAl5wYN2VAWz8n21wjCTcr9f2k7BQy
/W74zug+8rbnejFoS50YOKB27PKUTcv0Qn5tWYrk/BK4FPovXyj+tPHZ+jIvAdvX
7UbGl1fkyylb2C6DUxlPPZRxjiaoA4xKwaSFrwcMQnVFphE/LuyQ5aBQZVtAI8Sj
K4zws2BjbCUh+JWzJawCZmIt8IEq1IavW/t/YaMIqR4IMMnHghZGhN8Hm34UOIwB
uyTx1bX/AlKIs7h2ibKSL5JKunQ2IbLqvPeyhjLoFdqiv57JsIzA154xFyl1uXlj
Y32kmtn2LO8SsJcJmDFaD63sj3+WbTR+BRw0QZf9lE94Fa/MPooF0I49aNIx59Wa
2uRR9r0oBgMkpNH+kNmCXcRdPyWIbILGZShkKMcp0Oo=
/* The following steps describe openssl command line used to sign then verify the signature */
/* ***** Step 1: sign the message ***** */
/* Sing the message using the RSA private key genrated before */
dgst -sha512 -sign '/PATH...private_key.pem' -out '/PATH.../PKGC_sample_signed.binr' '/PATH.../sample.txt'
/* Base64 Encoding the signature */
base64 -in '/PATH.../PKGC_sample_signed.binr' -out '/PATH.../PKGC_sample_singed.enc64'
/* ***** Step 2: signature verification ***** */
/* Base64 Decode the signature */
base64 -d -in '/PATH.../PKGC_sample_singed.enc64' -out '/PATH.../PKGC_sample_signed.decod64'
/* Verify the signature using the public key */
dgst -sha512 -verify '/PATH.../public_key.pem' -signature '/PATH.../PKGC_sample_signed.decod64' '/PATH.../sample.txt'
==> I get Verify OK.
I generated a public/private keypair with openssl:
openssl genrsa -out private.pem 1024
openssl pkcs8 -topk8 -in private.pem -outform DER -out private.der -nocrypt
openssl rsa -in private.pem -pubout -outform DER -out public.der
Now after that I created test code with Python, that was encrypting and deciphering strings. I took the example straight from PyCrypto documentation, encrypting:
string_to_encrypt = str(raw_input("Enter a string to encrypt: "))
print "Encrypting: %s" % string_to_encrypt
key = RSA.importKey(open('./public.der').read())
cipher = PKCS1_OAEP.new(key)
ciphertext = cipher.encrypt(string_to_encrypt)
and decrypting:
dec_key = RSA.importKey(open('./private.der').read())
d_cipher = PKCS1_OAEP.new(dec_key)
dec_message = d_cipher.decrypt(ciphertext)
Now this works as expected, and next I wanted to try deciphering same using C. I transfer the data via socket to the C application.. but now I'm unable to get the message back to clear text, even though the deciphering is not throwing errors.
Before I try to decipher the text, I print out the data to the screen, and the bytes match on both ends. The receiving function looks like this:
char* decrypt_packet(char* encrypted_buffer, int size) {
FILE *keyfile = fopen("./private.pem", "r");
RSA *rsa_pri = PEM_read_RSAPrivateKey (keyfile, NULL, NULL, NULL);
int rsa_private_len = RSA_size(rsa_pri);
for(i; i < size;i++)
printf("%02x:",(unsigned char)encrypted_buffer[i]);
printf("\n");
char * decrypt = (char*)malloc(rsa_private_len+1);
memset(decrypt,0,rsa_private_len+1); //Zero the buffer for printing
int res = RSA_private_decrypt(rsa_private_len, (unsigned char*)encrypted_buffer, (unsigned char*)decrypt, rsa_pri , RSA_NO_PADDING);
if(res == -1) {
ERR_load_crypto_strings();
printf("ERROR: %s\n",ERR_error_string(ERR_get_error(),NULL));
}
printf("size decrypt: %i\n", res);
printf("decrypted: %s\n", decrypt);
....
The deciphering doesn't fail, but the output is garbage (here sending just a test string "hello world"):
received buffer:
82:9d:a7:f7:3c:d6:71:12:01:31:ba:c6:a2:90:94:90:fd:69:d3:fe:14:11:2f:af:a9:8a:25:99:55:d2:84:1f:dc:e3:5e:a9:be:7b:8a:ac:cd:38:76:a2:91:ec:24:da:06:c7:8d:67:c8:15:19:73:c8:57:ce:a5:87:f0:da:db:c2:6d:5b:55:a3:ba:7e:7d:ca:6b:02:23:fd:fe:cb:b4:04:53:e2:74:c3:91:77:ee:5f:7a:61:7a:87:a6:42:37:28:c6:9c:cb:6a:46:f4:c0:bd:fe:8a:92:da:86:53:3b:5c:e2:e3:79:81:2c:32:28:9c:4c:be:0a:fa:75:7b:b2:
size decrypt: 128
decrypted: dÕf`5åiõuy<òáµÛ~G=/
Ä
I have chosen to use RSA_NO_PADDING? I have no idea really if this is correct.
But if I use something else, the decrypt function complains: RSA_padding_check_PKCS1_type_2:block type is not 02 or RSA_padding_check_PKCS1_OAEP:oaep decoding error
Am I correctly calling the RSA_private_decrypt function ? Could the problem be that I'm reading the private.pem file in C (in Python I was reading the .der file)?
One more thing came to mind. I used default openssl from Ubuntu to generate the keys, but my C application I'm linking against a downloaded and compiled source. The Makefile contains:
SOURCE_FILES = main.c client_handler.c
CC=gcc
$(CC) $(SOURCE_FILES) -o client_control_srv -lpthread -lssl -lcrypto -I/home/jlumme/openssl-1.0.1f_x86/include
Thanks for any tips!
Looks like my key generation was messed up.
I regenerated the keys:
openssl genrsa -out mykey_priv.pem 1024
openssl rsa -in mykey_priv.pem -out mykey_publ.der -outform DER -pubout
and now it works with the same code. As CBHacking pointed out, I also needed to have padding set to RSA_PKCS1_OAEP_PADDING.
I am trying to sign a email message using OpenSSL. I want to perform Clear Text Signing on the email message.
I know I can do this using command prompt with the command:
openssl smime -sign -in hw.txt -text -out signed.msg -signer mycert.pem
But how do I do this using C Code?
This is what I am doing right now:
int flgs = PKCS7_DETACHED | PKCS7_TEXT;
PKCS7 * p7 = PKCS7_sign(signCert, pKey, certs, bio, flgs);
if(p7 != NULL) {
int res = PKCS7_final(p7, bio, flags);
__android_log_print(ANDROID_LOG_DEBUG, "OpenSSLJni", "\nFinal result is: %d", res);
FILE *outfile = fopen("/storage/sdcard0/sign_output.txt", "w");
BIO* out = BIO_new_fp(outfile, BIO_NOCLOSE) ;// BIO_new_file(outfile, "");
BIO_reset(bio);
SMIME_write_PKCS7(out, p7, bio, flags);
return true;
}
But I in the output file I am only getting the signature not both the body and signature.
Any pointers?
Finally did it.
It was just a missing flag.
int flgs = PKCS7_STREAM | PKCS7_DETACHED | PKCS7_BINARY ;
Added this flag and it started working perfectly.
I was doing some experiments with cryptography. Now I have the public key of receiver and i want to encrypt some data and pass to the receiver.
I want to use RSAES-OAEP algorithm. with SHA-256 as hash function and MGF1 as mask generation function.
I want do this using openssl. I found a function RSA_public_encrypt() with this function we can specify the padding. One of the padding option available was
RSA_PKCS1_OAEP_PADDING
EME-OAEP as defined in PKCS #1 v2.0 with SHA-1 , MGF1 .
they are using sha-1.
I want to reconfigure the function to use SHA256 as hash function ans MGF1 as hash function. How can I do it ?
The following excerpt allows using OAEP with SHA256 for both the MGF and hash function. Tested with OpenSSL 1.0.2L
int flags = CMS_BINARY | CMS_PARTIAL | CMS_KEY_PARAM;
cms = CMS_encrypt(NULL, in, cipher, flags)
ri = CMS_add1_recipient_cert(cms, cert, flags);
pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING);
EVP_PKEY_CTX_set_rsa_oaep_md(pctx, EVP_sha256());
EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, EVP_sha256());
With a newer OpenSSL 1.0.2+ you can do it using the command:
openssl pkeyutl -in PlaintextKeyMaterial.bin -out EncryptedKeyMaterial.bin -inkey PublicKey.bin -keyform DER -pubin -encrypt -pkeyopt rsa_padding_mode:oaep
-pkeyopt rsa_oaep_md:sha256
This is taken from AWS KMS doc here: https://aws.amazon.com/es/premiumsupport/knowledge-center/invalidciphertext-kms/
OpenSSL uses definitions from PKCS #1 v2.0 and so the default for EME-OAEP is SHA-1 and MGF1. If you need to use SHA-256, you'll need to do the encoding yourself. This isn't terribly difficult however, see PKCS #1 v2.2 PDF Page 18 for details.
In the latest version of Openssl(1.0.2k) the signature of the API is changed which gives us more flexibility. Please find the details below,
int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
const unsigned char *from, int flen,
int num, const unsigned char *param,
int plen, const EVP_MD *md,
const EVP_MD *mgf1md)
You can pass the EVP_MD structure to invoke the SHA-256 hashing using this.
The PKCS#1 doc referred to above is more mathematical and doesn't give coding examples, and the CMS answer is for ASN.1/SMIME data and not really relevant for the question asked, which was how to replace RSA_public_encrypt() which deals with simple binary/text data. I spent a whole day with trial and error and online searching to find the answer to this, and eventually got the answer (which was to use OpenSSL's EVP API) from looking at the source code of "openssl pkeyutl" - once I discovered that it was not difficult.
In my case I was looking to replace RSA_private_decrypt() for decryption using the private key and this is how to do that - it should be pretty easy to put to together an RSA_public_encrypt() replacement based on this:
EVP_PKEY *privKey = NULL;
BIO *bioPrivKey;
int outLen = 0, ret;
if ((bioPrivKey = BIO_new(BIO_s_mem())))
{
// Read the private key from the RSA context into the memory BIO,
// then convert it to an EVP_PKEY:
if ((ret = PEM_write_bio_RSAPrivateKey(bioPrivKey, rsaCtxt, NULL, NULL, 0, NULL, NULL)) &&
(privKey = PEM_read_bio_PrivateKey(bioPrivKey, NULL, NULL, NULL)))
{
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(privKey, NULL);
EVP_PKEY_free(privKey);
if (ctx)
{
if (EVP_PKEY_decrypt_init(ctx) > 0)
{
EVP_PKEY_CTX_ctrl_str(ctx, "rsa_padding_mode", "oaep");
EVP_PKEY_CTX_ctrl_str(ctx, "rsa_oaep_md", "sha256");
EVP_PKEY_CTX_ctrl_str(ctx, "rsa_mgf1_md", "sha256");
outLen = dataOutMax;
ret = EVP_PKEY_decrypt(ctx, dataOut, &outLen, dataIn, inDataLen);
if (ret > 0 && outLen > 0 && outLen <= dataOutMax)
{
// Success :-)
}
}
EVP_PKEY_CTX_free(ctx);
}
}
BIO_free_all(bioPrivKey);
}
You can add error handling for the failure cases using ERR_get_error().
Can anybody tell me how to use
PKCS12 *d2i_PKCS12_fp(FILE *fp, PKCS12 **p12);
int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca);
any documenatation reference will also work.
Without error-checking:
FILE *p12_file;
PKCS12 *p12_cert = NULL;
EVP_PKEY *pkey;
X509 *x509_cert;
STACK_OF(X509) *additional_certs = NULL;
p12_file = fopen("foo.p12", "rb");
d2i_PKCS12_fp(p12_file, &p12_cert);
fclose(p12_file);
PKCS12_parse(p12_cert, "password", &pkey, &x509_cert, &additional_certs);
The private key is now in pkey, the certificate in x509_cert and any additional certificates in additional_certs.
Here is openssl's page for parse: PKCS12_parse.html
Here is Apple's link to using openssl libs: see PKCS#12, Section 2: I/O
From Apple's site, here are the descriptions:
int PKCS12_parse(PKCS12 *p12, char *pass, EVP_PKEY **pkey, X509 **cert,
STACK **ca);
This function takes a PKCS12 structure and a password (ASCII, null terminated)
and returns the private key, the corresponding certificate and any CA
certificates. If any of these is not required it can be passed as a NULL.
The 'ca' parameter should be either NULL, a pointer to NULL or a valid STACK
structure. Typically to read in a PKCS#12 file you might do:
p12 = d2i_PKCS12_fp(fp, NULL);
PKCS12_parse(p12, password, &pkey, &cert, NULL); /* CAs not wanted */
PKCS12_free(p12);