Thursday, October 1, 2009

Nostalgic: SSL Certification Checker

While digging up old stuff I found a C program for checking if a SSL certificate is expired.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define HOSTNAME "example.com"

int main() {
int sock; /* socket descriptor */
struct hostent *hp; /* for resolving host */
struct servent *sp; /* for resolving port */
struct sockaddr_in sin; /* for creating socket */
SSL *ssl; /* for ssl ;) */
SSL_CTX *ctx; /* context for ssl */
SSL_METHOD *(*ssl_method)() = SSLv23_client_method;
X509 *cert; /* certificate we hope to get */
char certinfo[2048]; /* store output of program */

/* resolve the hostname and verify the port (should be 443) */
hp = gethostbyname(HOSTNAME);
sp = getservbyname("https", "tcp");

/* create a socket */
bzero((char*)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr,(char*)&sin.sin_addr, hp->h_length);
sin.sin_port = sp->s_port;
sock = socket(AF_INET, SOCK_STREAM, 0);

/* connect */
printf("connecting to %s\n", HOSTNAME);
connect(sock, (struct sockaddr*)&sin, sizeof(sin));

/* openssl voodoo */
SSL_library_init();
SSL_load_error_strings();
ctx = SSL_CTX_new(ssl_method());
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
SSL_connect(ssl);
cert = NULL;

/* pull up the server's certificate */
cert = SSL_get_peer_certificate(ssl);

/* if we have a certificate continue */
if(cert != NULL) {
char buf[256], *p;

/* certinfo will be the output of the program */
strncpy(certinfo, "\nsubject:", sizeof(certinfo));

/* store the x509 info in buf */
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));

/* tokenize the string we got above which is delimited by '/' */
p = strtok(buf, "/");
while(p != NULL) {
/* add everything to certinfo, which we'll dump in the end,
* and try to make certinfo look pretty
* */
strncat(certinfo, "\n\t", sizeof(certinfo));
strncat(certinfo, p, sizeof(certinfo));
/* continue tokenizing until end of string */
p = strtok(NULL, "/");
}

/* this is essentially the same as above except instead of the cert's
* subject data we're now working with the issuer data
*/
strncat(certinfo, "\nissuer:", sizeof(certinfo));
X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
p = strtok(buf, "/");
while(p != NULL) {
strncat(certinfo, "\n\t", sizeof(certinfo));
strncat(certinfo, p, sizeof(certinfo));
p = strtok(NULL, "/");
}
strncat(certinfo, "\n", sizeof(certinfo));

/* expiration check */
if(X509_cmp_current_time (X509_get_notBefore (cert)) >= 0)
strncat(certinfo, "certificate is not yet valid\n", sizeof(certinfo));
else if(X509_cmp_current_time (X509_get_notAfter (cert)) <= 0)
strncat(certinfo, "certficate has already expired\n", sizeof(certinfo));
else
strncat(certinfo, "certificate expiration is valid\n", sizeof(certinfo));

/* finally dump the info */
printf("%s", certinfo);
}
/* if getting the certificate failed */
else {
printf("couldn't pull certificate.\ntoo lazy to include debugging info.\nsorry.\n");
}

return 0;
}


I didn't bother testing it, but I bet it would still work. (Requires openssl libraries to compile.)