1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/cmd/checkcert/checkcert.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,559 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "secutil.h" 1.9 +#include "plgetopt.h" 1.10 +#include "cert.h" 1.11 +#include "secoid.h" 1.12 +#include "cryptohi.h" 1.13 + 1.14 +/* maximum supported modulus length in bits (indicate problem if over this) */ 1.15 +#define MAX_MODULUS (1024) 1.16 + 1.17 + 1.18 +static void Usage(char *progName) 1.19 +{ 1.20 + fprintf(stderr, "Usage: %s [aAvf] [certtocheck] [issuingcert]\n", 1.21 + progName); 1.22 + fprintf(stderr, "%-20s Cert to check is base64 encoded\n", 1.23 + "-a"); 1.24 + fprintf(stderr, "%-20s Issuer's cert is base64 encoded\n", 1.25 + "-A"); 1.26 + fprintf(stderr, "%-20s Verbose (indicate decoding progress etc.)\n", 1.27 + "-v"); 1.28 + fprintf(stderr, "%-20s Force sanity checks even if pretty print fails.\n", 1.29 + "-f"); 1.30 + fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", 1.31 + "-o output"); 1.32 + fprintf(stderr, "%-20s Specify the input type (no default)\n", 1.33 + "-t type"); 1.34 + exit(-1); 1.35 +} 1.36 + 1.37 + 1.38 +/* 1.39 + * Check integer field named fieldName, printing out results and 1.40 + * returning the length of the integer in bits 1.41 + */ 1.42 + 1.43 +static 1.44 +int checkInteger(SECItem *intItem, char *fieldName, int verbose) 1.45 +{ 1.46 + int len, bitlen; 1.47 + if (verbose) { 1.48 + printf("Checking %s\n", fieldName); 1.49 + } 1.50 + 1.51 + len = intItem->len; 1.52 + 1.53 + if (len && (intItem->data[0] & 0x80)) { 1.54 + printf("PROBLEM: %s is NEGATIVE 2's-complement integer.\n", 1.55 + fieldName); 1.56 + } 1.57 + 1.58 + 1.59 + /* calculate bit length and check for unnecessary leading zeros */ 1.60 + bitlen = len << 3; 1.61 + if (len > 1 && intItem->data[0] == 0) { 1.62 + /* leading zero byte(s) */ 1.63 + if (!(intItem->data[1] & 0x80)) { 1.64 + printf("PROBLEM: %s has unneeded leading zeros. Violates DER.\n", 1.65 + fieldName); 1.66 + } 1.67 + /* strip leading zeros in length calculation */ 1.68 + { 1.69 + int i=0; 1.70 + while (bitlen > 8 && intItem->data[i] == 0) { 1.71 + bitlen -= 8; 1.72 + i++; 1.73 + } 1.74 + } 1.75 + } 1.76 + return bitlen; 1.77 +} 1.78 + 1.79 + 1.80 + 1.81 + 1.82 +static 1.83 +void checkName(CERTName *n, char *fieldName, int verbose) 1.84 +{ 1.85 + char *v=0; 1.86 + if (verbose) { 1.87 + printf("Checking %s\n", fieldName); 1.88 + } 1.89 + 1.90 + v = CERT_GetCountryName(n); 1.91 + if (!v) { 1.92 + printf("PROBLEM: %s lacks Country Name (C)\n", 1.93 + fieldName); 1.94 + } 1.95 + PORT_Free(v); 1.96 + 1.97 + v = CERT_GetOrgName(n); 1.98 + if (!v) { 1.99 + printf("PROBLEM: %s lacks Organization Name (O)\n", 1.100 + fieldName); 1.101 + } 1.102 + PORT_Free(v); 1.103 + 1.104 + v = CERT_GetOrgUnitName(n); 1.105 + if (!v) { 1.106 + printf("WARNING: %s lacks Organization Unit Name (OU)\n", 1.107 + fieldName); 1.108 + } 1.109 + PORT_Free(v); 1.110 + 1.111 + v = CERT_GetCommonName(n); 1.112 + if (!v) { 1.113 + printf("PROBLEM: %s lacks Common Name (CN)\n", 1.114 + fieldName); 1.115 + } 1.116 + PORT_Free(v); 1.117 +} 1.118 + 1.119 + 1.120 +static 1.121 +SECStatus 1.122 +OurVerifyData(unsigned char *buf, int len, SECKEYPublicKey *key, 1.123 + SECItem *sig, SECAlgorithmID *sigAlgorithm) 1.124 +{ 1.125 + SECStatus rv; 1.126 + VFYContext *cx; 1.127 + SECOidData *sigAlgOid, *oiddata; 1.128 + SECOidTag sigAlgTag; 1.129 + SECOidTag hashAlgTag; 1.130 + int showDigestOid=0; 1.131 + 1.132 + cx = VFY_CreateContextWithAlgorithmID(key, sig, sigAlgorithm, &hashAlgTag, 1.133 + NULL); 1.134 + if (cx == NULL) 1.135 + return SECFailure; 1.136 + 1.137 + sigAlgOid = SECOID_FindOID(&sigAlgorithm->algorithm); 1.138 + if (sigAlgOid == 0) 1.139 + return SECFailure; 1.140 + sigAlgTag = sigAlgOid->offset; 1.141 + 1.142 + 1.143 + if (showDigestOid) { 1.144 + oiddata = SECOID_FindOIDByTag(hashAlgTag); 1.145 + if ( oiddata ) { 1.146 + printf("PROBLEM: (cont) Digest OID is %s\n", oiddata->desc); 1.147 + } else { 1.148 + SECU_PrintAsHex(stdout, 1.149 + &oiddata->oid, "PROBLEM: UNKNOWN OID", 0); 1.150 + } 1.151 + } 1.152 + 1.153 + rv = VFY_Begin(cx); 1.154 + if (rv == SECSuccess) { 1.155 + rv = VFY_Update(cx, buf, len); 1.156 + if (rv == SECSuccess) 1.157 + rv = VFY_End(cx); 1.158 + } 1.159 + 1.160 + VFY_DestroyContext(cx, PR_TRUE); 1.161 + return rv; 1.162 +} 1.163 + 1.164 + 1.165 + 1.166 +static 1.167 +SECStatus 1.168 +OurVerifySignedData(CERTSignedData *sd, CERTCertificate *cert) 1.169 +{ 1.170 + SECItem sig; 1.171 + SECKEYPublicKey *pubKey = 0; 1.172 + SECStatus rv; 1.173 + 1.174 + /* check the certificate's validity */ 1.175 + rv = CERT_CertTimesValid(cert); 1.176 + if ( rv ) { 1.177 + return(SECFailure); 1.178 + } 1.179 + 1.180 + /* get cert's public key */ 1.181 + pubKey = CERT_ExtractPublicKey(cert); 1.182 + if ( !pubKey ) { 1.183 + return(SECFailure); 1.184 + } 1.185 + 1.186 + /* check the signature */ 1.187 + sig = sd->signature; 1.188 + DER_ConvertBitString(&sig); 1.189 + rv = OurVerifyData(sd->data.data, sd->data.len, pubKey, &sig, 1.190 + &sd->signatureAlgorithm); 1.191 + 1.192 + SECKEY_DestroyPublicKey(pubKey); 1.193 + 1.194 + if ( rv ) { 1.195 + return(SECFailure); 1.196 + } 1.197 + 1.198 + return(SECSuccess); 1.199 +} 1.200 + 1.201 + 1.202 + 1.203 + 1.204 +static 1.205 +CERTCertificate *createEmptyCertificate(void) 1.206 +{ 1.207 + PLArenaPool *arena = 0; 1.208 + CERTCertificate *c = 0; 1.209 + 1.210 + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.211 + if ( !arena ) { 1.212 + return 0; 1.213 + } 1.214 + 1.215 + 1.216 + c = (CERTCertificate *) PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); 1.217 + 1.218 + if (c) { 1.219 + c->referenceCount = 1; 1.220 + c->arena = arena; 1.221 + } else { 1.222 + PORT_FreeArena(arena,PR_TRUE); 1.223 + } 1.224 + 1.225 + return c; 1.226 +} 1.227 + 1.228 + 1.229 + 1.230 + 1.231 +int main(int argc, char **argv) 1.232 +{ 1.233 + int rv, verbose=0, force=0; 1.234 + int ascii=0, issuerAscii=0; 1.235 + char *progName=0; 1.236 + PRFileDesc *inFile=0, *issuerCertFile=0; 1.237 + SECItem derCert, derIssuerCert; 1.238 + PLArenaPool *arena=0; 1.239 + CERTSignedData *signedData=0; 1.240 + CERTCertificate *cert=0, *issuerCert=0; 1.241 + SECKEYPublicKey *rsapubkey=0; 1.242 + SECAlgorithmID md5WithRSAEncryption, md2WithRSAEncryption; 1.243 + SECAlgorithmID sha1WithRSAEncryption, rsaEncryption; 1.244 + SECItem spk; 1.245 + int selfSigned=0; 1.246 + int invalid=0; 1.247 + char *inFileName = NULL, *issuerCertFileName = NULL; 1.248 + PLOptState *optstate; 1.249 + PLOptStatus status; 1.250 + 1.251 + PORT_Memset(&md5WithRSAEncryption, 0, sizeof(md5WithRSAEncryption)); 1.252 + PORT_Memset(&md2WithRSAEncryption, 0, sizeof(md2WithRSAEncryption)); 1.253 + PORT_Memset(&sha1WithRSAEncryption, 0, sizeof(sha1WithRSAEncryption)); 1.254 + PORT_Memset(&rsaEncryption, 0, sizeof(rsaEncryption)); 1.255 + 1.256 + progName = strrchr(argv[0], '/'); 1.257 + progName = progName ? progName+1 : argv[0]; 1.258 + 1.259 + optstate = PL_CreateOptState(argc, argv, "aAvf"); 1.260 + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 1.261 + switch (optstate->option) { 1.262 + case 'v': 1.263 + verbose = 1; 1.264 + break; 1.265 + 1.266 + case 'f': 1.267 + force = 1; 1.268 + break; 1.269 + 1.270 + case 'a': 1.271 + ascii = 1; 1.272 + break; 1.273 + 1.274 + case 'A': 1.275 + issuerAscii = 1; 1.276 + break; 1.277 + 1.278 + case '\0': 1.279 + if (!inFileName) 1.280 + inFileName = PL_strdup(optstate->value); 1.281 + else if (!issuerCertFileName) 1.282 + issuerCertFileName = PL_strdup(optstate->value); 1.283 + else 1.284 + Usage(progName); 1.285 + break; 1.286 + } 1.287 + } 1.288 + 1.289 + if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) { 1.290 + /* insufficient or excess args */ 1.291 + Usage(progName); 1.292 + } 1.293 + 1.294 + inFile = PR_Open(inFileName, PR_RDONLY, 0); 1.295 + if (!inFile) { 1.296 + fprintf(stderr, "%s: unable to open \"%s\" for reading\n", 1.297 + progName, inFileName); 1.298 + exit(1); 1.299 + } 1.300 + 1.301 + issuerCertFile = PR_Open(issuerCertFileName, PR_RDONLY, 0); 1.302 + if (!issuerCertFile) { 1.303 + fprintf(stderr, "%s: unable to open \"%s\" for reading\n", 1.304 + progName, issuerCertFileName); 1.305 + exit(1); 1.306 + } 1.307 + 1.308 + if (SECU_ReadDERFromFile(&derCert, inFile, ascii, PR_FALSE) != SECSuccess) { 1.309 + printf("Couldn't read input certificate as DER binary or base64\n"); 1.310 + exit(1); 1.311 + } 1.312 + 1.313 + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.314 + if (arena == 0) { 1.315 + fprintf(stderr,"%s: can't allocate scratch arena!", progName); 1.316 + exit(1); 1.317 + } 1.318 + 1.319 + if (issuerCertFile) { 1.320 + CERTSignedData *issuerCertSD=0; 1.321 + if (SECU_ReadDERFromFile(&derIssuerCert, issuerCertFile, issuerAscii, 1.322 + PR_FALSE) != SECSuccess) { 1.323 + printf("Couldn't read issuer certificate as DER binary or base64.\n"); 1.324 + exit(1); 1.325 + } 1.326 + issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData); 1.327 + if (!issuerCertSD) { 1.328 + fprintf(stderr,"%s: can't allocate issuer signed data!", progName); 1.329 + exit(1); 1.330 + } 1.331 + rv = SEC_ASN1DecodeItem(arena, issuerCertSD, 1.332 + SEC_ASN1_GET(CERT_SignedDataTemplate), 1.333 + &derIssuerCert); 1.334 + if (rv) { 1.335 + fprintf(stderr, "%s: Issuer cert isn't X509 SIGNED Data?\n", 1.336 + progName); 1.337 + exit(1); 1.338 + } 1.339 + issuerCert = createEmptyCertificate(); 1.340 + if (!issuerCert) { 1.341 + printf("%s: can't allocate space for issuer cert.", progName); 1.342 + exit(1); 1.343 + } 1.344 + rv = SEC_ASN1DecodeItem(arena, issuerCert, 1.345 + SEC_ASN1_GET(CERT_CertificateTemplate), 1.346 + &issuerCertSD->data); 1.347 + if (rv) { 1.348 + printf("%s: Does not appear to be an X509 Certificate.\n", 1.349 + progName); 1.350 + exit(1); 1.351 + } 1.352 + } 1.353 + 1.354 + signedData = PORT_ArenaZNew(arena,CERTSignedData); 1.355 + if (!signedData) { 1.356 + fprintf(stderr,"%s: can't allocate signedData!", progName); 1.357 + exit(1); 1.358 + } 1.359 + 1.360 + rv = SEC_ASN1DecodeItem(arena, signedData, 1.361 + SEC_ASN1_GET(CERT_SignedDataTemplate), 1.362 + &derCert); 1.363 + if (rv) { 1.364 + fprintf(stderr, "%s: Does not appear to be X509 SIGNED Data.\n", 1.365 + progName); 1.366 + exit(1); 1.367 + } 1.368 + 1.369 + if (verbose) { 1.370 + printf("Decoded ok as X509 SIGNED data.\n"); 1.371 + } 1.372 + 1.373 + cert = createEmptyCertificate(); 1.374 + if (!cert) { 1.375 + fprintf(stderr, "%s: can't allocate cert", progName); 1.376 + exit(1); 1.377 + } 1.378 + 1.379 + rv = SEC_ASN1DecodeItem(arena, cert, 1.380 + SEC_ASN1_GET(CERT_CertificateTemplate), 1.381 + &signedData->data); 1.382 + if (rv) { 1.383 + fprintf(stderr, "%s: Does not appear to be an X509 Certificate.\n", 1.384 + progName); 1.385 + exit(1); 1.386 + } 1.387 + 1.388 + 1.389 + if (verbose) { 1.390 + printf("Decoded ok as an X509 certificate.\n"); 1.391 + } 1.392 + 1.393 + SECU_RegisterDynamicOids(); 1.394 + rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0, 1.395 + SECU_PrintCertificate); 1.396 + 1.397 + if (rv) { 1.398 + fprintf(stderr, "%s: Unable to pretty print cert. Error: %d\n", 1.399 + progName, PORT_GetError()); 1.400 + if (!force) { 1.401 + exit(1); 1.402 + } 1.403 + } 1.404 + 1.405 + 1.406 + /* Do various checks on the cert */ 1.407 + 1.408 + printf("\n"); 1.409 + 1.410 + /* Check algorithms */ 1.411 + SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption, 1.412 + SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL); 1.413 + 1.414 + SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption, 1.415 + SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL); 1.416 + 1.417 + SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption, 1.418 + SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL); 1.419 + 1.420 + SECOID_SetAlgorithmID(arena, &rsaEncryption, 1.421 + SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); 1.422 + 1.423 + { 1.424 + int isMD5RSA = (SECOID_CompareAlgorithmID(&cert->signature, 1.425 + &md5WithRSAEncryption) == 0); 1.426 + int isMD2RSA = (SECOID_CompareAlgorithmID(&cert->signature, 1.427 + &md2WithRSAEncryption) == 0); 1.428 + int isSHA1RSA = (SECOID_CompareAlgorithmID(&cert->signature, 1.429 + &sha1WithRSAEncryption) == 0); 1.430 + 1.431 + if (verbose) { 1.432 + printf("\nDoing algorithm checks.\n"); 1.433 + } 1.434 + 1.435 + if (!(isMD5RSA || isMD2RSA || isSHA1RSA)) { 1.436 + printf("PROBLEM: Signature not PKCS1 MD5, MD2, or SHA1 + RSA.\n"); 1.437 + } else if (!isMD5RSA) { 1.438 + printf("WARNING: Signature not PKCS1 MD5 with RSA Encryption\n"); 1.439 + } 1.440 + 1.441 + if (SECOID_CompareAlgorithmID(&cert->signature, 1.442 + &signedData->signatureAlgorithm)) { 1.443 + printf("PROBLEM: Algorithm in sig and certInfo don't match.\n"); 1.444 + } 1.445 + } 1.446 + 1.447 + if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm, 1.448 + &rsaEncryption)) { 1.449 + printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n"); 1.450 + } 1.451 + 1.452 + /* Check further public key properties */ 1.453 + spk = cert->subjectPublicKeyInfo.subjectPublicKey; 1.454 + DER_ConvertBitString(&spk); 1.455 + 1.456 + if (verbose) { 1.457 + printf("\nsubjectPublicKey DER\n"); 1.458 + rv = DER_PrettyPrint(stdout, &spk, PR_FALSE); 1.459 + printf("\n"); 1.460 + } 1.461 + 1.462 + rsapubkey = (SECKEYPublicKey *) 1.463 + PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey)); 1.464 + if (!rsapubkey) { 1.465 + fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName); 1.466 + exit(1); 1.467 + } 1.468 + 1.469 + rv = SEC_ASN1DecodeItem(arena, rsapubkey, 1.470 + SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &spk); 1.471 + if (rv) { 1.472 + printf("PROBLEM: subjectPublicKey is not a DER PKCS1 RSAPublicKey.\n"); 1.473 + } else { 1.474 + int mlen; 1.475 + int pubexp; 1.476 + if (verbose) { 1.477 + printf("Decoded RSA Public Key ok. Doing key checks.\n"); 1.478 + } 1.479 + PORT_Assert(rsapubkey->keyType == rsaKey); /* XXX RSA */ 1.480 + mlen = checkInteger(&rsapubkey->u.rsa.modulus, "Modulus", verbose); 1.481 + printf("INFO: Public Key modulus length in bits: %d\n", mlen); 1.482 + if (mlen > MAX_MODULUS) { 1.483 + printf("PROBLEM: Modulus length exceeds %d bits.\n", 1.484 + MAX_MODULUS); 1.485 + } 1.486 + if (mlen < 512) { 1.487 + printf("WARNING: Short modulus.\n"); 1.488 + } 1.489 + if (mlen != (1 << (ffs(mlen)-1))) { 1.490 + printf("WARNING: Unusual modulus length (not a power of two).\n"); 1.491 + } 1.492 + checkInteger(&rsapubkey->u.rsa.publicExponent, "Public Exponent", 1.493 + verbose); 1.494 + pubexp = DER_GetInteger(&rsapubkey->u.rsa.publicExponent); 1.495 + if (pubexp != 17 && pubexp != 3 && pubexp != 65537) { 1.496 + printf("WARNING: Public exponent not any of: 3, 17, 65537\n"); 1.497 + } 1.498 + } 1.499 + 1.500 + 1.501 + /* Name checks */ 1.502 + checkName(&cert->issuer, "Issuer Name", verbose); 1.503 + checkName(&cert->subject, "Subject Name", verbose); 1.504 + 1.505 + if (issuerCert) { 1.506 + SECComparison c = 1.507 + CERT_CompareName(&cert->issuer, &issuerCert->subject); 1.508 + if (c) { 1.509 + printf("PROBLEM: Issuer Name and Subject in Issuing Cert differ\n"); 1.510 + } 1.511 + } 1.512 + 1.513 + /* Check if self-signed */ 1.514 + selfSigned = (CERT_CompareName(&cert->issuer, &cert->subject) == 0); 1.515 + if (selfSigned) { 1.516 + printf("INFO: Certificate is self signed.\n"); 1.517 + } else { 1.518 + printf("INFO: Certificate is NOT self-signed.\n"); 1.519 + } 1.520 + 1.521 + 1.522 + /* Validity time check */ 1.523 + if (CERT_CertTimesValid(cert) == SECSuccess) { 1.524 + printf("INFO: Inside validity period of certificate.\n"); 1.525 + } else { 1.526 + printf("PROBLEM: Not in validity period of certificate.\n"); 1.527 + invalid = 1; 1.528 + } 1.529 + 1.530 + /* Signature check if self-signed */ 1.531 + if (selfSigned && !invalid) { 1.532 + if (rsapubkey->u.rsa.modulus.len) { 1.533 + SECStatus ver; 1.534 + if (verbose) { 1.535 + printf("Checking self signature.\n"); 1.536 + } 1.537 + ver = OurVerifySignedData(signedData, cert); 1.538 + if (ver != SECSuccess) { 1.539 + printf("PROBLEM: Verification of self-signature failed!\n"); 1.540 + } else { 1.541 + printf("INFO: Self-signature verifies ok.\n"); 1.542 + } 1.543 + } else { 1.544 + printf("INFO: Not checking signature due to key problems.\n"); 1.545 + } 1.546 + } else if (!selfSigned && !invalid && issuerCert) { 1.547 + SECStatus ver; 1.548 + ver = OurVerifySignedData(signedData, issuerCert); 1.549 + if (ver != SECSuccess) { 1.550 + printf("PROBLEM: Verification of issuer's signature failed!\n"); 1.551 + } else { 1.552 + printf("INFO: Issuer's signature verifies ok.\n"); 1.553 + } 1.554 + } else { 1.555 + printf("INFO: Not checking signature.\n"); 1.556 + } 1.557 + 1.558 + return 0; 1.559 +} 1.560 + 1.561 + 1.562 +