/* * Copyright (C) 2003 Nikos Mavroyanopoulos * Copyright (C) 2004 Free Software Foundation * * This file is part of GNUTLS. * * GNUTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GNUTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #ifdef ENABLE_PKI #include #include #include #include #include #include "certtool-gaa.h" #include #include #include static void print_crl_info( gnutls_x509_crl crl, FILE* out, int all); int generate_prime(int bits); void pkcs7_info( void); void pkcs12_info( void); void generate_pkcs12( void); void verify_chain(void); void verify_crl(void); gnutls_x509_privkey load_private_key(int mand); gnutls_x509_crq load_request(void); gnutls_x509_privkey load_ca_private_key(void); gnutls_x509_crt load_ca_cert(void); gnutls_x509_crt load_cert(int mand); void certificate_info( void); void crl_info( void); void privkey_info( void); static void print_certificate_info( gnutls_x509_crt crt, FILE* out, unsigned int); static void gaa_parser(int argc, char **argv); void generate_self_signed( void); void generate_request(void); gnutls_x509_crt* load_cert_list(int mand, int *size); static gaainfo info; FILE* outfile; FILE* infile; int in_cert_format; int out_cert_format; unsigned char buffer[50*1024]; const int buffer_size = sizeof(buffer); static void tls_log_func( int level, const char* str) { fprintf(stderr, "|<%d>| %s", level, str); } int main(int argc, char** argv) { gaa_parser(argc, argv); return 0; } static void read_crt_set( gnutls_x509_crt crt, const char* input_str, const char* oid) { char input[128]; int ret; fputs( input_str, stderr); fgets( input, sizeof(input), stdin); if (strlen(input)==1) /* only newline */ return; ret = gnutls_x509_crt_set_dn_by_oid(crt, oid, 0, input, strlen(input)-1); if (ret < 0) { fprintf(stderr, "set_dn: %s\n", gnutls_strerror(ret)); exit(1); } } static void read_crq_set( gnutls_x509_crq crq, const char* input_str, const char* oid) { char input[128]; int ret; fputs( input_str, stderr); fgets( input, sizeof(input), stdin); if (strlen(input)==1) /* only newline */ return; ret = gnutls_x509_crq_set_dn_by_oid(crq, oid, 0, input, strlen(input)-1); if (ret < 0) { fprintf(stderr, "set_dn: %s\n", gnutls_strerror(ret)); exit(1); } } static int read_int( const char* input_str) { char input[128]; fputs( input_str, stderr); fgets( input, sizeof(input), stdin); if (strlen(input)==1) /* only newline */ return 0; return atoi(input); } static const char* read_str( const char* input_str) { static char input[128]; int len; fputs( input_str, stderr); if (fgets( input, sizeof(input), stdin) == NULL) return NULL; len = strlen(input); if ( (len > 0) && (input[len-1] == '\n') ) input[len-1] = 0; if (input[0] == 0) return NULL; return input; } static int read_yesno( const char* input_str) { char input[128]; fputs( input_str, stderr); fgets( input, sizeof(input), stdin); if (strlen(input)==1) /* only newline */ return 0; if (input[0] == 'y' || input[0] == 'Y') return 1; return 0; } static gnutls_x509_privkey generate_private_key_int( void) { gnutls_x509_privkey key; int ret, key_type; const char* msg; if (info.dsa) { msg = "DSA"; key_type = GNUTLS_PK_DSA; if (info.bits > 1024) { fprintf(stderr, "The DSA algorithm cannot be used with primes over 1024 bits.\n"); exit(1); } } else { msg = "RSA"; key_type = GNUTLS_PK_RSA; } if (info.privkey) return load_private_key(1); ret = gnutls_x509_privkey_init(&key); if (ret < 0) { fprintf(stderr, "privkey_init: %s\n", gnutls_strerror(ret)); exit(1); } fprintf(stderr, "Generating a %d bit %s private key...\n", info.bits, msg); ret = gnutls_x509_privkey_generate( key, key_type, info.bits, 0); if (ret < 0) { fprintf(stderr, "privkey_generate: %s\n", gnutls_strerror(ret)); exit(1); } return key; } static void print_key_usage( unsigned int x, FILE* out) { if (x&GNUTLS_KEY_DIGITAL_SIGNATURE) fprintf(out,"\t\tDigital signature.\n"); if (x&GNUTLS_KEY_NON_REPUDIATION) fprintf(out,"\t\tNon repudiation.\n"); if (x&GNUTLS_KEY_KEY_ENCIPHERMENT) fprintf(out,"\t\tKey encipherment.\n"); if (x&GNUTLS_KEY_DATA_ENCIPHERMENT) fprintf(out,"\t\tData encipherment.\n"); if (x&GNUTLS_KEY_KEY_AGREEMENT) fprintf(out,"\t\tKey agreement.\n"); if (x&GNUTLS_KEY_KEY_CERT_SIGN) fprintf(out,"\t\tCertificate signing.\n"); if (x&GNUTLS_KEY_CRL_SIGN) fprintf(out,"\t\tCRL signing.\n"); if (x&GNUTLS_KEY_ENCIPHER_ONLY) fprintf(out,"\t\tKey encipher only.\n"); if (x&GNUTLS_KEY_DECIPHER_ONLY) fprintf(out,"\t\tKey decipher only.\n"); } static void print_private_key( gnutls_x509_privkey key) { int ret; size_t size; if (!key) return; if (!info.pkcs8) { size = sizeof(buffer); ret = gnutls_x509_privkey_export( key, out_cert_format, buffer, &size); if (ret < 0) { fprintf(stderr, "privkey_export: %s\n", gnutls_strerror(ret)); exit(1); } } else { unsigned int flags; const char* pass; if (info.export) flags = GNUTLS_PKCS_USE_PKCS12_RC2_40; else flags = GNUTLS_PKCS_USE_PKCS12_3DES; if ((pass=read_pass("Enter password: ")) == NULL) flags = GNUTLS_PKCS_PLAIN; size = sizeof(buffer); ret = gnutls_x509_privkey_export_pkcs8( key, out_cert_format, pass, flags, buffer, &size); if (ret < 0) { fprintf(stderr, "privkey_export_pkcs8: %s\n", gnutls_strerror(ret)); exit(1); } } fwrite(buffer, 1, size, outfile); } void generate_private_key( void) { gnutls_x509_privkey key; fprintf(stderr, "Generating a private key...\n"); key = generate_private_key_int(); print_private_key( key); gnutls_x509_privkey_deinit(key); } gnutls_x509_crt generate_certificate( gnutls_x509_privkey *ret_key) { gnutls_x509_crt crt; gnutls_x509_privkey key = NULL; int size, serial; int days, result, ca_status; const char* str; int vers = 3; /* the default version in the certificate */ unsigned int usage = 0, server; gnutls_x509_crq crq; /* request */ size = gnutls_x509_crt_init(&crt); if (size < 0) { fprintf(stderr, "crt_init: %s\n", gnutls_strerror(size)); exit(1); } crq = load_request(); if (crq == NULL) { key = load_private_key(1); fprintf(stderr, "Please enter the details of the certificate's distinguished name. " "Just press enter to ignore a field.\n"); read_crt_set( crt, "Country name (2 chars): ", GNUTLS_OID_X520_COUNTRY_NAME); read_crt_set( crt, "Organization name: ", GNUTLS_OID_X520_ORGANIZATION_NAME); read_crt_set( crt, "Organizational unit name: ", GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME); read_crt_set( crt, "Locality name: ", GNUTLS_OID_X520_LOCALITY_NAME); read_crt_set( crt, "State or province name: ", GNUTLS_OID_X520_LOCALITY_NAME); read_crt_set( crt, "Common name: ", GNUTLS_OID_X520_COMMON_NAME); fprintf(stderr, "This field should not be used in new certificates.\n"); read_crt_set( crt, "E-mail: ", GNUTLS_OID_PKCS9_EMAIL); result = gnutls_x509_crt_set_key( crt, key); if (result < 0) { fprintf(stderr, "set_key: %s\n", gnutls_strerror(result)); exit(1); } } else { result = gnutls_x509_crt_set_crq( crt, crq); if (result < 0) { fprintf(stderr, "set_crq: %s\n", gnutls_strerror(result)); exit(1); } } serial = read_int( "Enter the certificate's serial number (decimal): "); buffer[3] = serial & 0xff; buffer[2] = (serial >> 8) & 0xff; buffer[1] = (serial >> 16) & 0xff; buffer[0] = 0; result = gnutls_x509_crt_set_serial( crt, buffer, 4); if (result < 0) { fprintf(stderr, "serial: %s\n", gnutls_strerror(result)); exit(1); } fprintf(stderr, "\n\nActivation/Expiration time.\n"); gnutls_x509_crt_set_activation_time( crt, time(NULL)); do { days = read_int( "The generated certificate will expire in (days): "); } while( days==0); result = gnutls_x509_crt_set_expiration_time( crt, time(NULL)+days*24*60*60); if (result < 0) { fprintf(stderr, "serial: %s\n", gnutls_strerror(result)); exit(1); } fprintf(stderr, "\n\nExtensions.\n"); ca_status = read_yesno( "Does the certificate belong to an authority? (Y/N): "); result = gnutls_x509_crt_set_ca_status( crt, ca_status); if (result < 0) { fprintf(stderr, "ca_status: %s\n", gnutls_strerror(result)); exit(1); } server = read_yesno( "Is this a web server certificate? (Y/N): "); if (server != 0) { str = read_str( "Enter the dnsName of the subject of the certificate: "); if (str != NULL) { result = gnutls_x509_crt_set_subject_alternative_name( crt, GNUTLS_SAN_DNSNAME, str); if (result < 0) { fprintf(stderr, "subject_alt_name: %s\n", gnutls_strerror(result)); exit(1); } } } else { str = read_str( "Enter the e-mail of the subject of the certificate: "); if (str != NULL) { result = gnutls_x509_crt_set_subject_alternative_name( crt, GNUTLS_SAN_RFC822NAME, str); if (result < 0) { fprintf(stderr, "subject_alt_name: %s\n", gnutls_strerror(result)); exit(1); } } } if (!ca_status || server) { int pk; const char* msg1, *msg2; if (server) msg1 = "Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (Y/N): "; else msg1 = "Will the certificate be used for signing (required for TLS)? (Y/N): "; if (server) msg2 = "Will the certificate be used for encryption (RSA ciphersuites)? (Y/N): "; else msg2 = "Will the certificate be used for encryption (not required for TLS)? (Y/N): "; pk = gnutls_x509_crt_get_pk_algorithm( crt, NULL); if (pk != GNUTLS_PK_DSA) { /* DSA keys can only sign. */ result = read_yesno( msg1); if (result) usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; result = read_yesno( msg2); if (result) usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; } else usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; } if (ca_status) { result = read_yesno( "Will the certificate be used to sign other certificates? (Y/N): "); if (result) usage |= GNUTLS_KEY_KEY_CERT_SIGN; result = read_yesno( "Will the certificate be used to sign CRLs? (Y/N): "); if (result) usage |= GNUTLS_KEY_CRL_SIGN; } if (usage != 0) { result = gnutls_x509_crt_set_key_usage( crt, usage); if (result < 0) { fprintf(stderr, "key_usage: %s\n", gnutls_strerror(result)); exit(1); } } /* Version. */ result = gnutls_x509_crt_set_version( crt, vers); if (result < 0) { fprintf(stderr, "set_version: %s\n", gnutls_strerror(result)); exit(1); } /* Subject Key ID. */ size = sizeof(buffer); result = gnutls_x509_crt_get_key_id(crt, 0, buffer, &size); if (result >= 0) { result = gnutls_x509_crt_set_subject_key_id( crt, buffer, size); if (result < 0) { fprintf(stderr, "set_subject_key_id: %s\n", gnutls_strerror(result)); exit(1); } } *ret_key = key; return crt; } gnutls_x509_crl generate_crl( void) { gnutls_x509_crl crl; gnutls_x509_crt * crts; int size; int days, result, i; int vers = 2; /* the default version in the CRL */ result = gnutls_x509_crl_init(&crl); if (result < 0) { fprintf(stderr, "crl_init: %s\n", gnutls_strerror(result)); exit(1); } crts = load_cert_list(1, &size); for (i=0;i= 0) { print = printable; for (i = 0; i < serial_size; i++) { sprintf(print, "%.2x ", (unsigned char) serial[i]); print += 3; } fprintf(out, "Serial Number (hex): %s\n", printable); } /* Subject */ dn_size = sizeof(dn); ret = gnutls_x509_crt_get_dn(crt, dn, &dn_size); if (ret >= 0) fprintf(out, "Subject: %s\n", dn); else fprintf(stderr, "get_issuer_dn: %s\n", gnutls_strerror(ret)); /* Issuer */ if (all) { dn_size = sizeof(dn); ret = gnutls_x509_crt_get_issuer_dn(crt, dn, &dn_size); if (ret >= 0) fprintf(out, "Issuer: %s\n", dn); else fprintf(stderr, "get_issuer_dn: %s\n", gnutls_strerror(ret)); /* signature algorithm */ fprintf(out, "Signature Algorithm: "); ret = gnutls_x509_crt_get_signature_algorithm(crt); cprint = get_sign_algorithm( ret); fprintf(out, "%s\n", cprint); } /* Validity */ fprintf(out, "Validity:\n"); tim = gnutls_x509_crt_get_activation_time(crt); fprintf(out, "\tNot Before: %s", ctime(&tim)); tim = gnutls_x509_crt_get_expiration_time(crt); fprintf(out, "\tNot After: %s", ctime(&tim)); /* Public key algorithm */ fprintf(out, "Subject Public Key Info:\n"); ret = gnutls_x509_crt_get_pk_algorithm(crt, NULL); fprintf(out, "\tPublic Key Algorithm: "); cprint = get_pk_algorithm( ret); fprintf(out, "%s\n", cprint); fprintf(out, "\nX.509 Extensions:\n"); /* subject alternative name */ for (i = 0; !(ret < 0); i++) { size = sizeof(buffer); ret = gnutls_x509_crt_get_subject_alt_name(crt, i, buffer, &size, &critical); if (i==0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { fprintf(out, "\tSubject Alternative name:"); if (critical) fprintf(out, " (critical)"); fprintf(out, "\n"); } if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { fprintf(out, "\t\tFound unsupported alternative name.\n"); } else switch (ret) { case GNUTLS_SAN_DNSNAME: fprintf(out, "\t\tDNSname: %s\n", buffer); break; case GNUTLS_SAN_RFC822NAME: fprintf(out, "\t\tRFC822name: %s\n", buffer); break; case GNUTLS_SAN_URI: fprintf(out, "\t\tURI: %s\n", buffer); break; case GNUTLS_SAN_IPADDRESS: fprintf(out, "\t\tIPAddress: %s\n", buffer); break; } } /* CRL dist points. */ ret = 0; for (i = 0; !(ret < 0); i++) { size = sizeof(buffer); ret = gnutls_x509_crt_get_crl_dist_points(crt, i, buffer, &size, NULL, &critical); if (i==0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { fprintf(out, "\tCRL Distribution points:"); if (critical) fprintf(out, " (critical)"); fprintf(out, "\n"); } if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { fprintf(out, "\t\tError decoding: %s\n", gnutls_strerror(ret)); } else switch (ret) { case GNUTLS_SAN_DNSNAME: fprintf(out, "\t\tDNSname: %s\n", buffer); break; case GNUTLS_SAN_RFC822NAME: fprintf(out, "\t\tRFC822name: %s\n", buffer); break; case GNUTLS_SAN_URI: fprintf(out, "\t\tURI: %s\n", buffer); break; case GNUTLS_SAN_IPADDRESS: fprintf(out, "\t\tIPAddress: %s\n", buffer); break; } } /* check for basicConstraints */ ret = gnutls_x509_crt_get_ca_status( crt, &critical); if (ret >= 0) { fprintf(out, "\tBasic Constraints:"); if (critical) fprintf(out, " (critical)"); fprintf(out, "\n"); if (ret==0) fprintf(out, "\t\tCA:FALSE\n"); else fprintf(out, "\t\tCA:TRUE\n"); } /* Key Usage. */ ret = gnutls_x509_crt_get_key_usage( crt, &key_usage, &critical); if (ret >= 0) { fprintf(out, "\tKey usage: %s\n", critical?"(critical)":""); print_key_usage(key_usage, out); } /* Subject Key ID */ size = sizeof(buffer); ret = gnutls_x509_crt_get_subject_key_id(crt, buffer, &size, &critical); if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { fprintf(out, "Error getting subject key id: %s\n", gnutls_strerror(ret)); } if (ret >= 0) { print = printable; for (i = 0; i < size; i++) { sprintf(print, "%.2x ", (unsigned char) buffer[i]); print += 3; } fprintf(out, "\tSubject Key ID: %s\n\t\t%s\n", critical?"(critical)":"", printable); } /* other extensions: */ indx = 0; ret = 0; for (i = 0; !(ret < 0); i++) { size = sizeof(oid); ret = gnutls_x509_crt_get_extension_oid( crt, i, oid, &size); if (ret >= 0) { if (known_oid( oid)) continue; if (strcmp( oid, old_oid) == 0) { indx++; } else { indx = 0; } fprintf( out, "\t%s: ", oid); size = sizeof(buffer); ret = gnutls_x509_crt_get_extension_by_oid( crt, oid, indx, buffer, &size, &critical); if (ret >= 0) { if (critical) fprintf(out, "(critical)\n"); else fprintf(out, "\n"); print = printable; for (j = 0; j < size; j++) { sprintf(print, "%.2x", (unsigned char) buffer[j]); print += 2; } fprintf(out, "\t\tDER Data: %s\n", printable); } ret = 0; strcpy( old_oid, oid); } } /* fingerprint */ fprintf( out, "\nOther information:\n"); if (all) { size = sizeof(buffer); if ((ret=gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_MD5, buffer, &size)) < 0) { fprintf(out, "Error in fingerprint calculation: %s\n", gnutls_strerror(ret)); } else { print = printable; for (i = 0; i < size; i++) { sprintf(print, "%.2x ", (unsigned char) buffer[i]); print += 3; } fprintf(out, "\tFingerprint: %s\n", printable); } } size = sizeof(buffer); if ((ret=gnutls_x509_crt_get_key_id(crt, 0, buffer, &size)) < 0) { fprintf(out, "Error in key id calculation: %s\n", gnutls_strerror(ret)); } else { print = printable; for (i = 0; i < size; i++) { sprintf(print, "%.2x ", (unsigned char) buffer[i]); print += 3; } fprintf(out, "\tPublic Key ID: %s\n", printable); } fprintf(out, "\n"); if (out==stderr) /* interactive */ if (read_yesno( "Is the above information ok? (Y/N): ")==0) { exit(1); } } static void print_crl_info( gnutls_x509_crl crl, FILE* out, int all) { int ret, rc; time_t tim; unsigned int i, j; char serial[128]; size_t serial_size = sizeof(serial), dn_size; char printable[256]; char *print, dn[256]; const char* cprint; fprintf(out, "CRL information:\n"); fprintf(out, "Version: %d\n", gnutls_x509_crl_get_version(crl)); /* Issuer */ if (all) { dn_size = sizeof(dn); ret = gnutls_x509_crl_get_issuer_dn(crl, dn, &dn_size); if (ret >= 0) fprintf(out, "Issuer: %s\n", dn); fprintf(out, "Signature Algorithm: "); ret = gnutls_x509_crl_get_signature_algorithm(crl); cprint = get_sign_algorithm( ret); fprintf(out, "%s\n", cprint); } /* Validity */ fprintf(out, "Update dates:\n"); tim = gnutls_x509_crl_get_this_update(crl); fprintf(out, "\tIssued at: %s", ctime(&tim)); tim = gnutls_x509_crl_get_next_update(crl); fprintf(out, "\tNext at: %s", ctime(&tim)); fprintf(out, "\n"); /* Count the certificates. */ rc = gnutls_x509_crl_get_crt_count( crl); fprintf(out, "Revoked certificates: %d\n", rc); for (j=0;j<(unsigned int)rc;j++) { /* serial number */ serial_size = sizeof(serial); ret = gnutls_x509_crl_get_crt_serial(crl, j, serial, &serial_size, &tim); if (ret < 0) { fprintf(stderr, "error: %s\n", gnutls_strerror(ret)); } else { print = printable; for (i = 0; i < serial_size; i++) { sprintf(print, "%.2x ", (unsigned char) serial[i]); print += 3; } fprintf(out, "\tCertificate SN: %s\n", printable); fprintf(out, "\tRevoked at: %s\n", ctime( &tim)); } } } void crl_info() { gnutls_x509_crl crl; int ret; size_t size; gnutls_datum pem; size = fread( buffer, 1, sizeof(buffer)-1, infile); buffer[size] = 0; gnutls_x509_crl_init(&crl); pem.data = buffer; pem.size = size; ret = gnutls_x509_crl_import(crl, &pem, in_cert_format); if (ret < 0) { fprintf(stderr, "Decoding error: %s\n", gnutls_strerror(ret)); exit(1); } print_crl_info( crl, outfile, 1); size = sizeof(buffer); ret = gnutls_x509_crl_export(crl, GNUTLS_X509_FMT_PEM, buffer, &size); if (ret < 0) { fprintf(stderr, "Encoding error: %s\n", gnutls_strerror(ret)); exit(1); } fprintf(outfile, "\n%s\n", buffer); } void privkey_info( void) { gnutls_x509_privkey key; size_t size; int ret; unsigned int i; gnutls_datum pem; char printable[256]; char *print; const char* cprint; const char* pass; size = fread( buffer, 1, sizeof(buffer)-1, infile); buffer[size] = 0; gnutls_x509_privkey_init(&key); pem.data = buffer; pem.size = size; if (!info.pkcs8) { ret = gnutls_x509_privkey_import(key, &pem, in_cert_format); } else { pass = read_pass("Enter password: "); ret = gnutls_x509_privkey_import_pkcs8(key, &pem, in_cert_format, pass, 0); } if (ret < 0) { fprintf(stderr, "Decoding error: %s\n", gnutls_strerror(ret)); exit(1); } /* Public key algorithm */ fprintf(outfile, "Public Key Info:\n"); ret = gnutls_x509_privkey_get_pk_algorithm(key); fprintf(outfile, "\tPublic Key Algorithm: "); cprint = get_pk_algorithm( ret); fprintf(outfile, "%s\n", cprint); size = sizeof(buffer); if ((ret=gnutls_x509_privkey_get_key_id(key, 0, buffer, &size)) < 0) { fprintf(stderr, "Error in key id calculation: %s\n", gnutls_strerror(ret)); } else { print = printable; for (i = 0; i < size; i++) { sprintf(print, "%.2x ", (unsigned char) buffer[i]); print += 3; } fprintf(outfile, "Public Key ID: %s\n", printable); } size = sizeof(buffer); ret = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &size); if (ret < 0) { fprintf(stderr, "Encoding error: %s\n", gnutls_strerror(ret)); exit(1); } fprintf(outfile, "\n%s\n", buffer); } /* mand should be non zero if it is required to read a private key. */ gnutls_x509_privkey load_private_key(int mand) { FILE* fd; gnutls_x509_privkey key; int ret; gnutls_datum dat; size_t size; const char* pass; if (!info.privkey && !mand) return NULL; if (!info.privkey) { fprintf(stderr, "error: a private key was not specified\n"); exit(1); } fd = fopen(info.privkey, "r"); if (fd == NULL) { fprintf(stderr, "error: could not load key file '%s'.\n", info.privkey); exit(1); } size = fread(buffer, 1, sizeof(buffer)-1, fd); buffer[size] = 0; fclose(fd); ret = gnutls_x509_privkey_init(&key); if (ret < 0) { fprintf(stderr, "privkey_init: %s\n", gnutls_strerror(ret)); exit(1); } dat.data = buffer; dat.size = size; if (!info.pkcs8) ret = gnutls_x509_privkey_import( key, &dat, in_cert_format); else { pass = read_pass("Enter password: "); ret = gnutls_x509_privkey_import_pkcs8( key, &dat, in_cert_format, pass, 0); } if (ret < 0) { fprintf(stderr, "privkey_import: %s\n", gnutls_strerror(ret)); exit(1); } return key; } gnutls_x509_crq load_request() { FILE* fd; gnutls_x509_crq crq; int ret; gnutls_datum dat; size_t size; if (!info.request) return NULL; fd = fopen(info.request, "r"); if (fd == NULL) { fprintf(stderr, "File %s does not exist.\n", info.request); exit(1); } size = fread(buffer, 1, sizeof(buffer)-1, fd); buffer[size] = 0; fclose(fd); ret = gnutls_x509_crq_init(&crq); if (ret < 0) { fprintf(stderr, "crq_init: %s\n", gnutls_strerror(ret)); exit(1); } dat.data = buffer; dat.size = size; ret = gnutls_x509_crq_import( crq, &dat, in_cert_format); if (ret < 0) { fprintf(stderr, "crq_import: %s\n", gnutls_strerror(ret)); exit(1); } return crq; } gnutls_x509_privkey load_ca_private_key() { FILE* fd; gnutls_x509_privkey key; int ret; const char* pass; gnutls_datum dat; size_t size; fprintf(stderr, "Loading CA's private key...\n"); if (info.ca_privkey==NULL) { fprintf(stderr, "You must specify a private key of the CA.\n"); exit(1); } fd = fopen(info.ca_privkey, "r"); if (fd == NULL) { fprintf(stderr, "File %s does not exist.\n", info.ca_privkey); exit(1); } size = fread(buffer, 1, sizeof(buffer)-1, fd); buffer[size] = 0; fclose(fd); ret = gnutls_x509_privkey_init(&key); if (ret < 0) { fprintf(stderr, "privkey_init: %s\n", gnutls_strerror(ret)); exit(1); } dat.data = buffer; dat.size = size; if (!info.pkcs8) ret = gnutls_x509_privkey_import( key, &dat, in_cert_format); else { pass = read_pass("Enter password: "); ret = gnutls_x509_privkey_import_pkcs8( key, &dat, in_cert_format, pass, 0); } if (ret < 0) { fprintf(stderr, "privkey_import: %s\n", gnutls_strerror(ret)); exit(1); } return key; } /* Loads the CA's certificate */ gnutls_x509_crt load_ca_cert() { FILE* fd; gnutls_x509_crt crt; int ret; gnutls_datum dat; size_t size; fprintf(stderr, "Loading CA's certificate...\n"); if (info.ca==NULL) { fprintf(stderr, "You must specify a certificate of the CA.\n"); exit(1); } fd = fopen(info.ca, "r"); if (fd == NULL) { fprintf(stderr, "File %s does not exist.\n", info.ca); exit(1); } size = fread(buffer, 1, sizeof(buffer)-1, fd); buffer[size] = 0; fclose(fd); ret = gnutls_x509_crt_init(&crt); if (ret < 0) { fprintf(stderr, "crt_init: %s\n", gnutls_strerror(ret)); exit(1); } dat.data = buffer; dat.size = size; ret = gnutls_x509_crt_import( crt, &dat, in_cert_format); if (ret < 0) { fprintf(stderr, "crt_import: %s\n", gnutls_strerror(ret)); exit(1); } return crt; } /* Loads the certificate * If mand is non zero then a certificate is mandatory. Otherwise * null will be returned if the certificate loading fails. */ gnutls_x509_crt load_cert(int mand) { gnutls_x509_crt *crt; int size; crt = load_cert_list( mand, &size); return crt[0]; } #define MAX_CERTS 256 /* Loads a certificate list */ gnutls_x509_crt* load_cert_list(int mand, int *crt_size) { FILE* fd; static gnutls_x509_crt crt[MAX_CERTS]; char* ptr; int ret, i; gnutls_datum dat; size_t size; int ptr_size; *crt_size = 0; fprintf(stderr, "Loading certificate list...\n"); if (info.cert==NULL) { fprintf(stderr, "You must specify a certificate.\n"); if (mand) exit(1); else return NULL; } fd = fopen(info.cert, "r"); if (fd == NULL) { fprintf(stderr, "File %s does not exist.\n", info.cert); exit(1); } size = fread(buffer, 1, sizeof(buffer)-1, fd); buffer[size] = 0; fclose(fd); ptr = buffer; ptr_size = size; for (i=0;i 0) break; if (ret < 0) { fprintf(stderr, "crt_import: %s\n", gnutls_strerror(ret)); exit(1); } ptr = strstr( ptr, "---END"); if (ptr==NULL) break; ptr++; ptr_size = size; ptr_size -= (unsigned int)((unsigned char*)ptr - (unsigned char*)buffer); if (ptr_size < 0) break; (*crt_size)++; } fprintf(stderr, "Loaded %d certificates.\n", *crt_size); return crt; } /* Generate a PKCS #10 certificate request. */ void generate_request(void) { gnutls_x509_crq crq; gnutls_x509_privkey key; int ret; const char* pass; size_t size; fprintf(stderr, "Generating a PKCS #10 certificate request...\n"); ret = gnutls_x509_crq_init(&crq); if (ret < 0) { fprintf(stderr, "crq_init: %s\n", gnutls_strerror(ret)); exit(1); } /* Load the private key. */ key = generate_private_key_int(); read_crq_set( crq, "Country name (2 chars): ", GNUTLS_OID_X520_COUNTRY_NAME); read_crq_set( crq, "Organization name: ", GNUTLS_OID_X520_ORGANIZATION_NAME); read_crq_set( crq, "Organizational unit name: ", GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME); read_crq_set( crq, "Locality name: ", GNUTLS_OID_X520_LOCALITY_NAME); read_crq_set( crq, "State or province name: ", GNUTLS_OID_X520_LOCALITY_NAME); read_crq_set( crq, "Common name: ", GNUTLS_OID_X520_COMMON_NAME); ret = gnutls_x509_crq_set_version( crq, 1); if (ret < 0) { fprintf(stderr, "set_version: %s\n", gnutls_strerror(ret)); exit(1); } pass = read_pass("Enter a challenge password: "); if (pass != NULL) { ret = gnutls_x509_crq_set_challenge_password( crq, pass); if (ret < 0) { fprintf(stderr, "set_pass: %s\n", gnutls_strerror(ret)); exit(1); } } ret = gnutls_x509_crq_set_key( crq, key); if (ret < 0) { fprintf(stderr, "set_key: %s\n", gnutls_strerror(ret)); exit(1); } ret = gnutls_x509_crq_sign( crq, key); if (ret < 0) { fprintf(stderr, "sign: %s\n", gnutls_strerror(ret)); exit(1); } size = sizeof(buffer); ret = gnutls_x509_crq_export( crq, out_cert_format, buffer, &size); if (ret < 0) { fprintf(stderr, "export: %s\n", gnutls_strerror(ret)); exit(1); } fwrite( buffer, 1, size, outfile); gnutls_x509_crq_deinit(crq); gnutls_x509_privkey_deinit(key); } static void print_verification_res( gnutls_x509_crt crt, gnutls_x509_crt issuer, gnutls_x509_crl *crl_list, int crl_list_size); #define CERT_SEP "-----BEGIN CERT" #define CRL_SEP "-----BEGIN X509 CRL" int _verify_x509_mem( const void* cert, int cert_size) { int siz, i; const char *ptr; int ret; unsigned int output; char name[256]; char issuer_name[256]; size_t name_size; size_t issuer_name_size; gnutls_datum tmp; gnutls_x509_crt *x509_cert_list = NULL; gnutls_x509_crl *x509_crl_list = NULL; int x509_ncerts, x509_ncrls; /* Decode the CA certificate */ /* Decode the CRL list */ siz = cert_size; ptr = cert; i = 1; if (strstr(ptr, CRL_SEP)!=NULL) /* if CRLs exist */ do { x509_crl_list = (gnutls_x509_crl *) realloc( x509_crl_list, i * sizeof(gnutls_x509_crl)); if (x509_crl_list == NULL) { fprintf(stderr, "memory error\n"); exit(1); } tmp.data = (char*)ptr; tmp.size = siz; ret = gnutls_x509_crl_init( &x509_crl_list[i-1]); if (ret < 0) { fprintf(stderr, "Error parsing the CRL[%d]: %s\n", i, gnutls_strerror(ret)); exit(1); } ret = gnutls_x509_crl_import( x509_crl_list[i-1], &tmp, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "Error parsing the CRL[%d]: %s\n", i, gnutls_strerror(ret)); exit(1); } /* now we move ptr after the pem header */ ptr = strstr(ptr, CRL_SEP); if (ptr!=NULL) ptr++; i++; } while ((ptr = strstr(ptr, CRL_SEP)) != NULL); x509_ncrls = i - 1; /* Decode the certificate chain. */ siz = cert_size; ptr = cert; i = 1; do { x509_cert_list = (gnutls_x509_crt *) realloc( x509_cert_list, i * sizeof(gnutls_x509_crt)); if (x509_cert_list == NULL) { fprintf(stderr, "memory error\n"); exit(1); } tmp.data = (char*)ptr; tmp.size = siz; ret = gnutls_x509_crt_init( &x509_cert_list[i-1]); if (ret < 0) { fprintf(stderr, "Error parsing the certificate[%d]: %s\n", i, gnutls_strerror(ret)); exit(1); } ret = gnutls_x509_crt_import( x509_cert_list[i-1], &tmp, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "Error parsing the certificate[%d]: %s\n", i, gnutls_strerror(ret)); exit(1); } if (i-1 != 0) { /* verify the previous certificate using this one * as CA. */ name_size = sizeof(name); ret = gnutls_x509_crt_get_dn( x509_cert_list[i-2], name, &name_size); if (ret < 0) { fprintf(stderr, "Error in get_dn: %s\n", gnutls_strerror(ret)); exit(1); } fprintf( outfile, "Certificate[%d]: %s\n", i-2, name); /* print issuer */ issuer_name_size = sizeof(issuer_name); ret = gnutls_x509_crt_get_issuer_dn( x509_cert_list[i-2], issuer_name, &issuer_name_size); if (ret < 0) { fprintf(stderr, "Error in get_dn: %s\n", gnutls_strerror(ret)); exit(1); } fprintf( outfile, "\tIssued by: %s\n", name); /* Get the Issuer's name */ name_size = sizeof(name); ret = gnutls_x509_crt_get_dn( x509_cert_list[i-1], name, &name_size); if (ret < 0) { fprintf(stderr, "Error in get_dn: %s\n", gnutls_strerror(ret)); exit(1); } fprintf( outfile, "\tVerifying against certificate[%d].\n", i-1); if (strcmp( issuer_name, name) != 0) { fprintf(stderr, "Error: Issuer's name: %s\n", name); fprintf(stderr, "Error: Issuer's name does not match the next certificate.\n"); exit(1); } fprintf( outfile, "\tVerification output: "); print_verification_res( x509_cert_list[i-2], x509_cert_list[i-1], x509_crl_list, x509_ncrls); fprintf( outfile, ".\n\n"); } /* now we move ptr after the pem header */ ptr = strstr(ptr, CERT_SEP); if (ptr!=NULL) ptr++; i++; } while ((ptr = strstr(ptr, CERT_SEP)) != NULL); x509_ncerts = i - 1; /* The last certificate in the list will be used as * a CA (should be self signed). */ name_size = sizeof(name); ret = gnutls_x509_crt_get_dn( x509_cert_list[x509_ncerts-1], name, &name_size); if (ret < 0) { fprintf(stderr, "Error in get_dn: %s\n", gnutls_strerror(ret)); exit(1); } fprintf( outfile, "Certificate[%d]: %s\n", x509_ncerts-1, name); /* print issuer */ issuer_name_size = sizeof(issuer_name); ret = gnutls_x509_crt_get_issuer_dn( x509_cert_list[x509_ncerts-1], issuer_name, &issuer_name_size); if (ret < 0) { fprintf(stderr, "Error in get_dn: %s\n", gnutls_strerror(ret)); exit(1); } fprintf( outfile, "\tIssued by: %s\n", name); if (strcmp( issuer_name, name) != 0) { fprintf(stderr, "Error: The last certificate is not self signed.\n"); exit(1); } fprintf( outfile, "\tVerification output: "); print_verification_res( x509_cert_list[x509_ncerts-1], x509_cert_list[x509_ncerts-1], x509_crl_list, x509_ncrls); fprintf( outfile, ".\n\n"); for (i=0;i now) { if (comma) fprintf(outfile, ", "); comma = 1; fprintf(outfile, "Not activated"); } if (gnutls_x509_crt_get_expiration_time(crt) < now) { if (comma) fprintf(outfile, ", "); comma = 1; fprintf(outfile, "Expired"); } ret = gnutls_x509_crt_check_revocation( crt, crl_list, crl_list_size); if (ret < 0) { fprintf(stderr, "Error in verification: %s\n", gnutls_strerror(ret)); exit(1); } if (ret == 1) { /* revoked */ if (comma) fprintf(outfile, ", "); comma = 1; fprintf(outfile, "Revoked"); } } void verify_chain( void) { size_t size; size = fread( buffer, 1, sizeof(buffer)-1, infile); buffer[size] = 0; _verify_x509_mem( buffer, size); } void verify_crl( void) { size_t size, dn_size; char dn[128]; unsigned int output; int comma=0; int ret; gnutls_datum pem; gnutls_x509_crl crl; time_t now = time(0); gnutls_x509_crt issuer; issuer = load_ca_cert(); fprintf(outfile, "\nCA certificate:\n"); dn_size = sizeof(dn); ret = gnutls_x509_crt_get_dn(issuer, dn, &dn_size); if (ret >= 0) fprintf(outfile, "\tSubject: %s\n\n", dn); size = fread( buffer, 1, sizeof(buffer)-1, infile); buffer[size] = 0; pem.data = buffer; pem.size = size; gnutls_x509_crl_init( &crl); ret = gnutls_x509_crl_import(crl, &pem, in_cert_format); if (ret < 0) { fprintf(stderr, "CRL decoding error: %s\n", gnutls_strerror(ret)); exit(1); } print_crl_info( crl, outfile, 1); fprintf(outfile, "Verification output: "); ret = gnutls_x509_crl_verify( crl, &issuer, 1, 0, &output); if (ret < 0) { fprintf(stderr, "Error in verification: %s\n", gnutls_strerror(ret)); exit(1); } if (output&GNUTLS_CERT_INVALID) { fprintf(outfile, "Not verified"); comma = 1; } else { fprintf(outfile, "Verified"); comma = 1; } if (output&GNUTLS_CERT_SIGNER_NOT_CA) { if (comma) fprintf(outfile, ", "); fprintf(outfile, "Issuer is not a CA"); comma = 1; } /* Check expiration dates. */ if (gnutls_x509_crl_get_this_update(crl) > now) { if (comma) fprintf(outfile, ", "); comma = 1; fprintf(outfile, "Issued in the future!"); } if (gnutls_x509_crl_get_next_update(crl) < now) { if (comma) fprintf(outfile, ", "); comma = 1; fprintf(outfile, "CRL is not up to date"); } fprintf(outfile, "\n"); } #include #include void generate_pkcs12( void) { gnutls_pkcs12 pkcs12; gnutls_pkcs12_bag bag, kbag; gnutls_x509_crt crt; gnutls_x509_privkey key; int result; size_t size; gnutls_datum data; const char* password; const char* name; unsigned int flags; gnutls_datum key_id; unsigned char _key_id[20]; int index; fprintf(stderr, "Generating a PKCS #12 structure...\n"); key = load_private_key(1); crt = load_cert(0); do { name = read_str("Enter a name for the key: "); } while( name == NULL); password = read_pass( "Enter password: "); result = gnutls_pkcs12_bag_init( &bag); if (result < 0) { fprintf(stderr, "bag_init: %s\n", gnutls_strerror(result)); exit(1); } size = sizeof(_key_id); result = gnutls_x509_privkey_get_key_id( key, 0, _key_id, &size); if (result < 0) { fprintf(stderr, "key_id: %s\n", gnutls_strerror(result)); exit(1); } key_id.data = _key_id; key_id.size = size; if (crt) { /* add the certificate only if it was specified. */ result = gnutls_pkcs12_bag_set_crt( bag, crt); if (result < 0) { fprintf(stderr, "set_crt: %s\n", gnutls_strerror(result)); exit(1); } index = result; result = gnutls_pkcs12_bag_set_friendly_name( bag, index, name); if (result < 0) { fprintf(stderr, "bag_set_key_id: %s\n", gnutls_strerror(result)); exit(1); } result = gnutls_pkcs12_bag_set_key_id( bag, index, &key_id); if (result < 0) { fprintf(stderr, "bag_set_key_id: %s\n", gnutls_strerror(result)); exit(1); } if (info.export) flags = GNUTLS_PKCS_USE_PKCS12_RC2_40; else flags = GNUTLS_PKCS8_USE_PKCS12_3DES; result = gnutls_pkcs12_bag_encrypt( bag, password, flags); if (result < 0) { fprintf(stderr, "bag_encrypt: %s\n", gnutls_strerror(result)); exit(1); } } /* Key BAG */ result = gnutls_pkcs12_bag_init( &kbag); if (result < 0) { fprintf(stderr, "bag_init: %s\n", gnutls_strerror(result)); exit(1); } if (info.export) flags = GNUTLS_PKCS_USE_PKCS12_RC2_40; else flags = GNUTLS_PKCS_USE_PKCS12_3DES; size = sizeof(buffer); result = gnutls_x509_privkey_export_pkcs8( key, GNUTLS_X509_FMT_DER, password, flags, buffer, &size); if (result < 0) { fprintf(stderr, "key_export: %s\n", gnutls_strerror(result)); exit(1); } data.data = buffer; data.size = size; result = gnutls_pkcs12_bag_set_data( kbag, GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, &data); if (result < 0) { fprintf(stderr, "bag_set_data: %s\n", gnutls_strerror(result)); exit(1); } index = result; result = gnutls_pkcs12_bag_set_friendly_name( kbag, index, name); if (result < 0) { fprintf(stderr, "bag_set_key_id: %s\n", gnutls_strerror(result)); exit(1); } result = gnutls_pkcs12_bag_set_key_id( kbag, result, &key_id); if (result < 0) { fprintf(stderr, "bag_set_key_id: %s\n", gnutls_strerror(result)); exit(1); } /* write the PKCS #12 structure. */ result = gnutls_pkcs12_init(&pkcs12); if (result < 0) { fprintf(stderr, "crt_sign: %s\n", gnutls_strerror(result)); exit(1); } if (crt) { result = gnutls_pkcs12_set_bag( pkcs12, bag); if (result < 0) { fprintf(stderr, "set_bag: %s\n", gnutls_strerror(result)); exit(1); } } result = gnutls_pkcs12_set_bag( pkcs12, kbag); if (result < 0) { fprintf(stderr, "set_bag: %s\n", gnutls_strerror(result)); exit(1); } result = gnutls_pkcs12_generate_mac( pkcs12, password); if (result < 0) { fprintf(stderr, "generate_mac: %s\n", gnutls_strerror(result)); exit(1); } size = sizeof(buffer); result = gnutls_pkcs12_export( pkcs12, out_cert_format, buffer, &size); if (result < 0) { fprintf(stderr, "pkcs12_export: %s\n", gnutls_strerror(result)); exit(1); } fwrite( buffer, 1, size, outfile); } const char* BAGTYPE( gnutls_pkcs12_bag_type x) { switch (x) { case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: return "PKCS #8 Encrypted key"; case GNUTLS_BAG_EMPTY: return "Empty"; case GNUTLS_BAG_PKCS8_KEY: return "PKCS #8 Key"; case GNUTLS_BAG_CERTIFICATE: return "Certificate"; case GNUTLS_BAG_ENCRYPTED: return "Encrypted"; case GNUTLS_BAG_CRL: return "CRL"; default: return "Unknown"; } } void print_bag_data(gnutls_pkcs12_bag bag) { int result; int count, i, type; gnutls_const_datum cdata; const char* str; gnutls_datum out, data; count = gnutls_pkcs12_bag_get_count( bag); if (count < 0) { fprintf(stderr, "get_count: %s\n", gnutls_strerror(count)); exit(1); } fprintf( outfile, "\tElements: %d\n", count); for (i=0;i 0) fprintf(outfile, "Certificates: %u\n", count); for (index = 0;index < count;index++) { size = sizeof(buffer); result = gnutls_pkcs7_get_crt_raw( pkcs7, index, buffer, &size); if (result < 0) { break; } data.data = buffer; data.size = size; result = gnutls_pem_base64_encode_alloc( "CERTIFICATE", &data, &b64); if (result < 0) { fprintf(stderr, "error encoding: %s\n", gnutls_strerror(result)); exit(1); } fputs( b64.data, outfile); fputs( "\n", outfile); gnutls_free( b64.data); } /* Read the CRLs now. */ result = gnutls_pkcs7_get_crl_count( pkcs7); if (result < 0) { fprintf(stderr, "p7_count: %s\n", gnutls_strerror(result)); exit(1); } count = result; if (count > 0) fprintf(outfile, "\nCRLs: %u\n", count); for (index = 0;index < count;index++) { size = sizeof(buffer); result = gnutls_pkcs7_get_crl_raw( pkcs7, index, buffer, &size); if (result < 0) { break; } data.data = buffer; data.size = size; result = gnutls_pem_base64_encode_alloc( "X509 CRL", &data, &b64); if (result < 0) { fprintf(stderr, "error encoding: %s\n", gnutls_strerror(result)); exit(1); } fputs( b64.data, outfile); fputs( "\n", outfile); gnutls_free( b64.data); } } #else /* ENABLE_PKI */ #include int main (int argc, char **argv) { printf ("\nX.509 PKI not supported. This program is a dummy.\n\n"); return 1; }; #endif void certtool_version(void) { fprintf(stderr, "certtool, "); fprintf(stderr, "version %s. Libgnutls %s.\n", LIBGNUTLS_VERSION, gnutls_check_version(NULL)); } void print_license(void) { fputs( "\nCopyright (C) 2004 Free Software Foundation\n" "This program is free software; you can redistribute it and/or modify \n" "it under the terms of the GNU General Public License as published by \n" "the Free Software Foundation; either version 2 of the License, or \n" "(at your option) any later version. \n" "\n" "This program is distributed in the hope that it will be useful, \n" "but WITHOUT ANY WARRANTY; without even the implied warranty of \n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \n" "GNU General Public License for more details. \n" "\n" "You should have received a copy of the GNU General Public License \n" "along with this program; if not, write to the Free Software \n" "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n", stdout); }