PageRenderTime 92ms CodeModel.GetById 28ms app.highlight 57ms RepoModel.GetById 1ms app.codeStats 1ms

/security/nss/cmd/checkcert/checkcert.c

http://github.com/zpao/v8monkey
C | 591 lines | 439 code | 98 blank | 54 comment | 114 complexity | dc3d08fa3ece15d85ad48508cf0536ab MD5 | raw file
  1/* ***** BEGIN LICENSE BLOCK *****
  2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3 *
  4 * The contents of this file are subject to the Mozilla Public License Version
  5 * 1.1 (the "License"); you may not use this file except in compliance with
  6 * the License. You may obtain a copy of the License at
  7 * http://www.mozilla.org/MPL/
  8 *
  9 * Software distributed under the License is distributed on an "AS IS" basis,
 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11 * for the specific language governing rights and limitations under the
 12 * License.
 13 *
 14 * The Original Code is the Netscape security libraries.
 15 *
 16 * The Initial Developer of the Original Code is
 17 * Netscape Communications Corporation.
 18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 19 * the Initial Developer. All Rights Reserved.
 20 *
 21 * Contributor(s):
 22 *
 23 * Alternatively, the contents of this file may be used under the terms of
 24 * either the GNU General Public License Version 2 or later (the "GPL"), or
 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 26 * in which case the provisions of the GPL or the LGPL are applicable instead
 27 * of those above. If you wish to allow use of your version of this file only
 28 * under the terms of either the GPL or the LGPL, and not to allow others to
 29 * use your version of this file under the terms of the MPL, indicate your
 30 * decision by deleting the provisions above and replace them with the notice
 31 * and other provisions required by the GPL or the LGPL. If you do not delete
 32 * the provisions above, a recipient may use your version of this file under
 33 * the terms of any one of the MPL, the GPL or the LGPL.
 34 *
 35 * ***** END LICENSE BLOCK ***** */
 36
 37#include "secutil.h"
 38#include "plgetopt.h"
 39#include "cert.h"
 40#include "secoid.h"
 41#include "cryptohi.h"
 42
 43/* maximum supported modulus length in bits (indicate problem if over this) */
 44#define MAX_MODULUS (1024)
 45
 46
 47static void Usage(char *progName)
 48{
 49    fprintf(stderr, "Usage: %s [aAvf] [certtocheck] [issuingcert]\n",
 50	    progName);
 51    fprintf(stderr, "%-20s Cert to check is base64 encoded\n",
 52	    "-a");
 53    fprintf(stderr, "%-20s Issuer's cert is base64 encoded\n",
 54	    "-A");
 55    fprintf(stderr, "%-20s Verbose (indicate decoding progress etc.)\n",
 56	    "-v");
 57    fprintf(stderr, "%-20s Force sanity checks even if pretty print fails.\n",
 58	    "-f");
 59    fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n",
 60	    "-o output");
 61    fprintf(stderr, "%-20s Specify the input type (no default)\n",
 62	    "-t type");
 63    exit(-1);
 64}
 65
 66
 67/*
 68 * Check integer field named fieldName, printing out results and
 69 * returning the length of the integer in bits
 70 */   
 71
 72static
 73int checkInteger(SECItem *intItem, char *fieldName, int verbose) 
 74{
 75    int len, bitlen;
 76    if (verbose) {
 77	printf("Checking %s\n", fieldName);
 78    }
 79
 80    len = intItem->len;
 81
 82    if (len && (intItem->data[0] & 0x80)) {
 83	printf("PROBLEM: %s is NEGATIVE 2's-complement integer.\n",
 84	       fieldName);
 85    }
 86
 87
 88    /* calculate bit length and check for unnecessary leading zeros */
 89    bitlen = len << 3;
 90    if (len > 1 && intItem->data[0] == 0) {
 91	/* leading zero byte(s) */
 92	if (!(intItem->data[1] & 0x80)) {
 93	    printf("PROBLEM: %s has unneeded leading zeros.  Violates DER.\n",
 94		   fieldName);
 95	}
 96	/* strip leading zeros in length calculation */
 97	{
 98	    int i=0;
 99	    while (bitlen > 8 && intItem->data[i] == 0) {
100		bitlen -= 8;
101		i++;
102	    }
103	}
104    }
105    return bitlen;
106}
107
108
109
110
111static
112void checkName(CERTName *n, char *fieldName, int verbose)
113{
114    char *v=0;
115    if (verbose) {
116	printf("Checking %s\n", fieldName);
117    }
118
119    v = CERT_GetCountryName(n);
120    if (!v) {
121	printf("PROBLEM: %s lacks Country Name (C)\n",
122	       fieldName);
123    }
124    PORT_Free(v);
125
126    v = CERT_GetOrgName(n);
127    if (!v) {
128	printf("PROBLEM: %s lacks Organization Name (O)\n",
129	       fieldName);
130    }
131    PORT_Free(v);
132
133    v = CERT_GetOrgUnitName(n);
134    if (!v) {
135	printf("WARNING: %s lacks Organization Unit Name (OU)\n",
136	       fieldName);
137    }
138    PORT_Free(v);	
139
140    v = CERT_GetCommonName(n);
141    if (!v) {
142	printf("PROBLEM: %s lacks Common Name (CN)\n",
143	       fieldName);
144    }
145    PORT_Free(v);
146}
147
148
149static
150SECStatus
151OurVerifyData(unsigned char *buf, int len, SECKEYPublicKey *key,
152	      SECItem *sig, SECAlgorithmID *sigAlgorithm)
153{
154    SECStatus rv;
155    VFYContext *cx;
156    SECOidData *sigAlgOid, *oiddata;
157    SECOidTag sigAlgTag;
158    SECOidTag hashAlgTag;
159    int showDigestOid=0;
160
161    cx = VFY_CreateContextWithAlgorithmID(key, sig, sigAlgorithm, &hashAlgTag, 
162                                          NULL);
163    if (cx == NULL)
164	return SECFailure;
165
166    sigAlgOid = SECOID_FindOID(&sigAlgorithm->algorithm);
167    if (sigAlgOid == 0)
168	return SECFailure;
169    sigAlgTag = sigAlgOid->offset;
170
171
172    if (showDigestOid) {
173	oiddata = SECOID_FindOIDByTag(hashAlgTag);
174	if ( oiddata ) {
175	    printf("PROBLEM: (cont) Digest OID is %s\n", oiddata->desc);
176	} else {
177	    SECU_PrintAsHex(stdout,
178			    &oiddata->oid, "PROBLEM: UNKNOWN OID", 0);
179	}
180    }
181
182    rv = VFY_Begin(cx);
183    if (rv == SECSuccess) {
184	rv = VFY_Update(cx, buf, len);
185	if (rv == SECSuccess)
186	    rv = VFY_End(cx);
187    }
188
189    VFY_DestroyContext(cx, PR_TRUE);
190    return rv;
191}
192
193
194
195static
196SECStatus
197OurVerifySignedData(CERTSignedData *sd, CERTCertificate *cert)
198{
199    SECItem sig;
200    SECKEYPublicKey *pubKey = 0;
201    SECStatus rv;
202
203    /* check the certificate's validity */
204    rv = CERT_CertTimesValid(cert);
205    if ( rv ) {
206	return(SECFailure);
207    }
208
209    /* get cert's public key */
210    pubKey = CERT_ExtractPublicKey(cert);
211    if ( !pubKey ) {
212	return(SECFailure);
213    }
214
215    /* check the signature */
216    sig = sd->signature;
217    DER_ConvertBitString(&sig);
218    rv = OurVerifyData(sd->data.data, sd->data.len, pubKey, &sig,
219		       &sd->signatureAlgorithm);
220
221    SECKEY_DestroyPublicKey(pubKey);
222
223    if ( rv ) {
224	return(SECFailure);
225    }
226
227    return(SECSuccess);
228}
229
230
231
232
233static
234CERTCertificate *createEmptyCertificate(void)
235{
236    PRArenaPool *arena = 0;
237    CERTCertificate *c = 0;
238
239    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
240    if ( !arena ) {
241	return 0;
242    }
243
244
245    c = (CERTCertificate *) PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
246
247    if (c) {
248	c->referenceCount = 1;
249	c->arena = arena;
250    } else {
251	PORT_FreeArena(arena,PR_TRUE);
252    }
253
254    return c;
255}    
256
257
258
259
260int main(int argc, char **argv)
261{
262    int rv, verbose=0, force=0;
263    int ascii=0, issuerAscii=0;
264    char *progName=0;
265    PRFileDesc *inFile=0, *issuerCertFile=0;
266    SECItem derCert, derIssuerCert;
267    PRArenaPool *arena=0;
268    CERTSignedData *signedData=0;
269    CERTCertificate *cert=0, *issuerCert=0;
270    SECKEYPublicKey *rsapubkey=0;
271    SECAlgorithmID md5WithRSAEncryption, md2WithRSAEncryption;
272    SECAlgorithmID sha1WithRSAEncryption, rsaEncryption;
273    SECItem spk;
274    int selfSigned=0;
275    int invalid=0;
276    char *inFileName = NULL, *issuerCertFileName = NULL;
277    PLOptState *optstate;
278    PLOptStatus status;
279
280    PORT_Memset(&md5WithRSAEncryption, 0, sizeof(md5WithRSAEncryption));
281    PORT_Memset(&md2WithRSAEncryption, 0, sizeof(md2WithRSAEncryption));
282    PORT_Memset(&sha1WithRSAEncryption, 0, sizeof(sha1WithRSAEncryption));
283    PORT_Memset(&rsaEncryption, 0, sizeof(rsaEncryption));
284
285    progName = strrchr(argv[0], '/');
286    progName = progName ? progName+1 : argv[0];
287
288    optstate = PL_CreateOptState(argc, argv, "aAvf");
289    while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
290	switch (optstate->option) {
291	  case 'v':
292	    verbose = 1;
293	    break;
294
295	  case 'f':
296	    force = 1;
297	    break;
298
299	  case 'a':
300	    ascii = 1;
301	    break;
302
303	  case 'A':
304	    issuerAscii = 1;
305	    break;
306
307	  case '\0':
308	    if (!inFileName)
309		inFileName = PL_strdup(optstate->value);
310	    else if (!issuerCertFileName)
311		issuerCertFileName = PL_strdup(optstate->value);
312	    else
313		Usage(progName);
314	    break;
315	}
316    }
317
318    if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) {
319	/* insufficient or excess args */
320	Usage(progName);
321    }
322
323    inFile = PR_Open(inFileName, PR_RDONLY, 0);
324    if (!inFile) {
325	fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
326	                 progName, inFileName);
327	exit(1);
328    }
329
330    issuerCertFile = PR_Open(issuerCertFileName, PR_RDONLY, 0);
331    if (!issuerCertFile) {
332	fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
333	                 progName, issuerCertFileName);
334	exit(1);
335    }
336
337    if (SECU_ReadDERFromFile(&derCert, inFile, ascii) != SECSuccess) {
338	printf("Couldn't read input certificate as DER binary or base64\n");
339	exit(1);
340    }
341
342    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
343    if (arena == 0) {
344	fprintf(stderr,"%s: can't allocate scratch arena!", progName);
345	exit(1);
346    }
347
348    if (issuerCertFile) {
349	CERTSignedData *issuerCertSD=0;
350	if (SECU_ReadDERFromFile(&derIssuerCert, issuerCertFile, issuerAscii)
351	    != SECSuccess) {
352	    printf("Couldn't read issuer certificate as DER binary or base64.\n");
353	    exit(1);
354	}
355	issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData);
356	if (!issuerCertSD) {
357	    fprintf(stderr,"%s: can't allocate issuer signed data!", progName);
358	    exit(1);
359	}
360	rv = SEC_ASN1DecodeItem(arena, issuerCertSD, 
361	                        SEC_ASN1_GET(CERT_SignedDataTemplate),
362				&derIssuerCert);
363	if (rv) {
364	    fprintf(stderr, "%s: Issuer cert isn't X509 SIGNED Data?\n",
365		    progName);
366	    exit(1);
367	}
368	issuerCert = createEmptyCertificate();
369	if (!issuerCert) {
370	    printf("%s: can't allocate space for issuer cert.", progName);
371	    exit(1);
372	}
373	rv = SEC_ASN1DecodeItem(arena, issuerCert, 
374	                    SEC_ASN1_GET(CERT_CertificateTemplate),
375			    &issuerCertSD->data);
376	if (rv) {
377	    printf("%s: Does not appear to be an X509 Certificate.\n",
378		   progName);
379	    exit(1);
380	}
381    }
382
383    signedData =  PORT_ArenaZNew(arena,CERTSignedData);
384    if (!signedData) {
385	fprintf(stderr,"%s: can't allocate signedData!", progName);
386	exit(1);
387    }
388
389    rv = SEC_ASN1DecodeItem(arena, signedData, 
390                            SEC_ASN1_GET(CERT_SignedDataTemplate), 
391			    &derCert);
392    if (rv) {
393	fprintf(stderr, "%s: Does not appear to be X509 SIGNED Data.\n",
394		progName);
395	exit(1);
396    }
397
398    if (verbose) {
399	printf("Decoded ok as X509 SIGNED data.\n");
400    }
401
402    cert = createEmptyCertificate();
403    if (!cert) {
404	fprintf(stderr, "%s: can't allocate cert", progName);
405	exit(1);
406    }
407
408    rv = SEC_ASN1DecodeItem(arena, cert, 
409                        SEC_ASN1_GET(CERT_CertificateTemplate), 
410			&signedData->data);
411    if (rv) {
412	fprintf(stderr, "%s: Does not appear to be an X509 Certificate.\n",
413		progName);
414	exit(1);
415    }
416
417
418    if (verbose) {
419	printf("Decoded ok as an X509 certificate.\n");
420    }
421
422    SECU_RegisterDynamicOids();
423    rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0,
424			      SECU_PrintCertificate);
425
426    if (rv) {
427	fprintf(stderr, "%s: Unable to pretty print cert. Error: %d\n",
428		progName, PORT_GetError());
429	if (!force) {
430	    exit(1);
431	}
432    }
433
434
435    /* Do various checks on the cert */
436
437    printf("\n");
438
439    /* Check algorithms */
440    SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption,
441		       SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL);
442
443    SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption,
444		       SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL);
445
446    SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption,
447		       SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL);
448
449    SECOID_SetAlgorithmID(arena, &rsaEncryption,
450		       SEC_OID_PKCS1_RSA_ENCRYPTION, NULL);
451
452    {
453	int isMD5RSA = (SECOID_CompareAlgorithmID(&cert->signature,
454					       &md5WithRSAEncryption) == 0);
455	int isMD2RSA = (SECOID_CompareAlgorithmID(&cert->signature,
456					       &md2WithRSAEncryption) == 0);
457	int isSHA1RSA = (SECOID_CompareAlgorithmID(&cert->signature,
458					       &sha1WithRSAEncryption) == 0);
459
460	if (verbose) {
461	    printf("\nDoing algorithm checks.\n");
462	}
463
464	if (!(isMD5RSA || isMD2RSA || isSHA1RSA)) {
465	    printf("PROBLEM: Signature not PKCS1 MD5, MD2, or SHA1 + RSA.\n");
466	} else if (!isMD5RSA) {
467	    printf("WARNING: Signature not PKCS1 MD5 with RSA Encryption\n");
468	}
469
470	if (SECOID_CompareAlgorithmID(&cert->signature,
471				   &signedData->signatureAlgorithm)) {
472	    printf("PROBLEM: Algorithm in sig and certInfo don't match.\n");
473	}
474    }
475
476    if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm,
477			       &rsaEncryption)) {
478	printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n");
479    }
480
481    /* Check further public key properties */
482    spk = cert->subjectPublicKeyInfo.subjectPublicKey;
483    DER_ConvertBitString(&spk);
484
485    if (verbose) {
486	printf("\nsubjectPublicKey DER\n");
487	rv = DER_PrettyPrint(stdout, &spk, PR_FALSE);
488	printf("\n");
489    }
490
491    rsapubkey = (SECKEYPublicKey *) 
492	             PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey));
493    if (!rsapubkey) {
494	fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName);
495	exit(1);
496    }
497
498    rv = SEC_ASN1DecodeItem(arena, rsapubkey, 
499                            SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &spk);
500    if (rv) {
501	printf("PROBLEM: subjectPublicKey is not a DER PKCS1 RSAPublicKey.\n");
502    } else {
503	int mlen;
504	int pubexp;
505	if (verbose) {
506	    printf("Decoded RSA Public Key ok.  Doing key checks.\n");
507	}
508	PORT_Assert(rsapubkey->keyType == rsaKey); /* XXX RSA */
509	mlen = checkInteger(&rsapubkey->u.rsa.modulus, "Modulus", verbose);
510	printf("INFO: Public Key modulus length in bits: %d\n", mlen);
511	if (mlen > MAX_MODULUS) {
512	    printf("PROBLEM: Modulus length exceeds %d bits.\n",
513		   MAX_MODULUS);
514	}
515	if (mlen < 512) {
516	    printf("WARNING: Short modulus.\n");
517	}
518	if (mlen != (1 << (ffs(mlen)-1))) {
519	    printf("WARNING: Unusual modulus length (not a power of two).\n");
520	}
521	checkInteger(&rsapubkey->u.rsa.publicExponent, "Public Exponent",
522                     verbose);
523	pubexp = DER_GetInteger(&rsapubkey->u.rsa.publicExponent);
524	if (pubexp != 17 && pubexp != 3 && pubexp != 65537) {
525	    printf("WARNING: Public exponent not any of: 3, 17, 65537\n");
526	}
527    }
528
529
530    /* Name checks */
531    checkName(&cert->issuer, "Issuer Name", verbose);
532    checkName(&cert->subject, "Subject Name", verbose);
533
534    if (issuerCert) {
535	SECComparison c =
536	    CERT_CompareName(&cert->issuer, &issuerCert->subject);
537	if (c) {
538         printf("PROBLEM: Issuer Name and Subject in Issuing Cert differ\n");
539        }
540    }
541
542    /* Check if self-signed */
543    selfSigned = (CERT_CompareName(&cert->issuer, &cert->subject) == 0);
544    if (selfSigned) {
545	printf("INFO: Certificate is self signed.\n");
546    } else {
547	printf("INFO: Certificate is NOT self-signed.\n");
548    }
549
550
551    /* Validity time check */
552    if (CERT_CertTimesValid(cert) == SECSuccess) {
553	printf("INFO: Inside validity period of certificate.\n");
554    } else {
555	printf("PROBLEM: Not in validity period of certificate.\n");
556	invalid = 1;
557    }
558
559    /* Signature check if self-signed */
560    if (selfSigned && !invalid) {
561	if (rsapubkey->u.rsa.modulus.len) {
562	    SECStatus ver;
563	    if (verbose) {
564		printf("Checking self signature.\n");
565	    }
566	    ver = OurVerifySignedData(signedData, cert);
567	    if (ver != SECSuccess) {
568		printf("PROBLEM: Verification of self-signature failed!\n");
569	    } else {
570		printf("INFO: Self-signature verifies ok.\n");
571	    }
572	} else {
573	    printf("INFO: Not checking signature due to key problems.\n");
574	}
575    } else if (!selfSigned && !invalid && issuerCert) {
576	SECStatus ver;
577	ver = OurVerifySignedData(signedData, issuerCert);
578	if (ver != SECSuccess) {
579	    printf("PROBLEM: Verification of issuer's signature failed!\n");
580	} else {
581	    printf("INFO: Issuer's signature verifies ok.\n");
582	}
583    } else {
584	printf("INFO: Not checking signature.\n");
585    }
586
587    return 0;    
588}
589
590
591