nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / checkcert / checkcert.c
blob27c201e1b0a77b8ac4931d604e72c71d7c4433c1
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
14 * The Original Code is the Netscape security libraries.
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.
21 * Contributor(s):
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.
35 * ***** END LICENSE BLOCK ***** */
37 #include "secutil.h"
38 #include "plgetopt.h"
39 #include "cert.h"
40 #include "secoid.h"
41 #include "cryptohi.h"
43 /* maximum supported modulus length in bits (indicate problem if over this) */
44 #define MAX_MODULUS (1024)
47 static void Usage(char *progName)
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);
68 * Check integer field named fieldName, printing out results and
69 * returning the length of the integer in bits
70 */
72 static
73 int checkInteger(SECItem *intItem, char *fieldName, int verbose)
75 int len, bitlen;
76 if (verbose) {
77 printf("Checking %s\n", fieldName);
80 len = intItem->len;
82 if (len && (intItem->data[0] & 0x80)) {
83 printf("PROBLEM: %s is NEGATIVE 2's-complement integer.\n",
84 fieldName);
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);
96 /* strip leading zeros in length calculation */
98 int i=0;
99 while (bitlen > 8 && intItem->data[i] == 0) {
100 bitlen -= 8;
101 i++;
105 return bitlen;
111 static
112 void checkName(CERTName *n, char *fieldName, int verbose)
114 char *v=0;
115 if (verbose) {
116 printf("Checking %s\n", fieldName);
119 v = CERT_GetCountryName(n);
120 if (!v) {
121 printf("PROBLEM: %s lacks Country Name (C)\n",
122 fieldName);
124 PORT_Free(v);
126 v = CERT_GetOrgName(n);
127 if (!v) {
128 printf("PROBLEM: %s lacks Organization Name (O)\n",
129 fieldName);
131 PORT_Free(v);
133 v = CERT_GetOrgUnitName(n);
134 if (!v) {
135 printf("WARNING: %s lacks Organization Unit Name (OU)\n",
136 fieldName);
138 PORT_Free(v);
140 v = CERT_GetCommonName(n);
141 if (!v) {
142 printf("PROBLEM: %s lacks Common Name (CN)\n",
143 fieldName);
145 PORT_Free(v);
149 static
150 SECStatus
151 OurVerifyData(unsigned char *buf, int len, SECKEYPublicKey *key,
152 SECItem *sig, SECAlgorithmID *sigAlgorithm)
154 SECStatus rv;
155 VFYContext *cx;
156 SECOidData *sigAlgOid, *oiddata;
157 SECOidTag sigAlgTag;
158 SECOidTag hashAlgTag;
159 int showDigestOid=0;
161 cx = VFY_CreateContextWithAlgorithmID(key, sig, sigAlgorithm, &hashAlgTag,
162 NULL);
163 if (cx == NULL)
164 return SECFailure;
166 sigAlgOid = SECOID_FindOID(&sigAlgorithm->algorithm);
167 if (sigAlgOid == 0)
168 return SECFailure;
169 sigAlgTag = sigAlgOid->offset;
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);
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);
189 VFY_DestroyContext(cx, PR_TRUE);
190 return rv;
195 static
196 SECStatus
197 OurVerifySignedData(CERTSignedData *sd, CERTCertificate *cert)
199 SECItem sig;
200 SECKEYPublicKey *pubKey = 0;
201 SECStatus rv;
203 /* check the certificate's validity */
204 rv = CERT_CertTimesValid(cert);
205 if ( rv ) {
206 return(SECFailure);
209 /* get cert's public key */
210 pubKey = CERT_ExtractPublicKey(cert);
211 if ( !pubKey ) {
212 return(SECFailure);
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);
221 SECKEY_DestroyPublicKey(pubKey);
223 if ( rv ) {
224 return(SECFailure);
227 return(SECSuccess);
233 static
234 CERTCertificate *createEmptyCertificate(void)
236 PRArenaPool *arena = 0;
237 CERTCertificate *c = 0;
239 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
240 if ( !arena ) {
241 return 0;
245 c = (CERTCertificate *) PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
247 if (c) {
248 c->referenceCount = 1;
249 c->arena = arena;
250 } else {
251 PORT_FreeArena(arena,PR_TRUE);
254 return c;
260 int main(int argc, char **argv)
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;
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));
285 progName = strrchr(argv[0], '/');
286 progName = progName ? progName+1 : argv[0];
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;
295 case 'f':
296 force = 1;
297 break;
299 case 'a':
300 ascii = 1;
301 break;
303 case 'A':
304 issuerAscii = 1;
305 break;
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;
318 if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) {
319 /* insufficient or excess args */
320 Usage(progName);
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);
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);
337 if (SECU_ReadDERFromFile(&derCert, inFile, ascii) != SECSuccess) {
338 printf("Couldn't read input certificate as DER binary or base64\n");
339 exit(1);
342 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
343 if (arena == 0) {
344 fprintf(stderr,"%s: can't allocate scratch arena!", progName);
345 exit(1);
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);
355 issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData);
356 if (!issuerCertSD) {
357 fprintf(stderr,"%s: can't allocate issuer signed data!", progName);
358 exit(1);
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);
368 issuerCert = createEmptyCertificate();
369 if (!issuerCert) {
370 printf("%s: can't allocate space for issuer cert.", progName);
371 exit(1);
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);
383 signedData = PORT_ArenaZNew(arena,CERTSignedData);
384 if (!signedData) {
385 fprintf(stderr,"%s: can't allocate signedData!", progName);
386 exit(1);
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);
398 if (verbose) {
399 printf("Decoded ok as X509 SIGNED data.\n");
402 cert = createEmptyCertificate();
403 if (!cert) {
404 fprintf(stderr, "%s: can't allocate cert", progName);
405 exit(1);
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);
418 if (verbose) {
419 printf("Decoded ok as an X509 certificate.\n");
422 SECU_RegisterDynamicOids();
423 rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0,
424 SECU_PrintCertificate);
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);
435 /* Do various checks on the cert */
437 printf("\n");
439 /* Check algorithms */
440 SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption,
441 SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL);
443 SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption,
444 SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL);
446 SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption,
447 SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL);
449 SECOID_SetAlgorithmID(arena, &rsaEncryption,
450 SEC_OID_PKCS1_RSA_ENCRYPTION, NULL);
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);
460 if (verbose) {
461 printf("\nDoing algorithm checks.\n");
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");
470 if (SECOID_CompareAlgorithmID(&cert->signature,
471 &signedData->signatureAlgorithm)) {
472 printf("PROBLEM: Algorithm in sig and certInfo don't match.\n");
476 if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm,
477 &rsaEncryption)) {
478 printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n");
481 /* Check further public key properties */
482 spk = cert->subjectPublicKeyInfo.subjectPublicKey;
483 DER_ConvertBitString(&spk);
485 if (verbose) {
486 printf("\nsubjectPublicKey DER\n");
487 rv = DER_PrettyPrint(stdout, &spk, PR_FALSE);
488 printf("\n");
491 rsapubkey = (SECKEYPublicKey *)
492 PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey));
493 if (!rsapubkey) {
494 fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName);
495 exit(1);
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");
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);
515 if (mlen < 512) {
516 printf("WARNING: Short modulus.\n");
518 if (mlen != (1 << (ffs(mlen)-1))) {
519 printf("WARNING: Unusual modulus length (not a power of two).\n");
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");
530 /* Name checks */
531 checkName(&cert->issuer, "Issuer Name", verbose);
532 checkName(&cert->subject, "Subject Name", verbose);
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");
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");
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;
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");
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");
572 } else {
573 printf("INFO: Not checking signature due to key problems.\n");
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");
583 } else {
584 printf("INFO: Not checking signature.\n");
587 return 0;