OPENSSL 1.1.1d signature verification faills, RSASSA-PSS signature scheme - c

I am trying to generate an RSASSA-PSS signature with openssl 1.1.1d.
The code below does generate some signature, different each time I run it, as expected for PSS scheme.
However, whenever I try to verify the signature online, it fails (I am using this site to do the verification: https://8gwifi.org/RSAFunctionality?rsasignverifyfunctions=rsasignverifyfunctions&keysize=2048).
Any expert here who could see if anything is missing in this source code?
int main()
{
EVP_MD_CTX *mdctx = NULL;
int ret = 0;
unsigned char *sig;
EVP_PKEY *key = NULL;
EVP_PKEY_CTX *ctx = NULL;
char *msg = "This is a nice message to be signed.";
int len;
size_t siglen;
FILE *fp;
char sig64[512];
unsigned char md[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, msg, strlen(msg));
SHA256_Final(md, &sha256);
sig = NULL;
len = strlen(msg);
fp = fopen("MYPRIVATEKEY.KEY", "r");
key = EVP_PKEY_new();
PEM_read_PrivateKey(fp, &key, NULL, NULL);
ctx = EVP_PKEY_CTX_new(key, NULL);
if (EVP_PKEY_sign_init(ctx) <= 0)
goto err;
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0)
goto err;
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0)
goto err;
/* Determine buffer length */
if (EVP_PKEY_sign(ctx, NULL, &siglen, md, SHA256_DIGEST_LENGTH) <= 0)
goto err;
sig = OPENSSL_malloc(siglen);
if (!sig)
goto err;
if (EVP_PKEY_sign(ctx, sig, &siglen, md, SHA256_DIGEST_LENGTH) <= 0)
goto err;
/* Signature is siglen bytes written to buffer sig */
/* Success */
ret = 1;
Encode_B64(sig, siglen, sig64);
printf("Signature: %s\n", sig64);
err:
if(ret != 1)
{
/* Do some error handling */
}
/* Clean up */
if(mdctx) EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(key);
EVP_PKEY_CTX_free(ctx);
if(sig && !ret) OPENSSL_free(sig);
return 0;
}

The PSS parameters must match on both sides for successful verification:
the PSS digest: this is set to SHA256 in the C code with EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()).
the MGF1 digest: This corresponds to the PSS digest by default, so it is also SHA256 here. If a different MGF1 digest is to be set, this is possible with EVP_PKEY_CTX_set_rsa_mgf1_md().
the salt length: this is set by OpenSSL by default equal to the maximum salt length (modulus - output length of the digest - 2, in bytes), which thus also applies here. The salt length can be set explicitly with EVP_PKEY_CTX_set_rsa_pss_saltlen() with the values RSA_PSS_SALTLEN_MAX (maximum salt length, default) and RSA_PSS_SALTLEN_DIGEST (output length of the PSS digest).
For completeness: Actually, more generally, the mask generation function must be specified, but by default MGF1 is applied, so that only the digest used by this function must be set (see point 2). In addition there is the trailer field number, which is not critical here, since both sides take the usual value of 1 by default.
The website, in contrast, uses different values for the salt length and the two digests:
RSASSA-PSS and SHA1WithRSA/PSS: SHA1 for the PSS and the MGF1 digest, salt length = output length of the digest (20 bytes)
SHA224WithRSA/PSS: SHA224 for the PSS and MGF1 digest, salt length = output length of the digest (28 bytes)
SHA384WithRSA/PSS: SHA384 for the PSS and the MGF1 digest, salt length = output length of the digest (48 bytes)
None of the PSS settings use SHA256 or the maximum salt length.
Due to the different PSS parameters, the verification fails.
However, verification is possible e.g. with the OpenSSL command line tool (e.g. with openssl pkeyutl):
openssl pkeyutl -verify -in <message hash file> -inkey <public key file> -sigfile <signature file> -pubin -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha256
The switches -pkeyopt rsa_mgf1_md:sha256 -pkeyopt rsa_pss_saltlen:max are set by default in the above statement.
The key file contains the PEM encoded X.509/SPKI key, the other two files contain the raw (i.e. not Base64 encoded) data of message hash and signature.

Related

OpenSSL EVP_aes_128_cbc decryption got unexpected result

I encrypted a string with EVP_aes_128_cbc cipher, then changed the 1st byte of the ciphertext, and decrypt this changed ciphertext. Unexpectly, it didn't decrypt error or get a fully wrong result, but got a wrong 1st 16 bytes and same string of rest. Here is the encrypt func:
int do_crypt1(unsigned char *in, unsigned char *outbuf, int inlen, int do_encrypt)
{
unsigned char inbuf[1024];
int outlen;
EVP_CIPHER_CTX *ctx;
/*
* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[32] = "test";
unsigned char iv[] = "1234567812345678";
/* Don't set key or IV right away; we want to check lengths */
ctx = EVP_CIPHER_CTX_new();
EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL,
do_encrypt);
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
/* Now we can set key and IV */
EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt);
int update_len = 0;
for (;;) {
memcpy(inbuf, in, inlen);
if (inlen <= 0)
break;
if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
update_len += outlen;
if(inlen <= 1024)
break;
}
if (!EVP_CipherFinal_ex(ctx, outbuf+outlen, &outlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
EVP_CIPHER_CTX_free(ctx);
update_len += outlen;
return update_len;
}
and in the main:
unsigned char* b = (unsigned char*)malloc(1024);
unsigned char* hmac_code = (unsigned char*)malloc(1024);
int len = do_crypt1(value, hmac_code, strlen(value), AES_ENCRYPT);
printf("%s\n", value);
for (i = 0; i < len; i++)
printf("%x", *(hmac_code + i));
printf("\n");
printf("%d\n", len);
// printf("%s\n", hmac_code);
*hmac_code = 0x23;
len = do_crypt1(hmac_code, b, len, AES_DECRYPT);
printf("%d\n", len);
printf("%s\n", b);
free(hmac_code);
free(b);
result
Could anyone give me the reason and how to resolve this?
Actually as long as your plaintext is at least 17 bytes, the first 17 decrypted bytes should be wrong -- but since you're displaying the invalid decryption as text, some of the bytes may be invisible: in your example, the output is 2 chars shorter, so clearly 2 chars of the first 17 aren't visible. That's the expected result for damage in (at the beginning of) the first block of a 2-block (or more) CBC ciphertext for a 16-byte-block cipher like AES; see the diagram in wikipedia to understand why.
If you want detection of damage to the ciphertext, don't use CBC mode, or at least don't use it alone. Common/standard solutions nowadays are:
add authentication, such as HMAC. (Intriguingly your code already uses the variable name hmac_code even though you don't do anything even remotely related to HMAC.)
use an authenticated-encryption mode, like GCM, which effectively combines encryption and (some type of) MAC internally. Most authenticated modes today, including GCM, also support 'additional' or 'associated' data that is authenticated but not encrypted, and as a result are called AEAD, but you may or may not care about that.
use an error-propagating mode. These were popular decades ago, around the time of presidents Nixon, Ford, Carter, and Reagan, but are now mostly considered obsolete and are not directly supported by OpenSSL.
Also, BTW, your code is completely pants for values longer than 1024 bytes, which your test clearly doesn't exercise. First of all you don't need to break up such a value into chunks at all, but if for some reason you want to, the method you implemented is wrong.
Plus, the IV for CBC should be different and unpredictable every time; using a hardcoded value like this exposes you to two different classes of attacks. In general the advice you will get on Stacks actually related to security is 'don't roll you own'. Cryptographic code written by people who don't know what they're doing, even if/when it produces the correct output, is usually insecure. This is the major difference between crypto/security software and others; you can easily see if your editor or spreadsheet or database produces correct or incorrect output, and as long as the output is correct that's usually all you need, but you can't tell by looking at the output from an encryption/decryption program whether it is secure or not.

Problem by Encryption & Decryption of text with EVP_des_ofb(), openSSL , C

I need to encrypt and decrypt txt-file with DES-ofb (libcrypto) using OpenSSL library, the key and Init Vector is given in one bin.file(key+iv). But after the decryption via EVP_DecryptUpdate(), decrypted text and plain text are not similar at all.
So I read plain.txt 8 bytes and a 'keyandIV.bin' files. Than I took first 8 bytes from keyandIVbuffer as a KEY for DES and the rest as IV. So I have 8 bytes key and 8 bytes IV, added '\0' at the end of both (Do I need '\0' here ? Key length must be 64 or 56 bits?).
This is my code for ercryption with DES ofb:
printf("ENCRYPTION:\n");
int howmany = 0, final1;
const EVP_CIPHER *CIPHER_TYPE = EVP_des_ofb();
EVP_CIPHER_CTX *ctx_encrypt = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ctx_encrypt);
EVP_EncryptInit(ctx_encrypt, CIPHER_TYPE, keybuf1, ivbuf1);
if(!EVP_EncryptUpdate(ctx_encrypt, ciphertextbuf1, &howmany, plaintextbuf1, plainlength1))return -1;
if(!EVP_EncryptUpdate(ctx_encrypt, ciphertextbuf1, &howmany, plaintextbuf1, plainlength1)) return -1;
EVP_EncryptFinal_ex(ctx_encrypt, ciphertextbuf1 + howmany , &final1);
EVP_CIPHER_CTX_cleanup(ctx_encrypt);
Than I took the encrypted buffer und decrypt it so:
printf("DECRYPTION:\n");
int final2;
EVP_CIPHER_CTX *ctx_decrypt = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ctx_decrypt);
EVP_DecryptInit(ctx_decrypt, CIPHER_TYPE, keybuf1, ivbuf1);
if(!EVP_DecryptUpdate(ctx_decrypt, decryptedtext, &howmany, ciphertextbuf1, strlen(ciphertextbuf1))) return -1;
if(!EVP_EncryptFinal_ex(ctx_decrypt, decryptedtext + howmany, &final2)) return -1;
EVP_CIPHER_CTX_cleanup(ctx_decrypt);
I definetly have understanding problem with DES. Maybe I did something wrong by creatimg key and IV from one file.I have seen plenty of examples but I still don't understand what I did wrong in my program.
The decryption sequence is EVP_DecryptInit_ex(), EVP_DecryptUpdate() and EVP_DecryptFinal_ex(). This follows EVP_EncryptInit_ex(), EVP_EncryptUpdate() and EVP_EncryptFinal_ex(). In your code you are calling EVP_EncryptFinal_ex() to decrypt, so obviously that's not going to work. Also, if something went wrong during an operation an error code should have been printed out to stderr.

How to get the size of the entire X509 certificate

I am reading a .crt certificate which I generate using openSSL. I have the certificate in my C program in a X509 file. I would like to know the size of the whole certificate that I have just read. How can this be there? Is there a specific function that returns the size of the certificate?
Thanks
Best Regards
For sending certificate over the network, I recommend using DER format. The reason is that PEM is Base64-encoded DER plus some additional text (prefix/suffix).
To estimate the size, you actually need to encode the certificate (this is DER):
size_t get_length(X509 *cer)
{
int len;
len = i2d_X509(cer, NULL);
return len > 0 ? len : 0;
}
For PEM it is trickier:
unsigned char *data;
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio, cer);
len = BIO_get_mem_data(bio, &data);
// here - data is a pointer to encoded data, len - length of data.
BIO_free(bio); // free _after_ you no longer need data

Different ciphertexts when I used OpenSSL AES command line tools and OpenSSL AES APIs?

Why do i got different ciphertexts when i used openssl aes command tools and openssl AES apis ?
I have used three types of encryption:
Type a) openssl command line tool
Type b) classes in javax.cryto
Type c) OpenSSL C api.
Using type (a) and (b), I got the same ciphertext. But I got different ciphertext when using (c).
I want to get the same ciphertexts when using method c and method a/b.
I think there's something wrong in type c, but I can't find it. Note that I used the same KEY,IV pair in the above three methods.
Type a:
openssl enc -aes-128-cbc -e -a -in pt.txt -out ct.txt -K 01010101010101010101010101010101 -iv 01010101010101010101010101010101 -p
Type b:
Java code using javax.crypto. I won't paste the code, because this way I got the same ciphertext with Type a.
Type c:
C code using OpenSSL API:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>
int main(int argc, char** argv) {
AES_KEY aes;
unsigned char key[AES_BLOCK_SIZE]; // AES_BLOCK_SIZE = 16
unsigned char iv[AES_BLOCK_SIZE]; // init vector
unsigned char* input_string;
unsigned char* encrypt_string;
unsigned char* decrypt_string;
unsigned int len; // encrypt length (in multiple of AES_BLOCK_SIZE)
unsigned int i;
// check usage
if (argc != 2) {
fprintf(stderr, "%s <plain text>\n", argv[0]);
exit(-1);
}
// set the encryption length
len = 0;
if ( strlen(argv[1])>=AES_BLOCK_SIZE ||
(strlen(argv[1]) + 1) % AES_BLOCK_SIZE == 0) {
len = strlen(argv[1]) + 1;
} else {
len = ((strlen(argv[1]) + 1) / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
}
// set the input string
input_string = (unsigned char*)calloc(len, sizeof(unsigned char));
if (input_string == NULL) {
fprintf(stderr, "Unable to allocate memory for input_string\n");
exit(-1);
}
strncpy((char*)input_string, argv[1], strlen(argv[1]));
// Generate AES 128-bit key
memset(key, 0x01, AES_BLOCK_SIZE);
// Set encryption key
memset(iv, 0x01, AES_BLOCK_SIZE);
if (AES_set_encrypt_key(key, 128, &aes) < 0) {
fprintf(stderr, "Unable to set encryption key in AES\n");
exit(-1);
}
// alloc encrypt_string
encrypt_string = (unsigned char*)calloc(len, sizeof(unsigned char));
if (encrypt_string == NULL) {
fprintf(stderr, "Unable to allocate memory for encrypt_string\n");
exit(-1);
}
// encrypt (iv will change)
AES_cbc_encrypt(input_string, encrypt_string, len, &aes, iv, AES_ENCRYPT);
/////////////////////////////////////
// alloc decrypt_string
decrypt_string = (unsigned char*)calloc(len, sizeof(unsigned char));
if (decrypt_string == NULL) {
fprintf(stderr, "Unable to allocate memory for decrypt_string\n");
exit(-1);
}
// Set decryption key
memset(iv, 0x01, AES_BLOCK_SIZE);
if (AES_set_decrypt_key(key, 128, &aes) < 0) {
fprintf(stderr, "Unable to set decryption key in AES\n");
exit(-1);
}
// decrypt
AES_cbc_encrypt(encrypt_string, decrypt_string, len, &aes, iv,
AES_DECRYPT);
// print
printf("input_string =%s\n", input_string);
printf("encrypted string =");
for (i=0; i<len; ++i) {
printf("%u ", encrypt_string[i]);
}
printf("\n");
printf("decrypted string =%s\n", decrypt_string);
return 0;
}
What could be the reason for different outputs?
In your C code, you are essentially using zero-padding: You allocate a memory area filled by zeros (by calloc), and then copy the plain text into this area, leaving the zeros at the end intact.
The openssl enc uses different padding than your C code. The documentation for openssl enc says (emphasis by me):
All the block ciphers normally use PKCS#5 padding also known as standard
block padding: this allows a rudimentary integrity or password check to be performed.
However since the chance of random data passing the test is better than 1 in 256 it
isn't a very good test.
In addition, the openssl enc command uses a salt by default, which randomizes the ciphertext. The salt serves a similar purpose as a per-message Initialization Vector (IV). But you are using an explicit IV, so the salt is not randomizing the ciphertext.
The documentation for javax.crypto.Cipher (which I suppose you used) says:
A transformation is of the form:
"algorithm/mode/padding" or
"algorithm"
(in the latter case, provider-specific default values for the mode and padding
scheme are used). For example, the following is a valid transformation:
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
So if you simply are using AES or ARS/CBC without indicating the padding mode, it uses whatever it finds fitting, which in your case happened to be the same as what OpenSSL used (i.e. PKCS#5 padding).
To change your C program, you'll have to do the same padding yourself (essentially, it is filling the block with a number x of bytes, all of which have the same value as this number, while appending a whole block filled with 16 when the last block is already full) - or use the higher level EVP-functions, which should provide you with a way to specify the padding mode to the cipher.

C Libmcrypt cannot encrypt/decrypt successfully

I am working with libmcrypt in c and attempting to implement a simple test of encryption and decryption using rijndael-256 as the algorithm of choice. I have mirrored this test implementation pretty closely to the man pages examples with rijndael as opposed to their chosen algorithms. When compiled with the string gcc -o encryption_test main.c -lmcrypt, the following source code produces output similar to:
The encrypted message buffer contains j��A��8 �qj��%`��jh���=ZЁ�j
The original string was ��m"�C��D�����Y�G�v6��s��zh�
Obviously, the decryption part is failing, but as it is just a single function call it leads me to believe the encryption scheme is not behaving correctly as well. I have several questions for the libmcrypt gurus out there if you could point me in the right direction.
First, what is causing this code to produce this broken output?
Second, when dealing with mandatory fixed-sizes such as the key size and block-size, for example a 256-bit key does the function expect 32-bytes of key + a trailing null byte, 31-bytes of key + a trailing null byte, or 32-bytes of key with the 33rd byte being irrelevant? The same question holds true for block-size as well.
Lastly, one of the examples I noted used mhash to generate a hash of the key-text to supply to the encryption call, this is of course preferable but it was commented out and linking in mhash seems to fail. What is the accepted way of handling this type of key-conversion when working with libmcrypt? I have chosen to leave any such complexities out as to prevent further complicating already broken code, but I would like to incorporate this into the final design. Below is the source code in question:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mcrypt.h>
int main(int argc, char *argv[])
{
MCRYPT mfd;
char *key;
char *plaintext;
char *IV;
unsigned char *message, *buffered_message, *ptr;
int i, blocks, key_size = 32, block_size = 32;
message = "Test Message";
/** Buffer message for encryption */
blocks = (int) (strlen(message) / block_size) + 1;
buffered_message = calloc(1, (blocks * block_size));
key = calloc(1, key_size);
strcpy(key, "&*GHLKPK7G1SD4CF%6HJ0(IV#X6f0(PK");
mfd = mcrypt_module_open(MCRYPT_RIJNDAEL_256, NULL, "cbc", NULL);
if(mfd == MCRYPT_FAILED)
{
printf("Mcrypt module open failed.\n");
return 1;
}
/** Generate random IV */
srand(time(0));
IV = malloc(mcrypt_enc_get_iv_size(mfd));
for(i = 0; i < mcrypt_enc_get_iv_size(mfd); i++)
{
IV[i] = rand();
}
/** Initialize cipher with key and IV */
i = mcrypt_generic_init(mfd, key, key_size, IV);
if(i < 0)
{
mcrypt_perror(i);
return 1;
}
strncpy(buffered_message, message, strlen(message));
mcrypt_generic(mfd, buffered_message, block_size);
printf("The encrypted message buffer contains %s\n", buffered_message);
mdecrypt_generic(mfd, buffered_message, block_size);
printf("The original string was %s\n", buffered_message);
mcrypt_generic_deinit(mfd);
mcrypt_module_close(mfd);
return 0;
}
You need to re-initialize the descriptor mfd for decryption, you cannot use the same descriptor for both encryption and decryption.

Resources