security/nss/cmd/checkcert/checkcert.c

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "secutil.h"
     6 #include "plgetopt.h"
     7 #include "cert.h"
     8 #include "secoid.h"
     9 #include "cryptohi.h"
    11 /* maximum supported modulus length in bits (indicate problem if over this) */
    12 #define MAX_MODULUS (1024)
    15 static void Usage(char *progName)
    16 {
    17     fprintf(stderr, "Usage: %s [aAvf] [certtocheck] [issuingcert]\n",
    18 	    progName);
    19     fprintf(stderr, "%-20s Cert to check is base64 encoded\n",
    20 	    "-a");
    21     fprintf(stderr, "%-20s Issuer's cert is base64 encoded\n",
    22 	    "-A");
    23     fprintf(stderr, "%-20s Verbose (indicate decoding progress etc.)\n",
    24 	    "-v");
    25     fprintf(stderr, "%-20s Force sanity checks even if pretty print fails.\n",
    26 	    "-f");
    27     fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n",
    28 	    "-o output");
    29     fprintf(stderr, "%-20s Specify the input type (no default)\n",
    30 	    "-t type");
    31     exit(-1);
    32 }
    35 /*
    36  * Check integer field named fieldName, printing out results and
    37  * returning the length of the integer in bits
    38  */   
    40 static
    41 int checkInteger(SECItem *intItem, char *fieldName, int verbose) 
    42 {
    43     int len, bitlen;
    44     if (verbose) {
    45 	printf("Checking %s\n", fieldName);
    46     }
    48     len = intItem->len;
    50     if (len && (intItem->data[0] & 0x80)) {
    51 	printf("PROBLEM: %s is NEGATIVE 2's-complement integer.\n",
    52 	       fieldName);
    53     }
    56     /* calculate bit length and check for unnecessary leading zeros */
    57     bitlen = len << 3;
    58     if (len > 1 && intItem->data[0] == 0) {
    59 	/* leading zero byte(s) */
    60 	if (!(intItem->data[1] & 0x80)) {
    61 	    printf("PROBLEM: %s has unneeded leading zeros.  Violates DER.\n",
    62 		   fieldName);
    63 	}
    64 	/* strip leading zeros in length calculation */
    65 	{
    66 	    int i=0;
    67 	    while (bitlen > 8 && intItem->data[i] == 0) {
    68 		bitlen -= 8;
    69 		i++;
    70 	    }
    71 	}
    72     }
    73     return bitlen;
    74 }
    79 static
    80 void checkName(CERTName *n, char *fieldName, int verbose)
    81 {
    82     char *v=0;
    83     if (verbose) {
    84 	printf("Checking %s\n", fieldName);
    85     }
    87     v = CERT_GetCountryName(n);
    88     if (!v) {
    89 	printf("PROBLEM: %s lacks Country Name (C)\n",
    90 	       fieldName);
    91     }
    92     PORT_Free(v);
    94     v = CERT_GetOrgName(n);
    95     if (!v) {
    96 	printf("PROBLEM: %s lacks Organization Name (O)\n",
    97 	       fieldName);
    98     }
    99     PORT_Free(v);
   101     v = CERT_GetOrgUnitName(n);
   102     if (!v) {
   103 	printf("WARNING: %s lacks Organization Unit Name (OU)\n",
   104 	       fieldName);
   105     }
   106     PORT_Free(v);	
   108     v = CERT_GetCommonName(n);
   109     if (!v) {
   110 	printf("PROBLEM: %s lacks Common Name (CN)\n",
   111 	       fieldName);
   112     }
   113     PORT_Free(v);
   114 }
   117 static
   118 SECStatus
   119 OurVerifyData(unsigned char *buf, int len, SECKEYPublicKey *key,
   120 	      SECItem *sig, SECAlgorithmID *sigAlgorithm)
   121 {
   122     SECStatus rv;
   123     VFYContext *cx;
   124     SECOidData *sigAlgOid, *oiddata;
   125     SECOidTag sigAlgTag;
   126     SECOidTag hashAlgTag;
   127     int showDigestOid=0;
   129     cx = VFY_CreateContextWithAlgorithmID(key, sig, sigAlgorithm, &hashAlgTag, 
   130                                           NULL);
   131     if (cx == NULL)
   132 	return SECFailure;
   134     sigAlgOid = SECOID_FindOID(&sigAlgorithm->algorithm);
   135     if (sigAlgOid == 0)
   136 	return SECFailure;
   137     sigAlgTag = sigAlgOid->offset;
   140     if (showDigestOid) {
   141 	oiddata = SECOID_FindOIDByTag(hashAlgTag);
   142 	if ( oiddata ) {
   143 	    printf("PROBLEM: (cont) Digest OID is %s\n", oiddata->desc);
   144 	} else {
   145 	    SECU_PrintAsHex(stdout,
   146 			    &oiddata->oid, "PROBLEM: UNKNOWN OID", 0);
   147 	}
   148     }
   150     rv = VFY_Begin(cx);
   151     if (rv == SECSuccess) {
   152 	rv = VFY_Update(cx, buf, len);
   153 	if (rv == SECSuccess)
   154 	    rv = VFY_End(cx);
   155     }
   157     VFY_DestroyContext(cx, PR_TRUE);
   158     return rv;
   159 }
   163 static
   164 SECStatus
   165 OurVerifySignedData(CERTSignedData *sd, CERTCertificate *cert)
   166 {
   167     SECItem sig;
   168     SECKEYPublicKey *pubKey = 0;
   169     SECStatus rv;
   171     /* check the certificate's validity */
   172     rv = CERT_CertTimesValid(cert);
   173     if ( rv ) {
   174 	return(SECFailure);
   175     }
   177     /* get cert's public key */
   178     pubKey = CERT_ExtractPublicKey(cert);
   179     if ( !pubKey ) {
   180 	return(SECFailure);
   181     }
   183     /* check the signature */
   184     sig = sd->signature;
   185     DER_ConvertBitString(&sig);
   186     rv = OurVerifyData(sd->data.data, sd->data.len, pubKey, &sig,
   187 		       &sd->signatureAlgorithm);
   189     SECKEY_DestroyPublicKey(pubKey);
   191     if ( rv ) {
   192 	return(SECFailure);
   193     }
   195     return(SECSuccess);
   196 }
   201 static
   202 CERTCertificate *createEmptyCertificate(void)
   203 {
   204     PLArenaPool *arena = 0;
   205     CERTCertificate *c = 0;
   207     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   208     if ( !arena ) {
   209 	return 0;
   210     }
   213     c = (CERTCertificate *) PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
   215     if (c) {
   216 	c->referenceCount = 1;
   217 	c->arena = arena;
   218     } else {
   219 	PORT_FreeArena(arena,PR_TRUE);
   220     }
   222     return c;
   223 }    
   228 int main(int argc, char **argv)
   229 {
   230     int rv, verbose=0, force=0;
   231     int ascii=0, issuerAscii=0;
   232     char *progName=0;
   233     PRFileDesc *inFile=0, *issuerCertFile=0;
   234     SECItem derCert, derIssuerCert;
   235     PLArenaPool *arena=0;
   236     CERTSignedData *signedData=0;
   237     CERTCertificate *cert=0, *issuerCert=0;
   238     SECKEYPublicKey *rsapubkey=0;
   239     SECAlgorithmID md5WithRSAEncryption, md2WithRSAEncryption;
   240     SECAlgorithmID sha1WithRSAEncryption, rsaEncryption;
   241     SECItem spk;
   242     int selfSigned=0;
   243     int invalid=0;
   244     char *inFileName = NULL, *issuerCertFileName = NULL;
   245     PLOptState *optstate;
   246     PLOptStatus status;
   248     PORT_Memset(&md5WithRSAEncryption, 0, sizeof(md5WithRSAEncryption));
   249     PORT_Memset(&md2WithRSAEncryption, 0, sizeof(md2WithRSAEncryption));
   250     PORT_Memset(&sha1WithRSAEncryption, 0, sizeof(sha1WithRSAEncryption));
   251     PORT_Memset(&rsaEncryption, 0, sizeof(rsaEncryption));
   253     progName = strrchr(argv[0], '/');
   254     progName = progName ? progName+1 : argv[0];
   256     optstate = PL_CreateOptState(argc, argv, "aAvf");
   257     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
   258 	switch (optstate->option) {
   259 	  case 'v':
   260 	    verbose = 1;
   261 	    break;
   263 	  case 'f':
   264 	    force = 1;
   265 	    break;
   267 	  case 'a':
   268 	    ascii = 1;
   269 	    break;
   271 	  case 'A':
   272 	    issuerAscii = 1;
   273 	    break;
   275 	  case '\0':
   276 	    if (!inFileName)
   277 		inFileName = PL_strdup(optstate->value);
   278 	    else if (!issuerCertFileName)
   279 		issuerCertFileName = PL_strdup(optstate->value);
   280 	    else
   281 		Usage(progName);
   282 	    break;
   283 	}
   284     }
   286     if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) {
   287 	/* insufficient or excess args */
   288 	Usage(progName);
   289     }
   291     inFile = PR_Open(inFileName, PR_RDONLY, 0);
   292     if (!inFile) {
   293 	fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
   294 	                 progName, inFileName);
   295 	exit(1);
   296     }
   298     issuerCertFile = PR_Open(issuerCertFileName, PR_RDONLY, 0);
   299     if (!issuerCertFile) {
   300 	fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
   301 	                 progName, issuerCertFileName);
   302 	exit(1);
   303     }
   305     if (SECU_ReadDERFromFile(&derCert, inFile, ascii, PR_FALSE) != SECSuccess) {
   306 	printf("Couldn't read input certificate as DER binary or base64\n");
   307 	exit(1);
   308     }
   310     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   311     if (arena == 0) {
   312 	fprintf(stderr,"%s: can't allocate scratch arena!", progName);
   313 	exit(1);
   314     }
   316     if (issuerCertFile) {
   317 	CERTSignedData *issuerCertSD=0;
   318 	if (SECU_ReadDERFromFile(&derIssuerCert, issuerCertFile, issuerAscii,
   319 	                         PR_FALSE) != SECSuccess) {
   320 	    printf("Couldn't read issuer certificate as DER binary or base64.\n");
   321 	    exit(1);
   322 	}
   323 	issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData);
   324 	if (!issuerCertSD) {
   325 	    fprintf(stderr,"%s: can't allocate issuer signed data!", progName);
   326 	    exit(1);
   327 	}
   328 	rv = SEC_ASN1DecodeItem(arena, issuerCertSD, 
   329 	                        SEC_ASN1_GET(CERT_SignedDataTemplate),
   330 				&derIssuerCert);
   331 	if (rv) {
   332 	    fprintf(stderr, "%s: Issuer cert isn't X509 SIGNED Data?\n",
   333 		    progName);
   334 	    exit(1);
   335 	}
   336 	issuerCert = createEmptyCertificate();
   337 	if (!issuerCert) {
   338 	    printf("%s: can't allocate space for issuer cert.", progName);
   339 	    exit(1);
   340 	}
   341 	rv = SEC_ASN1DecodeItem(arena, issuerCert, 
   342 	                    SEC_ASN1_GET(CERT_CertificateTemplate),
   343 			    &issuerCertSD->data);
   344 	if (rv) {
   345 	    printf("%s: Does not appear to be an X509 Certificate.\n",
   346 		   progName);
   347 	    exit(1);
   348 	}
   349     }
   351     signedData =  PORT_ArenaZNew(arena,CERTSignedData);
   352     if (!signedData) {
   353 	fprintf(stderr,"%s: can't allocate signedData!", progName);
   354 	exit(1);
   355     }
   357     rv = SEC_ASN1DecodeItem(arena, signedData, 
   358                             SEC_ASN1_GET(CERT_SignedDataTemplate), 
   359 			    &derCert);
   360     if (rv) {
   361 	fprintf(stderr, "%s: Does not appear to be X509 SIGNED Data.\n",
   362 		progName);
   363 	exit(1);
   364     }
   366     if (verbose) {
   367 	printf("Decoded ok as X509 SIGNED data.\n");
   368     }
   370     cert = createEmptyCertificate();
   371     if (!cert) {
   372 	fprintf(stderr, "%s: can't allocate cert", progName);
   373 	exit(1);
   374     }
   376     rv = SEC_ASN1DecodeItem(arena, cert, 
   377                         SEC_ASN1_GET(CERT_CertificateTemplate), 
   378 			&signedData->data);
   379     if (rv) {
   380 	fprintf(stderr, "%s: Does not appear to be an X509 Certificate.\n",
   381 		progName);
   382 	exit(1);
   383     }
   386     if (verbose) {
   387 	printf("Decoded ok as an X509 certificate.\n");
   388     }
   390     SECU_RegisterDynamicOids();
   391     rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0,
   392 			      SECU_PrintCertificate);
   394     if (rv) {
   395 	fprintf(stderr, "%s: Unable to pretty print cert. Error: %d\n",
   396 		progName, PORT_GetError());
   397 	if (!force) {
   398 	    exit(1);
   399 	}
   400     }
   403     /* Do various checks on the cert */
   405     printf("\n");
   407     /* Check algorithms */
   408     SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption,
   409 		       SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL);
   411     SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption,
   412 		       SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL);
   414     SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption,
   415 		       SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL);
   417     SECOID_SetAlgorithmID(arena, &rsaEncryption,
   418 		       SEC_OID_PKCS1_RSA_ENCRYPTION, NULL);
   420     {
   421 	int isMD5RSA = (SECOID_CompareAlgorithmID(&cert->signature,
   422 					       &md5WithRSAEncryption) == 0);
   423 	int isMD2RSA = (SECOID_CompareAlgorithmID(&cert->signature,
   424 					       &md2WithRSAEncryption) == 0);
   425 	int isSHA1RSA = (SECOID_CompareAlgorithmID(&cert->signature,
   426 					       &sha1WithRSAEncryption) == 0);
   428 	if (verbose) {
   429 	    printf("\nDoing algorithm checks.\n");
   430 	}
   432 	if (!(isMD5RSA || isMD2RSA || isSHA1RSA)) {
   433 	    printf("PROBLEM: Signature not PKCS1 MD5, MD2, or SHA1 + RSA.\n");
   434 	} else if (!isMD5RSA) {
   435 	    printf("WARNING: Signature not PKCS1 MD5 with RSA Encryption\n");
   436 	}
   438 	if (SECOID_CompareAlgorithmID(&cert->signature,
   439 				   &signedData->signatureAlgorithm)) {
   440 	    printf("PROBLEM: Algorithm in sig and certInfo don't match.\n");
   441 	}
   442     }
   444     if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm,
   445 			       &rsaEncryption)) {
   446 	printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n");
   447     }
   449     /* Check further public key properties */
   450     spk = cert->subjectPublicKeyInfo.subjectPublicKey;
   451     DER_ConvertBitString(&spk);
   453     if (verbose) {
   454 	printf("\nsubjectPublicKey DER\n");
   455 	rv = DER_PrettyPrint(stdout, &spk, PR_FALSE);
   456 	printf("\n");
   457     }
   459     rsapubkey = (SECKEYPublicKey *) 
   460 	             PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey));
   461     if (!rsapubkey) {
   462 	fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName);
   463 	exit(1);
   464     }
   466     rv = SEC_ASN1DecodeItem(arena, rsapubkey, 
   467                             SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &spk);
   468     if (rv) {
   469 	printf("PROBLEM: subjectPublicKey is not a DER PKCS1 RSAPublicKey.\n");
   470     } else {
   471 	int mlen;
   472 	int pubexp;
   473 	if (verbose) {
   474 	    printf("Decoded RSA Public Key ok.  Doing key checks.\n");
   475 	}
   476 	PORT_Assert(rsapubkey->keyType == rsaKey); /* XXX RSA */
   477 	mlen = checkInteger(&rsapubkey->u.rsa.modulus, "Modulus", verbose);
   478 	printf("INFO: Public Key modulus length in bits: %d\n", mlen);
   479 	if (mlen > MAX_MODULUS) {
   480 	    printf("PROBLEM: Modulus length exceeds %d bits.\n",
   481 		   MAX_MODULUS);
   482 	}
   483 	if (mlen < 512) {
   484 	    printf("WARNING: Short modulus.\n");
   485 	}
   486 	if (mlen != (1 << (ffs(mlen)-1))) {
   487 	    printf("WARNING: Unusual modulus length (not a power of two).\n");
   488 	}
   489 	checkInteger(&rsapubkey->u.rsa.publicExponent, "Public Exponent",
   490                      verbose);
   491 	pubexp = DER_GetInteger(&rsapubkey->u.rsa.publicExponent);
   492 	if (pubexp != 17 && pubexp != 3 && pubexp != 65537) {
   493 	    printf("WARNING: Public exponent not any of: 3, 17, 65537\n");
   494 	}
   495     }
   498     /* Name checks */
   499     checkName(&cert->issuer, "Issuer Name", verbose);
   500     checkName(&cert->subject, "Subject Name", verbose);
   502     if (issuerCert) {
   503 	SECComparison c =
   504 	    CERT_CompareName(&cert->issuer, &issuerCert->subject);
   505 	if (c) {
   506          printf("PROBLEM: Issuer Name and Subject in Issuing Cert differ\n");
   507         }
   508     }
   510     /* Check if self-signed */
   511     selfSigned = (CERT_CompareName(&cert->issuer, &cert->subject) == 0);
   512     if (selfSigned) {
   513 	printf("INFO: Certificate is self signed.\n");
   514     } else {
   515 	printf("INFO: Certificate is NOT self-signed.\n");
   516     }
   519     /* Validity time check */
   520     if (CERT_CertTimesValid(cert) == SECSuccess) {
   521 	printf("INFO: Inside validity period of certificate.\n");
   522     } else {
   523 	printf("PROBLEM: Not in validity period of certificate.\n");
   524 	invalid = 1;
   525     }
   527     /* Signature check if self-signed */
   528     if (selfSigned && !invalid) {
   529 	if (rsapubkey->u.rsa.modulus.len) {
   530 	    SECStatus ver;
   531 	    if (verbose) {
   532 		printf("Checking self signature.\n");
   533 	    }
   534 	    ver = OurVerifySignedData(signedData, cert);
   535 	    if (ver != SECSuccess) {
   536 		printf("PROBLEM: Verification of self-signature failed!\n");
   537 	    } else {
   538 		printf("INFO: Self-signature verifies ok.\n");
   539 	    }
   540 	} else {
   541 	    printf("INFO: Not checking signature due to key problems.\n");
   542 	}
   543     } else if (!selfSigned && !invalid && issuerCert) {
   544 	SECStatus ver;
   545 	ver = OurVerifySignedData(signedData, issuerCert);
   546 	if (ver != SECSuccess) {
   547 	    printf("PROBLEM: Verification of issuer's signature failed!\n");
   548 	} else {
   549 	    printf("INFO: Issuer's signature verifies ok.\n");
   550 	}
   551     } else {
   552 	printf("INFO: Not checking signature.\n");
   553     }
   555     return 0;    
   556 }

mercurial