Related
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.
I'm working on decryption of encrypted MS Excel(RC4 encryption with SHA1),password is already known.In vs2010,I've could decrypt it correctly,however,my program hasto work under both Win and linux.And I have no idea to get the encryption key under linux right now,which is something like below under Win:
int getEncrypKey(HCRYPTKEY *hKey, int blocknum)
{
//------------------------H0 = H(salt, password)-----
BYTE *pbSaltandPwdHash = NULL;
DWORD dwSaltandPwdLen = 0;
pbSaltandPwdHash = SHA1_2(psalt, 16, ppwd, strlen(pwd)/2, &dwSaltandPwdLen);
printf("SHA1 of SaltandPwd:\n");
for(DWORD i = 0 ; i < dwSaltandPwdLen ; i++) {
printf("%2.2x ",pbSaltandPwdHash[i]);
}
printf("\n");
//------------------------H0 = H(salt, password)-----
//------------------------Hfinal = H(H0, block)-----
HCRYPTHASH hHash1 = 0;
CryptCreateHash( hCryptProv, CALG_SHA1, 0, 0, &hHash1) ;
CryptHashData( hHash1, pbSaltandPwdHash, dwSaltandPwdLen, 0) ;
CryptHashData( hHash1, (unsigned char*)&blocknum, sizeof(blocknum), 0) ;
//------------------------Hfinal = H(H0, block)-----
CryptDeriveKey(hCryptProv, CALG_RC4, hHash1, 0x00280000, hKey);
if(hHash1 != 0) CryptDestroyHash(hHash1);
if(pbSaltandPwdHash != NULL) free(pbSaltandPwdHash);
return 0;
}
I knew how to get H0 under linux,but I dont know how to get the hHash1 and hKey.
This post sounds like it does the same thing: Implement Windows CryptoAPI CryptDeriveKey Using OpenSSL APIs
A more general way of generating hashes in openssl is below:
Before you do anything:
#include <ssl/evp.h>
int main(int argc, char argv[]) // or in an "initialise" type function
{
OpenSSL_add_all_digests()
...
}
Then to generate the hash (error checking omitted):
const EVP_MD *digest;
EVP_MD_CTX context;
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int hash_len;
digest = EVP_get_digestbyname("sha1"); /* choose the hash type here */
EVP_MD_CTX_init(&context);
EVP_DigestInit_ex(&contxt, digest, NULL);
EVP_DigestUpdate(&context, pbSaltandPwdHash, dwSaltandPwdLen);
EVP_DigestUpdate(&context, &blocknum, sizeof(blocknum));
EVP_DigestFinal_ex(&context, hash, &hash_len);
EVP_MD_CTX_cleanup(&context);
/* Now use hash and hash_len as required */
This is very similar to other questions but the ones I've looked at either don't have an answer or don't quite ask the same question. I have a self-signed CA certificate, and two other certificates that are signed with that CA certificate. I'm fairly sure the certificates are correct, because 'openssl verify' works:
$ openssl verify -CAfile ca.pem server.pem
server.pem: OK
(The above is from memory, I don't have them in front of me, so it may be slightly off).
Now I want to verify the certificates programatically. I have a utility function with pseudocode below:
int verify_cert(X509 *cert, X509 *cacert)
{
int ret;
X509_STORE *store;
X509_STORE_CTX *ctx;
store = X509_STORE_new();
X590_STORE_add_cert(store, cacert);
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, cert, NULL);
ret = X509_verify_cert(ctx);
/* check for errors and clean up */
}
My problem is that the above code always returns 'failed to find issuer certificate'. What have I done wrong? I believe I am creating a new store, adding the cacert, creating a new context, and adding the child cert to be verified to the context with a pointer to the store which contains the CA. I'm pretty obviously doing something wrong, but I'm unsure what.
Any ideas?
Update: I'm aware I can save these certs to disk and use something like X509_LOOKUP_file or something similar. I'm looking for a solution that doesn't touch the disk unnecessarily.
You can use the normal validation routines (see How do you verify a public key was issued by your private CA?), like the -verify function in OpenSSL does. You need to create a lookup method (X509_LOOKUP_METHOD) like X509_LOOKUP_file(), but which works with a character string instead of a filename. The code for X509_LOOKUP_buffer() is as follows.
Header file by_buffer.h:
/* File: by_buffer.h */
#ifndef BY_BUFFER_H
#define BY_BUFFER_H
#include <openssl/x509.h>
#ifdef __cplusplus
extern "C" {
#endif
#define X509_L_BUF_LOAD 1
#define X509_LOOKUP_load_buf(x,name,type) \
X509_LOOKUP_ctrl((x),X509_L_BUF_LOAD,(name),(long)(type),NULL)
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void);
#ifdef __cplusplus
}
#endif
#endif /* BY_BUFFER_H */
The c program by_buffer.c:
/* by_buffer.c - copied and modified from crypto/x509/by_file.c */
/* Copyright (C) - should be the same as for OpenSSL
*/
#include "by_buffer.h"
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include "../crypto/cryptlib.h"
#include <openssl/lhash.h>
#include <openssl/buffer.h>
#include <openssl/pem.h>
#include <openssl/err.h>
static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
long argl, char **ret);
X509_LOOKUP_METHOD x509_buffer_lookup=
{
"Load buffer into cache",
NULL, /* new */
NULL, /* free */
NULL, /* init */
NULL, /* shutdown */
by_buffer_ctrl, /* ctrl */
NULL, /* get_by_subject */
NULL, /* get_by_issuer_serial */
NULL, /* get_by_fingerprint */
NULL, /* get_by_alias */
};
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void)
{
return(&x509_buffer_lookup);
}
static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
char **ret)
{
int ok=0;
char *certBuf;
switch (cmd)
{
case X509_L_BUF_LOAD:
if (argl == X509_FILETYPE_DEFAULT)
{
X509err(X509_F_BY_FILE_CTRL,X509_R_LOADING_DEFAULTS);
}
else
{
if(argl == X509_FILETYPE_PEM)
ok = (X509_load_cert_crl_buf(ctx,argp,
X509_FILETYPE_PEM) != 0);
else
ok = (X509_load_cert_buf(ctx,argp,(int)argl) != 0);
}
break;
}
return(ok);
}
int X509_load_cert_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
int ret=0;
BIO *in=NULL;
int i,count=0;
X509 *x=NULL;
if (certBuf == NULL) return(1);
in=BIO_new(BIO_s_mem());
if(in==NULL) goto err;
if (type == X509_FILETYPE_PEM)
{
for (;;)
{
x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL);
if (x == NULL)
{
if ((ERR_GET_REASON(ERR_peek_last_error()) ==
PEM_R_NO_START_LINE) && (count > 0))
{
ERR_clear_error();
break;
}
else
{
X509err(X509_F_X509_LOAD_CERT_FILE,
ERR_R_PEM_LIB);
goto err;
}
}
i=X509_STORE_add_cert(ctx->store_ctx,x);
if (!i) goto err;
count++;
X509_free(x);
x=NULL;
}
ret=count;
}
else if (type == X509_FILETYPE_ASN1)
{
x=d2i_X509_bio(in,NULL);
if (x == NULL)
{
X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_ASN1_LIB);
goto err;
}
i=X509_STORE_add_cert(ctx->store_ctx,x);
if (!i) goto err;
ret=i;
}
else
{
X509err(X509_F_X509_LOAD_CERT_FILE,X509_R_BAD_X509_FILETYPE);
goto err;
}
err:
if (x != NULL) X509_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}
int X509_load_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
int ret=0;
BIO *in=NULL;
int i,count=0;
X509_CRL *x=NULL;
if (certBuf == NULL) return(1);
//in=BIO_new(BIO_s_file_internal());
in=BIO_new(BIO_s_mem());
if(in==NULL) goto err;
if (type == X509_FILETYPE_PEM)
{
for (;;)
{
x=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
if (x == NULL)
{
if ((ERR_GET_REASON(ERR_peek_last_error()) ==
PEM_R_NO_START_LINE) && (count > 0))
{
ERR_clear_error();
break;
}
else
{
X509err(X509_F_X509_LOAD_CRL_FILE,
ERR_R_PEM_LIB);
goto err;
}
}
i=X509_STORE_add_crl(ctx->store_ctx,x);
if (!i) goto err;
count++;
X509_CRL_free(x);
x=NULL;
}
ret=count;
}
else if (type == X509_FILETYPE_ASN1)
{
x=d2i_X509_CRL_bio(in,NULL);
if (x == NULL)
{
X509err(X509_F_X509_LOAD_CRL_FILE,ERR_R_ASN1_LIB);
goto err;
}
i=X509_STORE_add_crl(ctx->store_ctx,x);
if (!i) goto err;
ret=i;
}
else
{
X509err(X509_F_X509_LOAD_CRL_FILE,X509_R_BAD_X509_FILETYPE);
goto err;
}
err:
if (x != NULL) X509_CRL_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}
int X509_load_cert_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
BIO *in;
int i, count = 0;
if(type != X509_FILETYPE_PEM)
return X509_load_cert_buf(ctx, certBuf, type);
in = BIO_new(BIO_s_mem());
if(!in) {
X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_SYS_LIB);
return 0;
}
BIO_write(in, certBuf, strlen(certBuf));
inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
BIO_free(in);
if(!inf) {
X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_PEM_LIB);
return 0;
}
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(ctx->store_ctx, itmp->x509);
count++;
}
if(itmp->crl) {
X509_STORE_add_crl(ctx->store_ctx, itmp->crl);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return count;
}
Routine in C++ which calls the above routines:
#include "by_buffer.h"
static int check(X509_STORE *ctx, const char *certBuf);
static X509 *load_cert(const char *certBuf);
int validateKey(const char *rsaKeyCA, const char *rsaCertificate) {
int ret=0;
X509_STORE *cert_ctx=NULL;
X509_LOOKUP *lookup=NULL;
cert_ctx=X509_STORE_new();
if (cert_ctx == NULL) goto end;
OpenSSL_add_all_algorithms();
lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_buffer());
if (lookup == NULL)
goto end;
if(!X509_LOOKUP_load_buf(lookup,rsaKeyCA,X509_FILETYPE_PEM))
goto end;
lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_hash_dir());
if (lookup == NULL)
goto end;
X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
ret = check(cert_ctx, rsaCertificate);
end:
if (cert_ctx != NULL) X509_STORE_free(cert_ctx);
return ret;
}
static X509 *load_cert(const char *certBuf)
{
X509 *x=NULL;
BIO *cert;
if ((cert=BIO_new(BIO_s_mem())) == NULL)
goto end;
BIO_write(cert, certBuf, strlen(certBuf));
x=PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL);
end:
if (cert != NULL) BIO_free(cert);
return(x);
}
static int check(X509_STORE *ctx, const char *certBuf)
{
X509 *x=NULL;
int i=0,ret=0;
X509_STORE_CTX *csc;
x = load_cert(certBuf);
if (x == NULL)
goto end;
csc = X509_STORE_CTX_new();
if (csc == NULL)
goto end;
X509_STORE_set_flags(ctx, 0);
if(!X509_STORE_CTX_init(csc,ctx,x,0))
goto end;
////// See crypto/asn1/t_x509.c for ideas on how to access and print the values
//printf("X.509 name: %s\n", x->name);
i=X509_verify_cert(csc);
X509_STORE_CTX_free(csc);
ret=0;
end:
ret = (i > 0);
if (x != NULL)
X509_free(x);
return(ret);
}
I think, you can use "X509_STORE_set_verify_cb" to add a callback to identify the actual error:
static int verify_cb(int ok, X509_STORE_CTX *ctx)
{
if (!ok)
{
/* check the error code and current cert*/
X509 *currentCert = X509_STORE_CTX_get_current_cert(ctx);
int certError = X509_STORE_CTX_get_error(ctx);
int depth = X509_STORE_CTX_get_error_depth(ctx);
printCert(currentCert);
printf("Error depth %d, certError %d", depth, certError)
}
return(ok);
}
int verify_cert(X509 *cert, X509 *cacert)
{
int ret;
X509_STORE *store;
X509_STORE_CTX *ctx;
store = X509_STORE_new();
X509_STORE_set_verify_cb(store, verify_cb);
X590_STORE_add_cert(store, cacert);
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, cert, NULL);
ret = X590_verify_cert(ctx);
/* check for errors and clean up */
}
Unless we know the error code it is difficult to guess the actual problem. The code otherwise looks OK.
I encountered this problem myself and started off with code very close to the OP. My certificate chain included 3 certificates:
Certificate 1 (root-ca) Issuer: root-ca Subject: root-ca
Certificate 2 (signing-ca) Issuer: root-ca Subject: signing-ca
Certificate 3 (device) Issuer: signing-ca Subject: device
I wanted to verify the device certificate. My ca.pem equivalent (wrt OP) contained the root-ca and signing-ca.
The X509_verify_cert function needs the entire certificate chain all the way to the root (root-ca & signing-ca) in the X509_store.
Below is my code that works for me. Checks on return values were omitted to lean the code down.
int getIssuerCert(X509_STORE *x509_store){
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
BIO *in;
int i, count = 0;
in = BIO_new(BIO_s_mem());
BIO_write(in, issuerCertStr, strlen(issuerCertStr)); //string containing root-ca & signing-ca
inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
if(in != NULL) BIO_free(in);
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(x509_store, itmp->x509);
count++;
}
if(itmp->crl) {
X509_STORE_add_crl(x509_store, itmp->crl);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return 0;
}
int verify_cert(){
int ret = 0;
X509 *devCert = NULL;
X509_STORE *x509_store = NULL;
X509_STORE_CTX *x509_store_ctx = NULL;
OpenSSL_add_all_algorithms();
devCert = getDeviceCert(); // Returns X509 pointer
x509_store = X509_STORE_new();
X509_STORE_set_verify_cb(x509_store, verify_cb);
X509_STORE_set_flags(x509_store, 0);
x509_store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(x509_store_ctx, x509_store, devCert, NULL)
X509_STORE_CTX_set_purpose(x509_store_ctx, X509_PURPOSE_ANY);
ret = X509_verify_cert(x509_store_ctx);
if(x509_store_ctx != NULL) X509_STORE_CTX_free(x509_store_ctx);
if(x509_store != NULL) X509_STORE_free(x509_store);
if(devCert != NULL) X509_free(devCert);
EVP_cleanup();
return ret;
}
I didn't need to create any lookup methods. The key for me was looping through my certificates from my string in memory so I had all the certificates I need to complete the chain. The string is equivalent to what I would have fed into openssl verify for the option -CAfile.
Also, make sure your X509 pointers are not null when the are used.
A possible answer (don't have the rep points to add a comment, sorry): the manpage for SSL_CTX_load_verify_locations(3) says,
When building its own certificate chain, an OpenSSL client/server will try to fill in
missing certificates from CAfile/CApath, if the certificate chain was not explicitly
specified (see SSL_CTX_add_extra_chain_cert(3), SSL_CTX_use_certificate(3).
(Failure to match parens theirs, not mine.)
Which seems to mean that, as an alternative to SSL_CTX_load_verify_locations(3), it should be possible to use SSL_CTX_add_extra_chain_cert(3) or SSL_CTX_use_certificate(3) -- both of which take a X509 * arg. Thus obviating the need for Mr Ed's solution as seen above.
Please take a look at SSL_CTX_load_verify_locations () function: http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html
SSL_CTX_load_verify_locations() specifies the locations for ctx, at
which CA certificates for verification purposes are located. The
certificates available via CAfile and CApath are trusted.
You can generate a CA certificate file containing both ca.pem server.pem:
#!/bin/sh
rm CAfile.pem
for i in ca.pem server.pem ; do
openssl x509 -in $i -text >> CAfile.pem
done
And then set CAfile variable to point to CAfile.pem file.
Hope it helps !
See the official source code:
apps/verify.c
static int check(X509_STORE *ctx, const char *file,
STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
STACK_OF(X509_CRL) *crls, int show_chain);
You can see how to output 'OK' here:
if (i > 0 && X509_STORE_CTX_get_error(csc) == X509_V_OK) {
printf("%s: OK\n", (file == NULL) ? "stdin" : file);
The function dependence can be found in apps/apps.c