Wed, 31 Dec 2014 07:16:47 +0100
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 }