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