how to pass PEM certificate as first arg of i2d_X509 - c

I'm creating self-signed certificate by:
openssl req -new -x509 -key privkey.pem -out cert.pem -days 1095
How do I pass cert.pem to i2d_X509?
I need something like:
len = i2d_X509(".\cert.pem", &buf);
but my certificate in a PEM file..
Here is my code: (I used the example in https://www.openssl.org/docs/crypto/d2i_X509.html)
#include <openssl/x509.h>
#include <stdio.h>
int main(void)
{
int len,i;
unsigned char *buf;
buf = NULL;
len = i2d_X509((X509*)".\cert.pem", &buf);
if (len < 0){
printf("error len < 0");
return -1;
}
printf("buf:");
for (i = 0; i < len ; i++)
printf("0x%02X", *(buf+i));
return 0;
}

How do I pass cert.pem to i2d_X509? ...
You don't/can't. You need to read the certificate with PEM_read_bio_X509. PEM_read_bio_X509 returns an X509*. Then you can pass it to i2d_X509.
Be sure to call X509_free on the pointer when done with it.
Or, do as Philippe suggests - convert it to ASN.1/DER and then use it with d2i_X509_fp.
Also see the OpenSSL man pages on the PEM Read/Write functions.

Related

OpenSSL error: unable to verify the first certificate

I've been trying to make a simple client/server echo application for the sake of getting to know OpenSSL. I've been following Network Security with OpenSSL by John Viega, Matt Messier, Pravir Chandra (O'Reilly). Their example shows how to set up a root certificate authority, a server CA signed with the root CA, then server certificate signed with the server CA and, finally, a client certificate signed with the root CA and then import them in the C program.
I have made small changes to the program (such as not using threads at all) but most of it is their. My idea was to learn as I try this out but I've come to a problem.
When I try to connect the two, I get an error message saying "unable to get local issuer certificate" (error 20).
I would also like to state that the "previous version" worked (meaning that they progress in their examples).
Not sure if the problem is in the code, the certificates or both.
My OpenSSL version is OpenSSL 1.0.1f 6 Jan 2014. (as returned by the openssl version command)
My knowledge in this matter is very shallow at the moment and I've not had much luck finding an answer. I realize that I still have much reading to do and I will do so but this is a problem I need to overcome.
int seed_prng(int bytes) function is mine, a fix to the problem of not finding the seed_prng(void) function in all these libraries (also one of the things I found that is not fitting).
Perhaps, as this tutorial is 14 years old (at least), it is outdated and I should follow another one? If so, please recommend a tutorial with a high detail level, since the documentation of OpenSSL is not very friendly, in my experience.
Below, I provide all the files included in the process (except for the Makefile.)
The flags I use for the compilation are: -L/usr/lib -lssl -lcrypto -Wall
common.h :
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/objects.h>
#define PORT "8081"
#define SERVER "localhost"
#define CLIENT "localhost"
#define int_error(msg) handle_error(__FILE__, __LINE__, msg)
//Do not confuse with SSL_METHOD ; TLSv1_method() is also a viable option here
#define SSL_METHOD_ SSLv23_method()
#define SEED_PRNG_() seed_prng(30)
#define DEFAULT_DEPTH_ 4
#define DATA_SIZE_ 256
void handle_error(const char *file, int lineno, const char *msg) ;
void init_OpenSSL(void) ;
int seed_prng(int bytes) ;
int verify_callback(int ok, X509_STORE_CTX *store) ;
long post_connection_check(SSL *ssl, char *host) ;
common.c :
#include "common.h"
void handle_error(const char *file, int lineno, const char *msg)
{
fprintf(stderr, "** %s:%i %s\n", file, lineno, msg);
ERR_print_errors_fp(stderr);
exit(-1);
}
void init_OpenSSL(void)
{
if (!SSL_library_init())
{
fprintf(stderr, "** OpenSSL initialization failed!\n");
exit(-1);
}
SSL_load_error_strings();
}
//Not sure if this is good idea! Have to do some research
int seed_prng(int bytes)
{
if (!RAND_load_file("/dev/urandom", bytes))
{
return 0;
}
return 1;
}
int verify_callback(int ok, X509_STORE_CTX *store)
{
char data[DATA_SIZE_];
if (!ok)
{
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
fprintf(stderr, "-Error with certificate at depth: %i\n", depth);
X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
fprintf(stderr, " issuer = %s\n", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
fprintf(stderr, " subject = %s\n", data);
fprintf(stderr, " err %i:%s\n", err,
X509_verify_cert_error_string(err));
}
return ok;
}
long post_connection_check(SSL *ssl, char *host)
{
X509 *cert;
X509_NAME *subj;
char data[DATA_SIZE_];
int extcount;
int ok = 0;
if (!(cert = SSL_get_peer_certificate(ssl)) || !host)
{
if (cert)
{
X509_free(cert);
}
return X509_V_ERR_APPLICATION_VERIFICATION;
}
if ((extcount = X509_get_ext_count(cert)) > 0)
{
int i;
for (i = 0; i < extcount; i++)
{
const char *extstr;
X509_EXTENSION *ext;
ext = X509_get_ext(cert, i);
extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
if (!strcmp(extstr, "subjectAltName"))
{
int j;
unsigned char *data;
STACK_OF(CONF_VALUE) *val;
CONF_VALUE *nval;
const X509V3_EXT_METHOD *meth;
if (!(meth = X509V3_EXT_get(ext)))
{
break;
}
data = ext->value->data;
val = meth->i2v(meth,(meth->d2i(NULL, (const unsigned char **)(&data), ext->value->length)), NULL);
for (j = 0; j < sk_CONF_VALUE_num(val); j++)
{
nval = sk_CONF_VALUE_value(val, j);
if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host))
{
ok = 1;
break;
}
}
}
if (ok)
{
break;
}
}
}
if (!ok && (subj = X509_get_subject_name(cert)) && X509_NAME_get_text_by_NID(subj, NID_commonName, data, DATA_SIZE_) > 0)
{
data[DEFAULT_DEPTH_ - 1] = 0;
if (strcasecmp(data, host) != 0)
{
if (cert)
{
X509_free(cert);
}
return X509_V_ERR_APPLICATION_VERIFICATION;
}
}
X509_free(cert);
return SSL_get_verify_result(ssl);
}
server.c :
#include "common.h"
#define BUF_SIZE_ 80
#define CERTFILE "certificates/serverNOKEY.pem"
#define CAFILE "certificates/rootcert.pem"
#define CADIR NULL
SSL_CTX *setup_server_ctx(void)
{
SSL_CTX *ctx;
//This specifies that either SSL or TLS can be used
//Later, we will "filter" out SSLv2
ctx = SSL_CTX_new(SSLv23_method());
//These two functions are used to load trusted CAs
if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1)
{
int_error("Error loading CA file and/or directory");
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
{
int_error("Error loading default CA file and/or directory");
}
//This loads a certificate from a file
if (SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1)
{
int_error("Error loading certificate from file");
}
//This loads a private key (in our code, from the same file but I think that it is not necessary)
if (SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM) != 1)
{
int_error("Error loading private key from file");
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
SSL_CTX_set_verify_depth(ctx, DEFAULT_DEPTH_);
return ctx;
}
int do_server_loop(SSL *ssl)
{
int err, nread;
char buf[BUF_SIZE_];
//I think that this reading should be redone 'cause there's something wrong
do
{
for (nread = 0; nread < sizeof(buf) - 1; nread += err)
{
err = SSL_read(ssl, buf + nread, sizeof(buf) - nread);
printf("read %d bytes\n", err);
if (err <= 0)
{
break;
}
}
fwrite(buf, sizeof(char), nread, stdout);
} while (err > 0);
return (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) ? 1 : 0;
}
void server_part(SSL *ssl)
{
long err;
//accepting connection from ssl object (structure)
if (SSL_accept(ssl) <= 0)
{
int_error("Error accepting SSL connection");
}
if ((err = post_connection_check(ssl, CLIENT)) != X509_V_OK)
{
fprintf(stderr, "-Error: peer certificate: %s\n",
X509_verify_cert_error_string(err));
int_error("Error checking SSL object after connection");
}
fprintf(stderr, "SSL Connection opened\n");
//As I figured, SSL_shutdown is a clean way to go but SSL_clear
//will force the closing of the communication if it wasn't closed cleanly
if (do_server_loop(ssl))
{
//See this https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
SSL_shutdown(ssl);
}
else
{
// https://www.openssl.org/docs/manmaster/ssl/SSL_clear.html
SSL_clear(ssl);
}
fprintf(stderr, "SSL Connection closed\n");
SSL_free(ssl);
}
int main(int argc, char *argv[])
{
BIO *acc, *client;
SSL *ssl;
SSL_CTX *ctx;
init_OpenSSL();
//This is my function, gotta investigate it and see what should be there (maybe I got it right?)
SEED_PRNG_();
ctx = setup_server_ctx();
acc = BIO_new_accept(PORT);
if (!acc)
{
int_error("Error creating server socket");
}
if (BIO_do_accept(acc) <= 0)
{
int_error("Error binding server socket");
}
for (;;)
{
if (BIO_do_accept(acc) <= 0)
{
int_error("Error accepting connection");
}
client = BIO_pop(acc);
if (!(ssl = SSL_new(ctx)))
{
int_error("Error creating SSL context");
}
SSL_set_bio(ssl, client, client);
server_part(ssl);
}
SSL_CTX_free(ctx);
BIO_free(acc);
return 0;
}
client.c :
#include "common.h"
#define BUF_SIZE_ 80
#define CERTFILE "certificates/clientNOKEY.pem"
#define CAFILE "certificates/rootcert.pem"
#define CADIR NULL
SSL_CTX *setup_client_ctx(void)
{
SSL_CTX *ctx;
ctx = SSL_CTX_new(SSL_METHOD_);
if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1)
{
int_error("Error loading CA file and/or directory");
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
{
int_error("Error loading default CA file and/or directory");
}
if (SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1)
{
int_error("Error loading certificate from file");
}
if (SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM) != 1)
{
int_error("Error loading private key from file");
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
SSL_CTX_set_verify_depth(ctx, DEFAULT_DEPTH_);
return ctx;
}
int do_client_loop(SSL *ssl)
{
int err, nwritten;
char buf[BUF_SIZE_];
for (;;)
{
if (!fgets(buf, sizeof(buf), stdin))
{
break;
}
//printf("ucitao: %s\n", buf);
for (nwritten = 0; nwritten < sizeof(buf); nwritten += err)
{
err = SSL_write(ssl, buf + nwritten, strlen(buf) - nwritten);
if (err <= 0)
{
return 0;
}
else
{
printf("Sent: %s\n", buf);
}
}
}
return 1;
}
int main(int argc, char *argv[])
{
BIO *conn;
SSL *ssl;
SSL_CTX *ctx;
long err;
init_OpenSSL();
SEED_PRNG_();
ctx = setup_client_ctx();
conn = BIO_new_connect(SERVER ":" PORT);
if (!conn)
{
int_error("Error creating connection BIO");
}
if (BIO_do_connect(conn) <= 0)
{
int_error("Error connecting to remote machine");
}
if (!(ssl = SSL_new(ctx)))
int_error("Error creating an SSL context");
SSL_set_bio(ssl, conn, conn);
if (SSL_connect(ssl) <= 0)
{
int_error("Error connecting SSL object");
}
if ((err = post_connection_check(ssl, SERVER)) != X509_V_OK)
{
fprintf(stderr, "-Error: peer certificate: %s\n",
X509_verify_cert_error_string(err));
int_error("Error checking SSL object after connection");
}
fprintf(stderr, "SSL Connection opened\n");
if (do_client_loop(ssl))
{
SSL_shutdown(ssl);
}
else
{
SSL_clear(ssl);
}
fprintf(stderr, "SSL Connection closed\n");
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
The script I used to create the CAs, certificates and sign them:
#! /bin/bash
echo "Begin"
#the line below ensures that the script finishes after an unsuccessful command, not trying to execute any next command,
#since it's the assumption that every previous command was successful
set -e
echo "Creating the root CA"
openssl req -newkey rsa:1024 -sha1 -keyout rootkey.pem -out rootreq.pem
openssl x509 -req -in rootreq.pem -sha1 -extfile myopenssl.cnf -extensions v3_ca -signkey rootkey.pem -out rootcert.pem
cat rootcert.pem rootkey.pem > root.pem
openssl x509 -subject -issuer -noout -in root.pem
echo "Creating the server CA and signing it with the root CA"
openssl req -newkey rsa:1024 -sha1 -keyout serverCAkey.pem -out serverCAreq.pem
openssl x509 -req -in serverCAreq.pem -sha1 -extfile myopenssl.cnf -extensions v3_ca -CA root.pem -CAkey root.pem -CAcreateserial -out serverCAcert.pem
cat serverCAcert.pem serverCAkey.pem rootcert.pem > serverCA.pem
openssl x509 -subject -issuer -noout -in serverCA.pem
echo "Creating the server\'s certificate and signing it with the server CA"
openssl req -newkey rsa:1024 -sha1 -keyout serverkey.pem -out serverreq.pem
openssl x509 -req -in serverreq.pem -sha1 -extfile myopenssl.cnf -extensions usr_cert -CA serverCA.pem -CAkey serverCA.pem -CAcreateserial -out servercert.pem
cat servercert.pem serverkey.pem serverCAcert.pem rootcert.pem > server.pem
openssl x509 -subject -issuer -noout -in server.pem
echo "Creating the client certificate and signing it with the root CA"
openssl req -newkey rsa:1024 -sha1 -keyout clientkey.pem -out clientreq.pem
openssl x509 -req -in clientreq.pem -sha1 -extfile myopenssl.cnf -extensions usr_cert -CA root.pem -CAkey root.pem -CAcreateserial -out clientcert.pem
cat clientcert.pem clientkey.pem rootcert.pem > client.pem
openssl x509 -subject -issuer -noout -in client.pem
echo "creating dh512.pem and dh1024.pem"
openssl dhparam -check -text -5 512 -out dh512.pem
openssl dhparam -check -text -5 1024 -out dh1024.pem
echo "Making new keys which do not require passkey"
echo "Creating no passkey server key"
openssl rsa -in server.pem -out serverNOKEY.pem
openssl x509 -in server.pem >>serverNOKEY.pem
echo "serverNOKEY.pem created"
echo "Creating no passkey client key"
openssl rsa -in client.pem -out clientNOKEY.pem
openssl x509 -in client.pem >>clientNOKEY.pem
echo "clientNOKEY.pem created"
echo "Done! Success!"
I faced similar issue when I was setting up SSL Communications between java applications. To determine whether its a code issue or certificate issue itself you can run below openssl command. If certificate isn't getting verified it means there is problems in verifying certificates using root CA.
Verify a certificate
openssl verify certificate_name
Reference .
Also, as it seems you are working on self-signed certificate you can switch off verification of certificates in your code and proceed. In production environment when certificates are signed by Certificate Authority (CA) such errors are less likely to appear.

Mutual Authentication and restricting user certificates to specific set at server

I'm looking for a way to restrict client certificates to specific set of self-signed certificates on the server side using the OpenSSL API.
There is a set of trusted self-signed certificates, say ./dir/*.pem. I want to reject connections, if they don't supply one of those certificates.
I can achieve almost desired behaviour by comparing server and client certificate fingerprints in the SSL context verification callback:
SSL_CTX *ctx;
...
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
static inline int get_fingerprint(X509* cert, unsigned char *md, unsigned int *n)
{
return X509_digest(cert, EVP_sha1(), md, n);
}
static inline int compare_certificates(X509 *c1, X509 *c2)
{
unsigned char md1[EVP_MAX_MD_SIZE], md2[EVP_MAX_MD_SIZE];
unsigned int n1, n2;
if (!(get_fingerprint(c1, md1, &n1) && get_fingerprint(c2, md2, &n2))) {
return -1;
}
return memcmp(md1, md2, n1);
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
int err = X509_STORE_CTX_get_error(ctx);
/* Allow self-signed certificates */
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
if (0 != compare_certificates(ctx->current_cert, SSL_CTX_get0_certificate(ssl->ctx))) {
/* Peer certificate doesn't match the server certificate */
preverify_ok = 0;
}
/* More checks ... */
return preverify_ok;
}
So if the server and client certificate fingerprints match, verification passes. Otherwise, connection is closed by the server.
I might compute fingerprints of the trusted certificates somewhere in initialization phase, then check them in a loop within the verify_callback. However, I don't like this idea. There should be easier way to do this.
I thought SSL_CTX_load_verify_locations() is just what I was looking for(but it looks like it's not; i'll explain why):
SSL_CTX_load_verify_locations() specifies the locations for ctx, at which CA certificates for verification purposes are located.
...
If CAfile is not NULL, it points to a file of CA certificates in PEM format. The file can contain several CA certificates...
The certificates in CApath are only looked up when required, e.g. when building the certificate chain or when actually performing the verification of a peer certificate.
(man 3 SSL_CTX_load_verify_locations)
Well, I guess SSL_VERIFY_FAIL_IF_NO_PEER_CERT implies verifying the peer certificate. Then it looks like all I need to do is to make a bundle of trusted certificates and pass it to SSL_CTX_load_verify_locations():
bundle_file=CAbundle.pem
cd ./dir
rm -f $bundle_file
for i in *.pem; do
openssl x509 -in $i -text >> $bundle_file
done
c_rehash .
SSL_CTX *ctx;
const char *cafile = "dir/CAbundle.pem";
const char *capath = NULL;
...
if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
/* Unable to set verify locations ... */
}
cert_names = SSL_load_client_CA_file(cafile);
if (cert_names != NULL) {
SSL_CTX_set_client_CA_list(ctx, cert_names);
} else {
/* Handle error ... */
}
All looks good. But the server still accepts connections with different peer certificates.
I've reproduced this behaviour using standard OpenSSL utilities here: https://gist.github.com/rosmanov/d960a5d58a96bdb730303c5b8e86f951
So my question is: how do I configure the server to accept only peers providing only specific certificates?
Update
I've found that the "whitelist" of certificates (CA bundle) actually works,
when I remove the following from the verify_callback:
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
So without this block everything just works. The server responds to a client connected with one of certificates listed in CAbundle.pem. If a client connects with different certificate, the server closes connection.
However, there is a strange thing. In both cases openssl s_client outputs:
Verify return code: 18 (self signed certificate)
Then maybe
if (!preverify_ok
&& err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
&& allow_self_signed
&& !cafile
&& !capath) {
preverify_ok = 1;
}
?
Update 2
Now I understand why openssl s_client outputs Verify return code: 18 (self signed...). It doesn't trust server's certificate unless -CAfile or -CApath contains the server certificate. And the server certificate is self-signed.
An explanation (for commandline) and a halfanswer (for library):
I (this time fully) redid your gist and was reminded of an inconsistency here. Commandline openssl xxx utilities are mostly designed as test/debugging tools, and in particular:
s_client normally (except anonymous, SRP, etc) receives a cert chain from the server, but uses a callback that only logs what it got and ignores/overrides any error; this is the block
depth=0 C = AU, ST = StateA, L = CityA, O = CompanyA, CN = localhost, emailAddress = a#gmail.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = AU, ST = StateA, L = CityA, O = CompanyA, CN = localhost, emailAddress = a#gmail.com
verify return:1
just after CONNECTED(fd) in your s_client output, but as you see in spite of the error it continues with the handshake resulting in a usable connection.
s_server is a more complicated. It does not request a cert from client by default, only if you specify -verify or -Verify (which set SSL_VERIFY_PEER which is not the default for server), and if it does request a cert client has the option whether to send one (with associated proof in CertVerify). If client does send chain, s_server uses the same callback as s_client which overrides any error and continues with the connection; this in your s_server output with the same verify error:num-18... which actually means 'root (including selfsigned which is its own root) in received chain but not in local truststore'. If client does not send chain, -verify continues, but -Verify (which also sets SSL_VERIFY_FAIL_IF_NO_PEER_CERT) aborts the handshake with alert 40 and returns an error, so the s_server output is very different:
verify depth is 0, must return a certificate
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
ERROR
140679792887624:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate:s3_srvr.c:3271:
shutting down SSL
CONNECTION CLOSED
ACCEPT
But a program using the library should work. I hacked up this simple test from parts of some other programs (hence the odd indentation):
/* SO36821430 2016-04-25 */
#include <stdio.h>
#if defined(_WIN32)&&!defined(WIN32)
#define WIN32 /*anything*/
#endif
#ifdef WIN32
#include <winsock2.h>
typedef int socklen_t;
#define SOCKERR WSAGetLastError()
#include "openssl/applink.c"
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef INADDR_NONE
#define INADDR_NONE (ipaddr_t)-1
#endif
typedef int SOCKET;
enum { INVALID_SOCKET = -1, SOCKET_ERROR = -1 };
#define SOCKERR errno
#define closesocket close
#endif
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
void sockerr (const char *what){
fprintf (stderr, "%s %d %s\n", what, SOCKERR, strerror(SOCKERR));
}
void sslerrn (const char *what){
fprintf (stderr, "* %s failed:\n", what);
ERR_print_errors_fp (stderr);
}
void sslerr (const char *what, int rv){
fprintf (stderr, "* %s return %d:\n", what, rv);
ERR_print_errors_fp (stderr);
}
void sslerrx (SSL * ssl, const char *what, int rv){
int rc = SSL_get_error (ssl, rv);
if( rv == -1 && rc == SSL_ERROR_SYSCALL ) sockerr (what);
else fprintf (stderr, "* %s return %d,%d\n", what, rv, rc);
ERR_print_errors_fp (stderr);
}
void subj_oneline (X509 * cert, FILE *fp){
X509_NAME * subj = X509_get_subject_name (cert);
BIO *bmem = BIO_new (BIO_s_mem()); char *ptr; int n;
X509_NAME_print_ex (bmem, subj, 0, XN_FLAG_ONELINE);
n = (int) BIO_get_mem_data (bmem, &ptr);
if( n <= 0 ) ptr = "?", n = 1;
fwrite (ptr,1,n,fp);
}
const char * inaddr;
int inport;
char buf [9999];
int main (int argc, char* argv[] )
{
int rv;
struct sockaddr_in sin; socklen_t sinlen;
SOCKET s1, s2; SSL_CTX *ctx = NULL;
time_t now; struct tm * tm;
#ifdef WIN32
struct WSAData wsa;
rv = WSAStartup (MAKEWORD(1,1), &wsa);
if(rv){ printf ("WSAStartup %d\n", rv); exit(1); }
#endif
if( argc < 2 || argc > 6 )
printf ("usage: %s port key cert CAcerts\n", argv[0]), exit(1);
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons (atoi(argv[1]));
sin.sin_family = AF_INET;
/**/
SSL_library_init();
SSL_load_error_strings();
ctx = SSL_CTX_new (SSLv23_server_method());
if( !ctx ){ sslerrn("CTX_new"); exit(1); }
SSL_CTX_set_options (ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
rv = SSL_CTX_use_PrivateKey_file (ctx, argv[2], SSL_FILETYPE_PEM);
if( rv != 1 ){ sslerr ("use_PrivateKey_file",rv); exit(1); }
rv = SSL_CTX_use_certificate_file (ctx, argv[3], SSL_FILETYPE_PEM);
if( rv != 1 ){ sslerr ("use_certificate_file",rv); exit(1); }
SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
if( !SSL_CTX_load_verify_locations (ctx, argv[4], NULL) ){
sslerrn ("load_verify_locations"); exit(1); }
SSL_CTX_set_client_CA_list (ctx, SSL_load_client_CA_file (argv[4]));
/**/
if( (s1 = socket (AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET ){
sockerr ("socket()"); exit(1); }
if( bind (s1, (struct sockaddr*)&sin, sizeof sin) < 0 ){
sockerr ("bind()"); exit(1); }
if( listen (s1, 5) < 0 ){
sockerr ("listen()"); exit(1); }
do{
sinlen = sizeof sin;
if( (s2 = accept (s1, (struct sockaddr*)&sin, &sinlen)) == INVALID_SOCKET ){
sockerr ("accept()"); exit(1); }
now = time(NULL); tm = localtime(&now);
printf ("+ %s %u #%02d.%02d.%02d\n", inet_ntoa (sin.sin_addr),
ntohs (sin.sin_port), tm->tm_hour, tm->tm_min, tm->tm_sec);
/**/
SSL * ssl = SSL_new (ctx);
if( !ssl ){ sslerrn("SSL_new"); goto next; }
SSL_set_fd (ssl, s2);
if( (rv = SSL_accept(ssl)) < 0 ){
sslerrx (ssl, "SSL_accept", rv); goto next; }
{ X509 * cert = SSL_get_peer_certificate (ssl);
/*EVP_PKEY * key = cert? X509_get_pubkey (cert): NULL;*/
fprintf (stdout, "=%ld", SSL_get_verify_result (ssl));
if( cert ) putchar (':'), subj_oneline (cert, stdout);
putchar ('\n');
}
while( (rv = SSL_read (ssl, buf, sizeof buf)) > 0 )
printf ("%d: %.*s\n", rv, rv, buf);
sslerrx (ssl, "SSL_read", rv);
next:
if( ssl ) SSL_free (ssl);
/**/
now = time(NULL); tm = localtime(&now);
printf ("- %s %u #%02d.%02d.%02d\n", inet_ntoa (sin.sin_addr),
ntohs (sin.sin_port), tm->tm_hour, tm->tm_min, tm->tm_sec);
closesocket (s2);
} while (1);
return 0;
}
When run with $port cert1.key cert1.pem CAbundle.pem and connected from client using cert2.key & cert2.pem this aborts the handshake with alert 48 unknown_ca and returns an error as desired:
+ 127.0.0.1 46765 #22.07.36
* SSL_accept return -1,1
140240689366696:error:14089086:SSL routines:ssl3_get_client_certificate:certificate verify failed:s3_srvr.c:3270:
- 127.0.0.1 46765 #22.07.36
HTH.
If you want a whitelist of specific client certificates, you can prepare an indexed list in memory when you initialize.
For example, you can use PEM_X509_INFO_read to read a concatenated file of all client certificates in PEM format. This will give you a STACK_OF(X509_INFO)* of certificates. The number of certificates can be found with sk_X509_INFO_num, and you can see each certificate at sk_X509_INFO_value(..)->x509.
Then, for example, you can simply build an in-memory index and qsort by compare_x509.
Now, when your verify callback is called, just do a bsearch on your index by compare_x509, and either the certificate is on your whitelist, or it isn't.
You can accept the match on the result of compare_x5099, or of course you could double-check by verifying the full certificate once the search finds a match in the index.

How to verify key length of a PEM certificate using openSSL functions

How do I verify the key length of a PEM certificate that is generated in this way:
# openssl genrsa -des3 -out server.key 1024
# openssl req -new -key server.key -out server.csr
# cp server.key server.key.org
# openssl rsa -in server.key.org -out server.key
# openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
What I need is a C function using procedures from OpenSSL, that performs validation on a PEM certificate (I use it for the lighttpd HTTPS server), and returns the length of the key stored in the certificate (in this case, 1024).
After some tweaking, I believe have found the right routines.
The following should get you started with exploring other OpenSSL routines, in case you need to handle other types of certificates (x509, pem).
Also read through your local x509.h and pem.h for structures and functions that will recover other information you're after.
/* Compile with 'gcc -Wall -lcrypto foo.c' or similar...
---------------------------------------------------------
$ ./a.out server.crt
Opened: server.crt
RSA Public Key: (1024 bit)
$ ./a.out server.key
ERROR: could not read x509 data from server.key
*/
#include <stdio.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
int main(int argc, char *argv[])
{
FILE *fp = NULL;
X509 *x509 = NULL;
EVP_PKEY *public_key = NULL;
fp = fopen(argv[1], "r");
if (fp) {
PEM_read_X509(fp, &x509, NULL, NULL);
fclose(fp);
if (x509) {
fprintf(stderr, "Opened PEM certificate file: %s\n", argv[1]);
/* do stuff with certificate... */
public_key = X509_get_pubkey(x509);
if (public_key) {
switch (public_key->type) {
case EVP_PKEY_RSA:
fprintf(stdout, "RSA Public Key: (%d bit)\n", BN_num_bits(public_key->pkey.rsa->n));
break;
default:
fprintf(stdout, "Unknown public key type? See OpenSSL documentation\n");
break;
}
EVP_PKEY_free(public_key);
}
X509_free(x509);
}
else {
fprintf(stderr, "ERROR: could not read x509 data from %s\n", argv[1]);
return EXIT_FAILURE;
}
}
else {
fprintf(stderr, "ERROR: could not open file!\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

Extract pem certificate information programmatically using openssl

Using the openssl command line is possible to extract, in a human readable mode, all the information contained in a .pem certificate; that is:
openssl x509 -noout -in <MyCertificate>.pem -text
What are the suitable steps in order to extract this information using the openssl API?
The X509_print_ex family of functions is your answer.
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
int main(int argc, char **argv)
{
X509 *x509;
BIO *i = BIO_new(BIO_s_file());
BIO *o = BIO_new_fp(stdout,BIO_NOCLOSE);
if((argc < 2) ||
(BIO_read_filename(i, argv[1]) <= 0) ||
((x509 = PEM_read_bio_X509_AUX(i, NULL, NULL, NULL)) == NULL)) {
return -1;
}
X509_print_ex(o, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
}
As additional information related to this question, in case of having a certificate with the DER format instead of PEM; it is also possible to extract the information in a human readable mode using the following code:
//Assuming that the DER certificate binary information is stored in
//a byte array (unsigned char) called "pData" whose size is "lenData"
X509* x509;
BIO* input = BIO_new_mem_buf((void*)pData, lenData);
//d2i_X509_bio: Decodes the binary DER certificate
//and parses it to a X509 structure
x509 = d2i_X509_bio(input, NULL);
if (x509 == NULL)
{
//Error in d2i_X509_bio
}
else
{
//"certificateFile" is the full path file
//where to store the certificate information
//in a human readable mode (instead of stdout)
FILE* fd = fopen(certificateFile, "w+");
BIO* output = BIO_new_fp(fd, BIO_NOCLOSE);
X509_print_ex(output, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
fclose(fd);
BIO_free_all(output);
}
BIO_free_all(input);

How to verify the password of a pkcs#12 certificate (.PXF) with openssl C API?

I have an .pxf (AFAIK PKCS#12) certificate. How can I confirm a given password for this certificate using the openssl C API?
One approach to finding answers like this is to find an OpenSSL utility that performs the same functionality as what you are trying to do. In this case, you can use the pkcs12 utility that comes with OpenSSL to verify the password.
The command to verify a pfx file is the following:
openssl pkcs12 -in mypfx.pfx -noout
With that information, you can then look at its source code ({openssl_src}/apps/pkcs12.c) to see how they do it.
The source code shows that it calls PKCS12_verify_mac to verify the password. First to verify that there is no password:
if( PKCS12_verify_mac(p12, NULL, 0) )
{
printf("PKCS12 has no password.\n");
}
And then if there is a password, verify it by passing it as an argument:
if( PKCS12_verify_mac(p12, password, -1) )
{
printf("PKCS12 password matches.\n");
}
OpenSSL also has demos for working with PKCS12 in openssl/demos/pkcs12. The pkread.c demo provides an example for parsing a pfx file with a password.
EVP_PKEY *pkey;
X509 *cert;
STACK_OF(X509) *ca = NULL;
if (!PKCS12_parse(p12, password, &pkey, &cert, &ca)) {
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
exit(1);
}
Full example, compiled with gcc -std=c99 verifypfx.c -o verifypfx -lcrypto:
#include <stdio.h>
#include <errno.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
int main(int argc, char *argv[])
{
const char *password = "mypassword";
PKCS12 *p12;
// Load the pfx file.
FILE *fp = fopen("mypfx.pfx", "rb");
if( fp == NULL ) { perror("fopen"); return 1; }
p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
OpenSSL_add_all_algorithms();
ERR_load_PKCS12_strings();
if( p12 == NULL ) { ERR_print_errors_fp(stderr); exit(1); }
// Note: No password is not the same as zero-length password. Check for both.
if( PKCS12_verify_mac(p12, NULL, 0) )
{
printf("PKCS12 has no password.\n");
}
else if( PKCS12_verify_mac(p12, password, -1) )
{
printf("PKCS12 password matches.\n");
}
else
{
printf("Password not correct.\n");
}
return 0;
}
Use PKCS12_verify_mac(). eg.
FILE* f = fopen("myfile.pfx", "rb");
PKCS12* p12 = d2i_PKCS12_fp(f, NULL);
fclose(f);
if (!PKCS12_verify_mac(p12, (char*)"mypassword", strlen("mypassword")))
{
// handle failure
}

Resources