Sunday, April 25, 2010

RSA-PSS signing with OpenSSL and Bouncycastle

Verified:
Bouncycastle RSA-PSS works with OpenSSL RSA-PSS (if you can find out how to do it ;-)) which also accept the real test vectors provided by RSA.

Well done those folks.


How:

Code to use:
Use the Bouncycastle signing example code, it's super simple.

OpenSSL: Use this

/*
* An implementation of RSA PSS digital signature using OpenSSL
*
* Copyright (c) 2009 Mounir IDRASSI . All rights reserved.
* Modified 2010 by Blitter Nasty to interoperate with Bouncycastle properly.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
* Blitternasty's "With with Bouncycastle, dammit!" Mods are (from Mounir's code)...
* Use Sha1 not Sha256.
* Use fixed Salt length of 20 when signing (not -2 = "maximum")
*
* With these changes the sigs generated are accepted by Bouncycastle Java library. Nice.
*
*/

#include <stdio.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>


int main(int argc, char** argv)
{
RSA* pRsaKey = NULL;
unsigned char pDigest[32];
size_t uDigestLen = 32;
const char* szMessage = "This is the string to be signed";
EVP_MD_CTX md_ctx;
unsigned char EM[128];
unsigned char pSignature[128];
unsigned char pDecrypted[128];
int status = 0;

/* openssl initialization */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();

#ifdef _WIN32
RAND_screen();
#else
RAND_poll();
#endif

/* Generate an RSA key pair */
pRsaKey = RSA_generate_key(1024, 0x010001, NULL, NULL);
if (!pRsaKey)
{
printf("RSA_generate_key failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
goto prog_end;
}

/* hash the message */
EVP_MD_CTX_init(&md_ctx);
EVP_DigestInit(&md_ctx, EVP_sha256());
EVP_DigestUpdate(&md_ctx, (const void*) szMessage, strlen(szMessage));
EVP_DigestFinal(&md_ctx, pDigest, &uDigestLen);
EVP_MD_CTX_cleanup(&md_ctx);

/* compute the PSS padded data */
status = RSA_padding_add_PKCS1_PSS(pRsaKey, EM, pDigest, EVP_sha1(), 20 /* fixed salt length! (-2 didn't work for me)*/);
if (!status)
{
printf("RSA_padding_add_PKCS1_PSS failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
goto prog_end;
}

/* perform digital signature */
status = RSA_private_encrypt(128, EM, pSignature, pRsaKey, RSA_NO_PADDING);
if (status == -1)
{
printf("RSA_private_encrypt failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
goto prog_end;
}



// At this point you have a working RSA-PSS signature that works with Bouncycastle RSA-PSS validation code.
// The sig data is 128 bytes always with this code.


/* now we will verify the signature
Start by a RAW decrypt of the signature
*/
status = RSA_public_decrypt(128, pSignature, pDecrypted, pRsaKey, RSA_NO_PADDING);
if (status == -1)
{
printf("RSA_public_decrypt failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
goto prog_end;
}

/* verify the data */
status = RSA_verify_PKCS1_PSS(pRsaKey, pDigest, EVP_sha1(), pDecrypted, -2 /* salt length recovered from signature*/);
if (status == 1)
{
printf("Signature verification successfull!\n");
}
else
{
printf("RSA_verify_PKCS1_PSS failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
goto prog_end;
}

prog_end:
if (pRsaKey)
RSA_free(pRsaKey);

/* openssl cleanup */
CRYPTO_cleanup_all_ex_data();
RAND_cleanup();
EVP_cleanup();
ERR_free_strings();
ERR_remove_state(0);

return 0;
}