I'm trying to create a hybrid cryptography tool in C++ with Qt Gui.
(The data will be encrypted with AES 256-CBC, the AES Key RSA encrypted and saved then.)
But the RSA part of this tool doesn't work.
I wrote the sourcecode several times but I always get the same error on decrypt.
error:FFFFFFFFFFFFFFFF:lib(255):func(4095):reason(4095)
I hope someone could help me get a working RSA encrypt + decrypt implementation.
You can see the sourcecode here or download a testing Qt Project from my dropbox..
Dropbox Download: https://db.tt/6HKsYRTa
Sourcecode 1. Implementation:
void MainWindow::rsaEncrypt()
{
EVP_PKEY *pk = NULL;
EVP_PKEY_CTX *ctx = NULL;
QByteArray encrypted = QByteArray();
//------------------------------------------------
//--- READ PUBLIC KEY ----------------------------
FILE *pkFile = fopen(ui->publicKeyPath->text().toStdString().c_str(), "r");
if(pkFile == NULL) throw NULL;
pk = PEM_read_PUBKEY(pkFile, NULL, NULL, NULL);
if(pk == NULL) throw NULL;
fclose(pkFile);
//------------------------------------------------
ctx = EVP_PKEY_CTX_new(pk, NULL);
//------------------------------------------------
//--- ENCRYPT DATA -------------------------------
int err;
err = EVP_PKEY_encrypt_init(ctx);
if(err <= 0) throw NULL;
err = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PADDING);
if(err <= 0) throw NULL;
size_t outLen = 0;
err = EVP_PKEY_encrypt(
ctx,
NULL,
&outLen,
(uchar*) ui->plainTextEdit->document()->toPlainText().toStdString().c_str(),
ui->plainTextEdit->document()->toPlainText().size()
);
if(err <= 0) throw NULL;
encrypted.resize(outLen);
err = EVP_PKEY_encrypt(
ctx,
(uchar*) encrypted.data(),
&outLen,
(uchar*) ui->plainTextEdit->document()->toPlainText().toStdString().c_str(),
ui->plainTextEdit->document()->toPlainText().size()
);
//------------------------------------------------
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pk);
if(err > 0) ui->encryptedTextEdit->document()->setPlainText(QString(encrypted));
else {
QByteArray errStr = QByteArray();
errStr.resize(256);
ERR_load_ERR_strings();
ERR_error_string(err, errStr.data());
ui->encryptedTextEdit->document()->setPlainText( QString(errStr) );
}
}
void MainWindow::rsaDecrypt()
{
EVP_PKEY *pk = NULL;
EVP_PKEY_CTX *ctx = NULL;
QByteArray decrypted = QByteArray();
//------------------------------------------------
//--- READ PRIVATE KEY ---------------------------
FILE *pkFile = fopen(ui->privateKeyPath->text().toStdString().c_str(), "r");
if(pkFile == NULL) throw NULL;
pk = PEM_read_PrivateKey(pkFile, NULL, NULL, NULL);
if(pk == NULL) throw NULL;
fclose(pkFile);
//------------------------------------------------
ctx = EVP_PKEY_CTX_new(pk, NULL);
//------------------------------------------------
//--- DECRYPT DATA -------------------------------
int err;
err = EVP_PKEY_decrypt_init(ctx);
if(err <= 0) throw NULL;
err = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PADDING);
if(err <= 0) throw NULL;
size_t outLen = 0;
err = EVP_PKEY_decrypt(
ctx,
NULL,
&outLen,
(uchar*) ui->encryptedTextEdit->document()->toPlainText().toStdString().c_str(),
ui->encryptedTextEdit->document()->toPlainText().size()
);
if(err <= 0) throw NULL;
decrypted.resize(outLen);
err = EVP_PKEY_decrypt(
ctx,
(uchar*) decrypted.data(),
&outLen,
(uchar*) ui->encryptedTextEdit->document()->toPlainText().toStdString().c_str(),
ui->encryptedTextEdit->document()->toPlainText().size()
);
//------------------------------------------------
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pk);
if(err > 0) ui->decryptedTextEdit->document()->setPlainText(QString(decrypted));
else {
QByteArray errStr = QByteArray();
errStr.resize(256);
ERR_load_ERR_strings();
ERR_error_string(err, errStr.data());
ui->decryptedTextEdit->document()->setPlainText( QString(errStr) );
}
}
Sourcecode 2. Implementation:
void MainWindow::rsaEncrypt()
{
RSA *rsa = createRSAFromFile(ui->publicKeyPath->text().toStdString().c_str(), 1);
QByteArray encrypted = QByteArray();
encrypted.resize(2048);
int err = RSA_public_encrypt(
ui->plainTextEdit->document()->toPlainText().size(),
(uchar*) ui->plainTextEdit->document()->toPlainText().toStdString().c_str(),
(uchar*) encrypted.data(),
rsa,
RSA_PADDING
);
RSA_free(rsa);
if(err > 0) ui->encryptedTextEdit->document()->setPlainText( QString(encrypted) );
else {
QByteArray errStr = QByteArray();
errStr.resize(256);
ERR_load_ERR_strings();
ERR_error_string(err, errStr.data());
ui->encryptedTextEdit->document()->setPlainText( QString(errStr) );
}
}
void MainWindow::rsaDecrypt()
{
RSA *rsa = createRSAFromFile(ui->privateKeyPath->text().toStdString().c_str(), 0);
QByteArray decrypted = QByteArray();
decrypted.resize(2048);
int err = RSA_private_decrypt(
ui->encryptedTextEdit->document()->toPlainText().size(),
(uchar*) ui->encryptedTextEdit->document()->toPlainText().toStdString().c_str(),
(uchar*) decrypted.data(),
rsa,
RSA_PADDING
);
RSA_free(rsa);
if(err > 0) ui->decryptedTextEdit->document()->setPlainText( QString(decrypted) );
else {
QByteArray errStr = QByteArray();
errStr.resize(256);
ERR_load_ERR_strings();
ERR_error_string(err, errStr.data());
ui->decryptedTextEdit->document()->setPlainText( QString(errStr) );
}
}
RSA *MainWindow::createRSAFromFile(const char *keyPath, int pub)
{
FILE *keyFile = fopen(keyPath, "rb");
if(keyFile==NULL)
{
return 0;
}
RSA *rsa = RSA_new();
if(pub)
{
rsa = PEM_read_RSA_PUBKEY(keyFile, &rsa, NULL, NULL);
}
else
{
rsa = PEM_read_RSAPrivateKey(keyFile, &rsa, NULL, NULL);
}
fclose(keyFile);
return rsa;
}
Includes and defines for both implementations:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QByteArray>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define RSA_PADDING RSA_PKCS1_OAEP_PADDING
Try to use this Code for creating a RSA-object. It definetly works. You should read the .pem file before and then call this function:
RSA *CryptClassRSA::createRSA(unsigned char *key, int isPublic){
RSA *rsa = NULL;
BIO *keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio==NULL){
printf( "Failed to create key BIO");
return NULL;
}
if(isPublic){
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,NULL, NULL);
}
else{
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
}
if(rsa == NULL){
printf( "Failed to create RSA");
}
return rsa;
}
C++ RSA decrypt error:FFFFFFFFFFFFFFFF:lib(255):func(4095):reason(4095)
And
int err;
...
err = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PADDING);
if(err <= 0) throw NULL;
...
Its not clear to me where the error is happening or how you are getting the errstr output of error:FFFFFFFFFFFFFFFF:lib(255):func(4095):reason(4095). I picked EVP_PKEY_CTX_set_rsa_padding as the snippet because I think the return value is -2 (and it has significance as explained below).
err is just a return code. To get the actual error, you need to call ERR_get_error. Maybe something like:
int rc;
unsigned long err;
...
rc = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PADDING);
err = ERR_get_error();
if(rc <= 0)
{
// err is valid
}
You should also visit the EVP_PKEY_CTX_set_rsa_padding and ERR_get_error man pages.
Often, OpenSSL will return 0 for success, so I'm not sure about err <= 0 in some places. Also, because your error is 0xffff...4095 (and not 4096), I think you are getting the -2 return value discussed in the man page:
EVP_PKEY_CTX_ctrl() and its macros return a positive value for success and 0 or a negative value for failure. In particular a return value of -2 indicates the operation is not supported by the public key algorithm.
Also note... If you gather your error and print it in hex:
rc = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PADDING);
err = ERR_get_error();
if(rc <= 0)
{
// err is valid
std::cerr << std::hex << err << std::endl;
}
Then you can use the openssl errstr 0xNNNN to print it.
Related
I wrote this code to encrypt a file in python with AES and now I send this file over HTTP to a client in C++ using Windows OS. And I want to decrypt this file on the C++ client, but I haven't found any libraries/usefull code to help me with this manner.
Here is the code for python:
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = b''
while len(d) < key_length + iv_length:
d_i = md5(d_i + str.encode(password) + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length + iv_length]
def encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size # 16 bytes
salt = urandom(bs)
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write(salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0: #final block
padding_length = (bs - len(chunk) % bs) or bs
chunk += str.encode(padding_length * chr(padding_length))
finished = True
out_file.write(cipher.encrypt(chunk))
password = 'ABCDE' # some password
with open("some_file.bin", "rb") as in_file, open("enc_some_file.enc", "wb") as out_file:
encrypt(in_file, out_file, password)
Update:
I am updating this question regarding my try with WinCrypt. I am trying to compile from VS CODE using MSYS64 g++ compiler on Windows. Here is the code that I am trying to run. I also have some additional libraries to help me with the client-server part, such as libcurlpp and libcurl.
#include <string>
#include <sstream>
#include <conio.h>
#include <iostream>
#include <fstream>
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <wincrypt.h>
#include <windows.h>
#define AES_KEY_SIZE 32
#define IN_CHUNK_SIZE (AES_KEY_SIZE * 10) // a buffer must be a multiple of the key size
#define OUT_CHUNK_SIZE (IN_CHUNK_SIZE * 2) // an output buffer (for encryption) must be twice as big
using namespace std;
string host = ""; #some host
int main(void)
{
curlpp::Easy req;
req.setOpt(new curlpp::options::Url(host));
ofstream myfile;
myfile.open("enc_calc.enc", ios::out | ios::binary);
myfile << req;
myfile.close();
getch();
wchar_t *in_file = (wchar_t *)"enc_calc.enc";
wchar_t *out_file = (wchar_t *)"calc.dll";
wchar_t default_key[] = L"ABCDE";
wchar_t *key_str = default_key;
const size_t len = lstrlenW(key_str);
const size_t key_size = len * sizeof(key_str[0]);
const wchar_t *filename1 = (const wchar_t *)"enc_calc.enc";
const wchar_t *filename2 = (const wchar_t *)"calc.dll";
HANDLE hInpFile = CreateFileW(filename1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hInpFile == INVALID_HANDLE_VALUE) {
printf("Cannot open input file!\n");
system("pause");
return (-1);
}
HANDLE hOutFile = CreateFileW(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOutFile == INVALID_HANDLE_VALUE) {
printf("Cannot open output file!\n");
system("pause");
return (-1);
}
DWORD dwStatus = 0;
BOOL bResult = FALSE;
wchar_t info[] = L"Microsoft Enhanced RSA and AES Cryptographic Provider";
HCRYPTPROV hProv;
if (!CryptAcquireContextW(&hProv, NULL, info, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
dwStatus = GetLastError();
printf("CryptAcquireContext failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
HCRYPTHASH hHash;
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
dwStatus = GetLastError();
printf("CryptCreateHash failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
if (!CryptHashData(hHash, (BYTE*)key_str, key_size, 0)) {
DWORD err = GetLastError();
printf("CryptHashData Failed : %#x\n", err);
system("pause");
return (-1);
}
printf("[+] CryptHashData Success\n");
HCRYPTKEY hKey;
if (!CryptDeriveKey(hProv, CALG_AES_128, hHash, 0, &hKey)) {
dwStatus = GetLastError();
printf("CryptDeriveKey failed: %x\n", dwStatus);
CryptReleaseContext(hProv, 0);
system("pause");
return dwStatus;
}
printf("[+] CryptDeriveKey Success\n");
const size_t chunk_size = IN_CHUNK_SIZE;
BYTE *chunk = new BYTE[chunk_size];
DWORD out_len = 0;
BOOL isFinal = FALSE;
DWORD readTotalSize = 0;
DWORD inputSize = GetFileSize(hInpFile, NULL);
while (bResult = ReadFile(hInpFile, chunk, IN_CHUNK_SIZE, &out_len, NULL)) {
if (0 == out_len) {
break;
}
readTotalSize += out_len;
if (readTotalSize >= inputSize) {
isFinal = TRUE;
printf("Final chunk set, len: %d = %x\n", out_len, out_len);
}
if (!CryptDecrypt(hKey, NULL, isFinal, 0, chunk, &out_len)) {
printf("[-] CryptDecrypt failed: %x\n", GetLastError());
break;
}
DWORD written = 0;
if (!WriteFile(hOutFile, chunk, out_len, &written, NULL)) {
printf("writing failed!\n");
break;
}
memset(chunk, 0, chunk_size);
}
delete[]chunk; chunk = NULL;
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
CryptReleaseContext(hProv, 0);
CloseHandle(hInpFile);
CloseHandle(hOutFile);
printf("Finished. Processed %#x bytes.\n", readTotalSize);
return 0;
}
The errors I'm getting are regarding the windows.h library, such as:
"ULONG does not name a type" and other types in windows. and at runtime: "Cannot Open input file", not sure why, even tho the file is downloaded in the current folder and I'm trying to open it as such in the code.
I have this function which if you connect to a system with ssh, you can call it to execute your given command on that system.
std::string sshconnection::exec_ssh_command(ssh_session session, char *command) {
string receive = "";
int rc, nbytes;
char buffer[256];
ssh_channel channel = ssh_channel_new(session);
if( channel == NULL )
return NULL;
rc = ssh_channel_open_session(channel);
if( rc != SSH_OK ) {
ssh_channel_free(channel);
return NULL;
}
rc = ssh_channel_request_exec(channel, command);
if( rc != SSH_OK ) {
ssh_channel_close(channel);
ssh_channel_free(channel);
cout << "Error";
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
if (write(1, buffer, nbytes) != (unsigned int) nbytes)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if( nbytes < 0 )
return NULL;
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return receive;
}
this function works great. I just don't understand that part which is about to write from buffer into a file descriptor=1 . we haven't filled receive anywhere but it is the return value. if we call this function like below:
s = exec_ssh_command(my_ssh_session, "cat /proc/stat" );
the s won't have any value, but if we do this:
std::cout<<s;
this will print s value. and of course we can't save s in a file. can someone explain to me how is this happening?
EDIT:function to connect to ssh:
int sshconnection::sshConnection()
{
if( my_ssh_session == NULL ) {
cout << "Error creating ssh session" << endl;
return 1;
}
ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "yourip");
ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, "username");
int rc = ssh_connect(my_ssh_session);
if( rc != SSH_OK ) {
cout << "Error with connecting" << endl;
ssh_free(my_ssh_session);
return -1;
}
rc = ssh_userauth_password(my_ssh_session, NULL, "yourpassword");
if( rc != SSH_AUTH_SUCCESS) {
cout << "Error with authorization " << ssh_get_error(my_ssh_session) << endl;
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return -1;
}
// ssh_disconnect(my_ssh_session);
// ssh_free(my_ssh_session);
}
I know this is old, but I had the same issue. I came up with the following solution.
Use std::string::append like so receive.append(buffer, nbytes).
std::string sshconnection::exec_ssh_command(ssh_session session, char *command) {
string receive = "";
int rc, nbytes;
char buffer[256];
ssh_channel channel = ssh_channel_new(session);
if( channel == NULL )
return NULL;
rc = ssh_channel_open_session(channel);
if( rc != SSH_OK ) {
ssh_channel_free(channel);
return NULL;
}
rc = ssh_channel_request_exec(channel, command);
if( rc != SSH_OK ) {
ssh_channel_close(channel);
ssh_channel_free(channel);
cout << "Error";
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
receive.append(buffer, nbytes);
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if( nbytes < 0 )
return NULL;
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return receive;
}
I am trying to generate RSA keypair using openssl library and then read the same keys later. However, it fails. Sometimes it gives me this error:
error:0906D06C:PEM routines:PEM_read_bio:no start line
And sometimes, it gives me this error:
error:0906D06C:lib(9):func(109):reason(108)
What is the correct way to generate the keypair and later be able to read it ? Here is my code. If you run it, you will find that it correctly generates the RSA key pair but not able read them later.
#include <stdio.h>
#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <exception>
bool generate_key() {
size_t pri_len; // Length of private key
size_t pub_len; // Length of public key
char *pri_key; // Private key in PEM
char *pub_key; // Public key in PEM
int ret = 0;
RSA *r = NULL;
BIGNUM *bne = NULL;
BIO *bp_public = NULL, *bp_private = NULL;
int bits = 2048;
unsigned long e = RSA_F4;
EVP_PKEY *evp_pbkey = NULL;
EVP_PKEY *evp_pkey = NULL;
BIO *pbkeybio = NULL;
BIO *pkeybio = NULL;
// 1. generate rsa key
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;
}
// 2. save public key
//bp_public = BIO_new_file("public.pem", "w+");
bp_public = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
if (ret != 1) {
goto free_all;
}
// 3. save private key
//bp_private = BIO_new_file("private.pem", "w+");
bp_private = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
//4. Get the keys are PEM formatted strings
pri_len = BIO_pending(bp_private);
pub_len = BIO_pending(bp_public);
pri_key = (char*) malloc(pri_len + 1);
pub_key = (char*) malloc(pub_len + 1);
BIO_read(bp_private, pri_key, pri_len);
BIO_read(bp_public, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
printf("\n%s\n%s\n", pri_key, pub_key);
//verify if you are able to re-construct the keys
pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
if (pbkeybio == NULL) {
return -1;
}
evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
if (evp_pbkey == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading public key:%s\n", buffer);
}
pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
if (pkeybio == NULL) {
return -1;
}
evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
if (evp_pbkey == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading private key:%s\n", buffer);
}
BIO_free(pbkeybio);
BIO_free(pkeybio);
// 4. free
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
return (ret == 1);
}
int main(int argc, char* argv[]) {
generate_key();
return 0;
}
Looks good to me. Except on reloading; I would have used PEM_read_bio_RSAPublicKey in stead of PEM_read_bio_PUBKEY.
I am not sure it is the best way to do it though.
--- /tmp/stack_openssl.cpp.back 2018-05-25 12:53:12.366488025 +0000
+++ /tmp/stack_openssl.cpp 2018-05-25 13:57:20.614066828 +0000
## -18,6 +18,8 ##
int bits = 2048;
unsigned long e = RSA_F4;
+ RSA *pb_rsa = NULL;
+ RSA *p_rsa = NULL;
EVP_PKEY *evp_pbkey = NULL;
EVP_PKEY *evp_pkey = NULL;
## -66,27 +68,32 ##
printf("\n%s\n%s\n", pri_key, pub_key);
//verify if you are able to re-construct the keys
- pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
+ pbkeybio = BIO_new_mem_buf((void*) pub_key, pub_len);
if (pbkeybio == NULL) {
return -1;
}
- evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
- if (evp_pbkey == NULL) {
+ pb_rsa = PEM_read_bio_RSAPublicKey(pbkeybio, &pb_rsa, NULL, NULL);
+ if (pb_rsa == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading public key:%s\n", buffer);
}
+ evp_pbkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(evp_pbkey, pb_rsa);
- pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
+ pkeybio = BIO_new_mem_buf((void*) pri_key, pri_len);
if (pkeybio == NULL) {
return -1;
}
- evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
- if (evp_pbkey == NULL) {
+ p_rsa = PEM_read_bio_RSAPrivateKey(pkeybio, &p_rsa, NULL, NULL);
+ if (p_rsa == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading private key:%s\n", buffer);
}
+ evp_pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(evp_pkey, p_rsa);
BIO_free(pbkeybio);
BIO_free(pkeybio);
How do I load the PEM format certificate as an x509 in openssl c++?
int SSL_use_certificate(SSL *ssl, X509 *x);
int SSL_use_certificate_ASN1(SSL *ssl, unsigned char *d, int len);
int SSL_use_certificate_file(SSL *ssl, const char *file, int type);
These are the 3 functions available to add a certificate to a Handle. I have a certificate string inside the program(This is just a PEM formatted data). I want to add it to the handle. How do I proceed?
Will SSL_CTX_set_default_passwd_cb work with private keys that I am loading into and ssl handle and not a context?
How do I load the PEM format certificate as an x509 in openssl c++?
You should probably use SSL_CTX_use_certificate_chain_file and SSL_CTX_use_PrivateKey_file. You can use them and build a client or server context. The sample code is shown below. There are some nuances to using them, so take a look at OpenSSL's documentation at SSL_CTX_use_certificate(3).
I'm not sure what "as an x509" means. The certificate will be x509, but the private key will be PKCS #8. There are PEM_read_bio_X509 and PEM_read_X509, they return an X509*, and they may do what you want.
Will SSL_CTX_set_default_passwd_cb work with private keys that I am loading
It depends, but it should. The password callback is optional. Use it if you password protected the key. In the code below, I call it PasswordCallback, and its used for both reading and writing the key (only reading is shown below).
using SSL_CTX_ptr = std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)>;
SSL_CTX* CreateServerContext()
{
do
{
int rc;
unsigned long err;
const SSL_METHOD* method = SSLv23_server_method();
ASSERT(method != NULL);
if (method == NULL)
{
LogError("GetServerContext: SSLv23_server_method failed");
break; /* failed */
}
SSL_CTX_ptr t(SSL_CTX_new(method), ::SSL_CTX_free);
ASSERT(t.get() != NULL);
if (t.get() == NULL)
{
LogError("GetServerContext: SSL_CTX_new failed");
break; /* failed */
}
long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
flags |= SSL_OP_NO_COMPRESSION;
flags |= SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;
/* Cannot fail */
SSL_CTX_set_options(t.get(), flags);
string ciphers = GetServerCipherSuites();
ASSERT(!ciphers.empty());
rc = SSL_CTX_set_cipher_list(t.get(), ciphers.c_str());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_set_cipher_list failed");
break; /* failed */
}
string certFile = config.GetServerCertFile();
ASSERT(!certFile.empty());
rc = SSL_CTX_use_certificate_chain_file(t.get(), certFile.c_str());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_use_certificate_chain_file failed");
break; /* failed */
}
/* These two do not return a value... cannot fail? */
SSL_CTX_set_default_passwd_cb(t.get(), PasswordCallback);
SSL_CTX_set_default_passwd_cb_userdata(t.get(), (void*) SERVER_KEY_LABEL);
string keyFile = config.GetServerKeyFile();
ASSERT(!keyFile.empty());
rc = SSL_CTX_use_PrivateKey_file(t.get(), keyFile.c_str(), SSL_FILETYPE_PEM);
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_use_PrivateKey_file failed");
break; /* failed */
}
rc = SSL_CTX_check_private_key(t.get());
err = ERR_get_error();
ASSERT(rc == 1);
if (rc != 1)
{
LogError("GetServerContext: SSL_CTX_check_private_key failed");
/* non-fatal, but everything will probably break */
}
/* These three do not return a value... cannot fail? */
SSL_CTX_set_tmp_dh_callback(t.get(), DhCallback);
SSL_CTX_set_tmp_ecdh_callback(t.get(), EcdhCallback);
SSL_CTX_set_tlsext_servername_callback(t.get(), ServerNameCallback);
return t.release();
} while (0);
return NULL;
}
The idea with the password callback is: OpenSSL provides you a buffer and a size. You fill the buffer, and return the size of how much you filled.
My password callback is somewhat involved. It performs a single hash of the raw password before passing it on to the library. That ensures a "plain text" password is not used (but does not slow down the customary attacks). Yours can prompt the user for a string, or it can return a hard coded string.
My password callback uses a label. The label allows me to derive different keys depending on usage (even though the same 'base' secret is used). By specifying a different usage or label, I get a different derivation of key bits. The label is provided through arg below, and you can set it with SSL_CTX_set_default_passwd_cb_userdata.
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;
int PasswordCallback(char *buffer, int size, int rwflag, void *arg)
{
UNUSED(rwflag);
int rc;
unsigned long err;
ostringstream oss;
const char* label = (char*) arg;
size_t lsize = (label ? strlen(label) : 0);
SecureVector sv = config.GetMasterKey();
AC_ASSERT(!sv.empty());
if (sv.empty())
{
...
throw runtime_error(oss.str().c_str());
}
EVP_MD_CTX_ptr ctx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
AC_ASSERT(ctx.get() != NULL);
const EVP_MD* hash = EVP_sha512();
AC_ASSERT(hash != NULL);
rc = EVP_DigestInit_ex(ctx.get(), hash, NULL);
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
rc = EVP_DigestUpdate(ctx.get(), sv.data(), sv.size());
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
if (label && lsize)
{
rc = EVP_DigestUpdate(ctx.get(), label, lsize);
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
}
int n = std::min(size, EVP_MD_size(hash));
if (n <= 0)
return 0;
rc = EVP_DigestFinal_ex(ctx.get(), (unsigned char*) buffer, (unsigned int*) &n);
err = ERR_get_error();
AC_ASSERT(rc == 1);
if (rc != 1)
{
...
throw runtime_error(oss.str().c_str());
}
return n;
}
I wrote this piece of code to decrypt a given cipher using a given key and iv. It works fine until CryptDecrypt which returns false and GetLastError() returns 0x80090005 (which should be NTE_BAD_DATA).
#include <Windows.h>
#include <wincrypt.h>
struct AesKey
{
BLOBHEADER Header;
DWORD dwKeyLength;
BYTE cbKey[16];
AesKey()
{
ZeroMemory(this, sizeof(*this));
Header.bType = PLAINTEXTKEYBLOB;
Header.bVersion = CUR_BLOB_VERSION;
Header.reserved = 0;
Header.aiKeyAlg = CALG_AES_128;
dwKeyLength = 16;
}
};
void AesDecrypt(unsigned char *output, unsigned char *input, int inLen, unsigned char *key, unsigned char *iv, int &plainSize)
{
HCRYPTPROV provider;
AesKey rawKey;
HCRYPTKEY cKey;
BOOL hr = CryptAcquireContext(&provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);
if (hr == FALSE)
throw "Unable to acquire AES Context";
for (int i = 0; i < 16; i++)
rawKey.cbKey[i] = key[i];
hr = CryptImportKey(provider, (BYTE *) &rawKey, sizeof(AesKey), NULL, 0, &cKey);
if (hr == FALSE)
throw "Unable to import given key";
hr = CryptSetKeyParam(cKey, KP_IV, (BYTE *) iv, 0);
if (hr == FALSE)
throw "Unable to set IV";
DWORD dwMode = CRYPT_MODE_CBC;
hr = CryptSetKeyParam(cKey, KP_MODE, (BYTE*) &dwMode, 0);
if (hr == FALSE)
throw "Unable to set mode";
memcpy(output, input, inLen);
DWORD d = (DWORD) inLen;
hr = CryptDecrypt(cKey, NULL, TRUE, 0, output, &d);
if (hr == FALSE)
{
int err = GetLastError();
throw "Error during Decryption";
}
plainSize = d;
}
If you have any idea, please share :)
Thanks in advance...