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