diff --git a/.remote-sync.json b/.remote-sync.json new file mode 100644 index 0000000000000000000000000000000000000000..2d3e8f5b6c1d7e44caca6963a2e6402441eb06ce --- /dev/null +++ b/.remote-sync.json @@ -0,0 +1,14 @@ +{ + "transport": "scp", + "uploadOnSave": true, + "useAtomicWrites": true, + "deleteLocal": false, + "hostname": "ubuntu@115.146.95.37", + "ignore": [ + ".remote-sync.json", + ".git/**" + ], + "watch": [], + "keyfile": "~/Desktop/private.txt", + "passphrase": "j0nas13" +} \ No newline at end of file diff --git a/certexample b/certexample new file mode 100755 index 0000000000000000000000000000000000000000..133b4413ab264e369a804e79ee90672329bf6e0f Binary files /dev/null and b/certexample differ diff --git a/certexample.c b/certexample.c index f525abae5b3580cc348e3088a7a441c860751e4b..ab7dba11f5a4eb06162b95618e19b56559a40553 100644 --- a/certexample.c +++ b/certexample.c @@ -3,70 +3,173 @@ gcc -o certexample certexample.c -lssl -lcrypto */ #include <openssl/x509.h> +#include <openssl/asn1.h> +#include <openssl/crypto.h> +#include <openssl/asn1t.h> #include <openssl/x509v3.h> #include <openssl/bio.h> #include <openssl/pem.h> #include <openssl/err.h> #include <stdio.h> #include <string.h> +#include <assert.h> +#include <time.h> +#include <openssl/asn1_mac.h> -int main() -{ +const ASN1_TIME *X509_get0_notBefore(const X509 *x); +char** str_split(const char* a_str, const char a_delim); +char* concat(char *s1, char *s2); +char* get_basic_constraints(X509 *cert); +char* get_key_usage(X509 *cert); +char* compare_not_before(X509 *cert); +char* compare_not_after(X509 *cert); +char* get_SAN(X509 *cert); +char *get_domain_name(X509 *cert); +int matches_subject_alternative_name(const char *hostname, X509 *server_cert); +int get_public_key_length(X509 *cert); +int validate_basic_constraints(char* basic_constraints); +int validate_key_usage(char* key_usage); +int validate_key_length(int length); +int validate_CN(const char* hostname, char*cn); +int validate_wildcard_string(const char *hostname, char*hostname_with_wildcard); +int validate_CN_and_SAN(const char *url, X509 *cert); +int validate_not_before(X509 *cert); +int validate_not_after(X509 *cert); +int validate_certificate(const char *url, X509 *cert); - const char test_cert_example[] = "./cert-file2.pem"; - BIO *certificate_bio = NULL; - X509 *cert = NULL; - X509_NAME *cert_issuer = NULL; - X509_CINF *cert_inf = NULL; - STACK_OF(X509_EXTENSION) * ext_list; +int main(int argc, char **argv){ + int LINE_BUFFER=100; + char line[LINE_BUFFER]; - //initialise openSSL + FILE *csv_input = fopen(concat("./sample_certs/",argv[1]), "r"); + FILE *csv_output = fopen("output_test.csv" ,"w"); + + //for each line in the csv file, process each certificate OpenSSL_add_all_algorithms(); ERR_load_BIO_strings(); ERR_load_crypto_strings(); - //create BIO object to read certificate - certificate_bio = BIO_new(BIO_s_file()); + int n = 0; + while (fgets(line, LINE_BUFFER, csv_input) != NULL){ + + printf("CSV LINE # %d\n", n); + BIO *certificate_bio = NULL; + X509 *cert = NULL; + X509_NAME *cert_issuer = NULL; + X509_CINF *cert_inf = NULL; + STACK_OF(X509_EXTENSION) * ext_list; + certificate_bio = BIO_new(BIO_s_file()); + //here we are able to access each line + line[strlen(line)-1] = '\0'; + + + char **csv_row_elements = str_split(line, ','); + + printf("\tFILE: %s\n",csv_row_elements[0]); + printf("\tURL: %s\n",csv_row_elements[1]); + + + char *certificate_file = concat("./sample_certs/", csv_row_elements[0]); + + const char *url = csv_row_elements[1]; + //for some reason splitting keeps mututating the original string + char *unchanged_url = csv_row_elements[1]; + + FILE *fp = fopen(certificate_file, "r"); + + if (!(BIO_read_filename(certificate_bio, certificate_file))){ + fprintf(stderr, "Error in reading cert BIO filename"); + exit(EXIT_FAILURE); + } + + //load certiifcate + if (!(cert = PEM_read_bio_X509(certificate_bio, NULL, 0, NULL))){ + fprintf(stderr, "Error in loading certificate"); + exit(EXIT_FAILURE); + } + // printf ("\tBASIC CONSTRAINT: %s\n",get_basic_constraints(cert)); + // printf ("\tBASIC CONSTRAINT VALIDATION: %d\n",validate_basic_constraints(get_basic_constraints(cert))); + // + // // printf ("\tKEY USAGE: %s\n",get_key_usage(cert)); + // printf ("\tKEY USAGE VALIDATION: %d\n",validate_key_usage(get_key_usage(cert))); + // + // // printf ("\tKEY LENGTH BITS: %d\n",get_public_key_length(cert)); + // + // printf ("\tKEY LENGTH VALIDATION: %d\n",validate_key_length(get_public_key_length(cert))); + // + // // printf ("\tNot Before compared to Current: %s\n",compare_not_before(cert)); + // // printf ("\tNot After compared to Current: %s\n",compare_not_after(cert)); + // printf("\tNOT BEFORE VALIDATION %d\n", validate_not_before(cert)); + // printf("\tNOT AFTER VALIDATION %d\n", validate_not_after(cert)); + // // printf ("\tCommon Name: %s\n",get_domain_name(cert)); + // + // printf("\tCOMMON NAME AND SAN VALIDATION %d\n", validate_CN_and_SAN(url, cert)); + // printf ("%d", 0 || 1); + // printf("FINAL VALIDATION %d\n", validate_certificate(url, cert)); + // printf("\t%s\n", unchanged_url); + fprintf(csv_output,"%s,", csv_row_elements[0]); + fprintf(csv_output,"%s,", unchanged_url); + fprintf(csv_output,"%d\n", validate_certificate(url, cert)); + + // printf ("\tCOMMON NAME VALIDATION: %d\n",); + // printf ("\tMatches Subject Alternative Name Result: %d\n",); + // printf("URL: %s\n",url); + + // + // printf("URL: %s\n",url); + n++; - //Read certificate into BIO - if (!(BIO_read_filename(certificate_bio, test_cert_example))) - { - fprintf(stderr, "Error in reading cert BIO filename"); - exit(EXIT_FAILURE); - } - if (!(cert = PEM_read_bio_X509(certificate_bio, NULL, 0, NULL))) - { - fprintf(stderr, "Error in loading certificate"); - exit(EXIT_FAILURE); } + exit(0); +} +char* get_basic_constraints(X509 *cert){ - //cert contains the x509 certificate and can be used to analyse the certificate - - //********************* - // Example code of accessing certificate values - //********************* + X509_EXTENSION *ex = X509_get_ext(cert, X509_get_ext_by_NID(cert, NID_basic_constraints, -1)); - cert_issuer = X509_get_issuer_name(cert); - char issuer_cn[256] = "Issuer CN NOT FOUND"; - X509_NAME_get_text_by_NID(cert_issuer, NID_commonName, issuer_cn, 256); - printf("Issuer CommonName:%s\n", issuer_cn); + ASN1_OBJECT *obj = X509_EXTENSION_get_object(ex); + + char buff[1024]; + OBJ_obj2txt(buff, 1024, obj, 0); + //printf("Extension: %s, ", buff); + + BUF_MEM *bptr = NULL; + char *buf = NULL; + + BIO *bio = BIO_new(BIO_s_mem()); + + X509V3_EXT_print(bio, ex, 0, 0); + + + BIO_flush(bio); + BIO_get_mem_ptr(bio, &bptr); + + //bptr->data is not NULL terminated - add null character + buf = (char *)malloc((bptr->length + 1) * sizeof(char)); + memcpy(buf, bptr->data, bptr->length); + buf[bptr->length] = '\0'; + BIO_flush(bio); + //Can print or parse value + + return buf; +} +char* get_key_usage(X509 *cert){ + + X509_EXTENSION *ex = X509_get_ext(cert, X509_get_ext_by_NID(cert, NID_ext_key_usage, -1)); - //List of extensions available at https://www.openssl.org/docs/man1.1.0/crypto/X509_REVOKED_get0_extensions.html - //Need to check extension exists and is not null - X509_EXTENSION *ex = X509_get_ext(cert, X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1)); ASN1_OBJECT *obj = X509_EXTENSION_get_object(ex); + char buff[1024]; OBJ_obj2txt(buff, 1024, obj, 0); - printf("Extension:%s\n", buff); + //printf("Extension: %s, ", buff); BUF_MEM *bptr = NULL; char *buf = NULL; BIO *bio = BIO_new(BIO_s_mem()); - if (!X509V3_EXT_print(bio, ex, 0, 0)) - { - fprintf(stderr, "Error in reading extensions"); - } + + X509V3_EXT_print(bio, ex, 0, 0); + + BIO_flush(bio); BIO_get_mem_ptr(bio, &bptr); @@ -76,15 +179,273 @@ int main() buf[bptr->length] = '\0'; //Can print or parse value - printf("%s\n", buf); - //********************* - // End of Example code - //********************* + return buf; +} +int get_public_key_length(X509 *cert){ - X509_free(cert); - BIO_free_all(certificate_bio); - BIO_free_all(bio); - free(buf); - exit(0); -} \ No newline at end of file + EVP_PKEY *public_key = X509_get_pubkey(cert); + + RSA *rsa_key = EVP_PKEY_get1_RSA(public_key); + + int key_length_bytes = RSA_size(rsa_key); + + return (key_length_bytes*8); + +} +char* get_domain_name(X509 *cert){ + X509_NAME *x509_name = X509_get_subject_name(cert); + char *cert_cn = X509_NAME_oneline(x509_name, 0, 0); + cert_cn =(cert_cn+1); + char **chain = str_split(cert_cn, '/'); + char *domain = chain[5]; + + char **domain_clean = str_split(domain, '='); + + return domain_clean[1]; +} +char* compare_not_before(X509 *cert){ + int day, sec; + const ASN1_TIME *not_before = X509_get_notBefore(cert); + + if (!ASN1_TIME_diff(&day, &sec, NULL, not_before)){ + /* Invalid time format */ + printf("what the fuck do now\n"); + } + + if (day > 0 || sec > 0){ + return "Later"; + } + else if (day < 0 || sec < 0){ + return "Sooner"; + } + else{ + return "Same"; + } + return "invalid?"; +} +char* compare_not_after(X509 *cert){ + int day, sec; + const ASN1_TIME *not_after = X509_get_notAfter(cert); + + if (!ASN1_TIME_diff(&day, &sec, NULL, not_after)){ + /* Invalid time format */ + printf("what the fuck do now\n"); + } + + if (day > 0 || sec > 0){ + return "Later"; + } + else if (day < 0 || sec < 0){ + return "Sooner"; + } + else{ + return "Same"; + } + + return "invalid?"; +} +char* concat(char *s1, char *s2){ + //concats two strings + char *result = malloc(strlen(s1)+strlen(s2)+1); + strcpy(result, s1); + strcat(result, s2); + return result; + +} +char** str_split(const char* a_str, const char a_delim){ + char** result = 0; + size_t count = 0; + char* tmp = (char *)a_str; + char* copy = (char *)a_str; + char* last_comma = 0; + char delim[2]; + delim[0] = a_delim; + delim[1] = 0; + + /* Count how many elements will be extracted. */ + while (*tmp){ + if (a_delim == *tmp){ + count++; + last_comma = tmp; + } + tmp++; + } + /* Add space for trailing token. */ + count += last_comma < (a_str + strlen(a_str) - 1); + /* Add space for terminating null string so caller + knows where the list of returned strings ends. */ + count++; + result = malloc(sizeof(char*) * count); + if (result){ + size_t idx = 0; + char* token = strtok(copy, delim); + + while (token){ + assert(idx < count); + *(result + idx++) = strdup(token); + token = strtok(0, delim); + } + assert(idx == count - 1); + *(result + idx) = 0; + } + return result; +} +int matches_subject_alternative_name(const char *hostname, X509 *server_cert) { + + + int i; + int san_names_nb = -1; + STACK_OF(GENERAL_NAME) *san_names = NULL; + + // Try to extract the names within the SAN extension from the certificate + san_names = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); + + + if (san_names == NULL) { + + return 0; + } + + san_names_nb = sk_GENERAL_NAME_num(san_names); + + // Check each name within the extension + for (i=0; i<san_names_nb; i++) { + const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); + + if (current_name->type == GEN_DNS) { + // Current name is a DNS name, let's check it + char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName); + printf ("\t%s\n", dns_name); + // Make sure there isn't an embedded NUL character in the DNS name + if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { + + return 0; + break; + } + else { // Compare expected hostname with the DNS name + + + + if (validate_wildcard_string(hostname, dns_name) == 1) { + + return 1; + + break; + } + } + } + } + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + return 0; +} +int validate_key_usage(char* key_usage){ + + //key usage may be a bunch of strings, need to get first one if this is the case + + if(strlen("TLS Web Server Authentication")!=strlen(key_usage)){ + char **key_usage_clean = str_split(key_usage, ','); + if(strcmp(key_usage_clean[0], "TLS Web Server Authentication") == 0){ + return 1; + } + + return 0; + + + } + + else{ + if(strcmp(key_usage, "TLS Web Server Authentication") == 0){ + return 1; + } + return 0; + } + +} +int validate_basic_constraints(char* basic_constraints){ + + if(strcmp(basic_constraints, "CA:FALSE")==0){ + // printf("\tBASIC CONSTRAINT PASS\n"); + return 1; + } + return 0; +} + +int validate_wildcard_string(const char *hostname, char*hostname_with_wildcard){ + char **hostname_with_wildcard_split = str_split(hostname_with_wildcard, '.'); + + char **hostname_split = str_split(hostname, '.'); + + const char *hostname_with_wildcard_right = hostname_with_wildcard_split[1]; + const char *hostname_split_right = hostname_split[1]; + + if(strcasecmp(hostname_split_right, hostname_with_wildcard_right)==0){ + printf("\t\tWILDCARD FUNCTION\t\t%s == %s\n", hostname_split_right, hostname_with_wildcard_right); + return 1; + } + return 0; +} +int validate_key_length(int length){ + if (length==2048){ + return 1; + } + return 0; +} +int validate_CN(const char* hostname, char*cn){ + if(cn[0]=='*'){ + return (validate_wildcard_string(hostname, cn)); + } + else{ + if(strcasecmp(cn, hostname)==0){ + return 1; + } + else{ + return 0; + } + } + return 0; +} +int validate_CN_and_SAN(const char *url, X509 *cert){ + + if(validate_CN(url, get_domain_name(cert)) || matches_subject_alternative_name(url, cert)){ + return 1; + } + else{return 0;} +} +int validate_not_before(X509 *cert){ + + if(strcmp(compare_not_before(cert), "Sooner")==0){ + return 1; + } + return 0; +} +int validate_not_after(X509 *cert){ + + if(strcmp(compare_not_after(cert), "Later")==0){ + return 1; + } + return 0; +} +int validate_certificate(const char *url, X509 *cert){ + // printf ("\tBASIC CONSTRAINT VALIDATION: %d\n",validate_basic_constraints(get_basic_constraints(cert))); + // printf ("\tKEY USAGE VALIDATION: %d\n",validate_key_usage(get_key_usage(cert))); + // printf ("\tKEY LENGTH VALIDATION: %d\n",validate_key_length(get_public_key_length(cert))); + // printf("\tNOT BEFORE VALIDATION %d\n", validate_not_before(cert)); + // printf("\tNOT AFTER VALIDATION %d\n", validate_not_after(cert)); + // printf("\tCOMMON NAME AND SAN VALIDATION %d\n", validate_CN_and_SAN(url, cert)); + + int a = validate_basic_constraints(get_basic_constraints(cert)); + int b = validate_key_usage(get_key_usage(cert)); + int c = validate_key_length(get_public_key_length(cert)); + int d = validate_not_before(cert); + int e = validate_not_after(cert); + // printf("%s\n", url); + int f = validate_CN_and_SAN(url, cert); + // & b & c & d & e & f + if(a&b & c & d & e& f){ + return 1; + } + else{ + return 0; + } +} diff --git a/makefile b/makefile new file mode 100644 index 0000000000000000000000000000000000000000..f62ae4ab0424f650456b5a46205d6e7776f81b01 --- /dev/null +++ b/makefile @@ -0,0 +1,3 @@ +#!/bin/bash +all: certexample.c + gcc -I /usr/local/opt/openssl/include -L /usr/local/opt/openssl/lib -o certexample certexample.c -lssl -lcrypto diff --git a/output_test.csv b/output_test.csv new file mode 100644 index 0000000000000000000000000000000000000000..7b25d86d602b3f7f1458ff4dff09f48859ffb099 --- /dev/null +++ b/output_test.csv @@ -0,0 +1,13 @@ +testone.crt,www.example.com,0 +testtwo.crt,www.mydomain.com,1 +testthree.crt,www.test.com,0 +testfour.crt,www.oldsite.com,0 +testfive.crt,www.unimelb.com,0 +testsix.crt,www.codetest.com,0 +testseven.crt,www.example.com,1 +testeight.crt,www.comp30023.com,1 +testnine.crt,www.certtest.com,1 +testten.crt,www.victoria.com,1 +testeleven.crt,freeca.com,0 +testtwo.crt,www.example.com,0 +testseven.crt,example.com,0 diff --git a/sample_certs/sample_input_single.csv b/sample_certs/sample_input_single.csv new file mode 100644 index 0000000000000000000000000000000000000000..9239412f17ebe17cb8356c2f64a2cba45d93bd19 --- /dev/null +++ b/sample_certs/sample_input_single.csv @@ -0,0 +1 @@ +testeight.crt,www.comp30023.com