Example #1
0
SECComparison
SGN_CompareDigestInfo(SGNDigestInfo *a, SGNDigestInfo *b)
{
    SECComparison rv;

    /* Check signature algorithm's */
    rv = SECOID_CompareAlgorithmID(&a->digestAlgorithm, &b->digestAlgorithm);
    if (rv) return rv;

    /* Compare signature block length's */
    rv = SECITEM_CompareItem(&a->digest, &b->digest);
    return rv;
}
Example #2
0
/*
 * SecCmsAlgArrayGetIndexByAlgID - find a specific algorithm in an array of 
 * algorithms.
 *
 * algorithmArray - array of algorithm IDs
 * algid - algorithmid of algorithm to pick
 *
 * Returns:
 *  An integer containing the index of the algorithm in the array or -1 if 
 *  algorithm was not found.
 */
int
SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid)
{
    int i;

    if (algorithmArray == NULL || algorithmArray[0] == NULL)
	return -1;

    for (i = 0; algorithmArray[i] != NULL; i++) {
	if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual)
	    break;	/* bingo */
    }

    if (algorithmArray[i] == NULL)
	return -1;	/* not found */

    return i;
}
Example #3
0
int
handle_connection(
    PRFileDesc *tcp_sock,
    PRFileDesc *model_sock,
    int requestCert)
{
    PRFileDesc *ssl_sock = NULL;
    PRFileDesc *local_file_fd = NULL;
    char *pBuf; /* unused space at end of buf */
    const char *errString;
    PRStatus status;
    int bufRem;    /* unused bytes at end of buf */
    int bufDat;    /* characters received in buf */
    int newln = 0; /* # of consecutive newlns */
    int firstTime = 1;
    int reqLen;
    int rv;
    int numIOVs;
    PRSocketOptionData opt;
    PRIOVec iovs[16];
    char msgBuf[160];
    char buf[10240];
    char fileName[513];
    char *getData = NULL; /* inplace conversion */
    SECItem postData;
    PRBool isOcspRequest = PR_FALSE;
    PRBool isPost;

    postData.data = NULL;
    postData.len = 0;

    pBuf = buf;
    bufRem = sizeof buf;

    VLOG(("httpserv: handle_connection: starting"));
    opt.option = PR_SockOpt_Nonblocking;
    opt.value.non_blocking = PR_FALSE;
    PR_SetSocketOption(tcp_sock, &opt);

    VLOG(("httpserv: handle_connection: starting\n"));
    ssl_sock = tcp_sock;

    if (noDelay) {
        opt.option = PR_SockOpt_NoDelay;
        opt.value.no_delay = PR_TRUE;
        status = PR_SetSocketOption(ssl_sock, &opt);
        if (status != PR_SUCCESS) {
            errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)");
            if (ssl_sock) {
                PR_Close(ssl_sock);
            }
            return SECFailure;
        }
    }

    while (1) {
        const char *post;
        const char *foundStr = NULL;
        const char *tmp = NULL;

        newln = 0;
        reqLen = 0;

        rv = PR_Read(ssl_sock, pBuf, bufRem - 1);
        if (rv == 0 ||
            (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) {
            if (verbose)
                errWarn("HDX PR_Read hit EOF");
            break;
        }
        if (rv < 0) {
            errWarn("HDX PR_Read");
            goto cleanup;
        }
        /* NULL termination */
        pBuf[rv] = 0;
        if (firstTime) {
            firstTime = 0;
        }

        pBuf += rv;
        bufRem -= rv;
        bufDat = pBuf - buf;
        /* Parse the input, starting at the beginning of the buffer.
         * Stop when we detect two consecutive \n's (or \r\n's)
         * as this signifies the end of the GET or POST portion.
         * The posted data follows.
         */
        while (reqLen < bufDat && newln < 2) {
            int octet = buf[reqLen++];
            if (octet == '\n') {
                newln++;
            } else if (octet != '\r') {
                newln = 0;
            }
        }

        /* came to the end of the buffer, or second newln
         * If we didn't get an empty line (CRLFCRLF) then keep on reading.
         */
        if (newln < 2)
            continue;

        /* we're at the end of the HTTP request.
         * If the request is a POST, then there will be one more
         * line of data.
         * This parsing is a hack, but ok for SSL test purposes.
         */
        post = PORT_Strstr(buf, "POST ");
        if (!post || *post != 'P')
            break;

        postData.data = (void *)(buf + reqLen);

        tmp = "content-length: ";
        foundStr = PL_strcasestr(buf, tmp);
        if (foundStr) {
            int expectedPostLen;
            int havePostLen;

            expectedPostLen = atoi(foundStr + strlen(tmp));
            havePostLen = bufDat - reqLen;
            if (havePostLen >= expectedPostLen) {
                postData.len = expectedPostLen;
                break;
            }
        } else {
            /* use legacy hack */
            /* It's a post, so look for the next and final CR/LF. */
            while (reqLen < bufDat && newln < 3) {
                int octet = buf[reqLen++];
                if (octet == '\n') {
                    newln++;
                }
            }
            if (newln == 3)
                break;
        }
    } /* read loop */

    bufDat = pBuf - buf;
    if (bufDat)
        do { /* just close if no data */
            /* Have either (a) a complete get, (b) a complete post, (c) EOF */
            if (reqLen > 0) {
                PRBool isGetOrPost = PR_FALSE;
                unsigned skipChars = 0;
                isPost = PR_FALSE;

                if (!strncmp(buf, getCmd, sizeof getCmd - 1)) {
                    isGetOrPost = PR_TRUE;
                    skipChars = 4;
                } else if (!strncmp(buf, "POST ", 5)) {
                    isGetOrPost = PR_TRUE;
                    isPost = PR_TRUE;
                    skipChars = 5;
                }

                if (isGetOrPost) {
                    char *fnBegin = buf;
                    char *fnEnd;
                    char *fnstart = NULL;
                    PRFileInfo info;

                    fnBegin += skipChars;

                    fnEnd = strpbrk(fnBegin, " \r\n");
                    if (fnEnd) {
                        int fnLen = fnEnd - fnBegin;
                        if (fnLen < sizeof fileName) {
                            strncpy(fileName, fnBegin, fnLen);
                            fileName[fnLen] = 0; /* null terminate */
                            fnstart = fileName;
                            /* strip initial / because our root is the current directory*/
                            while (*fnstart && *fnstart == '/')
                                ++fnstart;
                        }
                    }
                    if (fnstart) {
                        if (!strncmp(fnstart, "ocsp", 4)) {
                            if (isPost) {
                                if (postData.data) {
                                    isOcspRequest = PR_TRUE;
                                }
                            } else {
                                if (!strncmp(fnstart, "ocsp/", 5)) {
                                    isOcspRequest = PR_TRUE;
                                    getData = fnstart + 5;
                                }
                            }
                        } else {
                            /* try to open the file named.
                             * If successful, then write it to the client.
                             */
                            status = PR_GetFileInfo(fnstart, &info);
                            if (status == PR_SUCCESS &&
                                info.type == PR_FILE_FILE &&
                                info.size >= 0) {
                                local_file_fd = PR_Open(fnstart, PR_RDONLY, 0);
                            }
                        }
                    }
                }
            }

            numIOVs = 0;

            iovs[numIOVs].iov_base = (char *)outHeader;
            iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1;
            numIOVs++;

            if (isOcspRequest && caRevoInfos) {
                CERTOCSPRequest *request = NULL;
                PRBool failThisRequest = PR_FALSE;
                PLArenaPool *arena = NULL;

                if (ocspMethodsAllowed == ocspGetOnly && postData.len) {
                    failThisRequest = PR_TRUE;
                } else if (ocspMethodsAllowed == ocspPostOnly && getData) {
                    failThisRequest = PR_TRUE;
                } else if (ocspMethodsAllowed == ocspRandomGetFailure && getData) {
                    if (!(rand() % 2)) {
                        failThisRequest = PR_TRUE;
                    }
                }

                if (failThisRequest) {
                    PR_Write(ssl_sock, outBadRequestHeader, strlen(outBadRequestHeader));
                    break;
                }
                /* get is base64, post is binary.
                 * If we have base64, convert into the (empty) postData array.
                 */
                if (getData) {
                    if (urldecode_base64chars_inplace(getData) == SECSuccess) {
                        /* The code below can handle a NULL arena */
                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                        NSSBase64_DecodeBuffer(arena, &postData, getData, strlen(getData));
                    }
                }
                if (postData.len) {
                    request = CERT_DecodeOCSPRequest(&postData);
                }
                if (arena) {
                    PORT_FreeArena(arena, PR_FALSE);
                }
                if (!request || !request->tbsRequest ||
                    !request->tbsRequest->requestList ||
                    !request->tbsRequest->requestList[0]) {
                    PORT_Sprintf(msgBuf, "Cannot decode OCSP request.\r\n");

                    iovs[numIOVs].iov_base = msgBuf;
                    iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                    numIOVs++;
                } else {
                    /* TODO: support more than one request entry */
                    CERTOCSPCertID *reqid = request->tbsRequest->requestList[0]->reqCert;
                    const caRevoInfo *revoInfo = NULL;
                    PRBool unknown = PR_FALSE;
                    PRBool revoked = PR_FALSE;
                    PRTime nextUpdate = 0;
                    PRTime revoDate = 0;
                    PRCList *caRevoIter;

                    caRevoIter = &caRevoInfos->link;
                    do {
                        CERTOCSPCertID *caid;

                        revoInfo = (caRevoInfo *)caRevoIter;
                        caid = revoInfo->id;

                        if (SECOID_CompareAlgorithmID(&reqid->hashAlgorithm,
                                                      &caid->hashAlgorithm) == SECEqual &&
                            SECITEM_CompareItem(&reqid->issuerNameHash,
                                                &caid->issuerNameHash) == SECEqual &&
                            SECITEM_CompareItem(&reqid->issuerKeyHash,
                                                &caid->issuerKeyHash) == SECEqual) {
                            break;
                        }
                        revoInfo = NULL;
                        caRevoIter = PR_NEXT_LINK(caRevoIter);
                    } while (caRevoIter != &caRevoInfos->link);

                    if (!revoInfo) {
                        unknown = PR_TRUE;
                        revoInfo = caRevoInfos;
                    } else {
                        CERTCrl *crl = &revoInfo->crl->crl;
                        CERTCrlEntry *entry = NULL;
                        DER_DecodeTimeChoice(&nextUpdate, &crl->nextUpdate);
                        if (crl->entries) {
                            int iv = 0;
                            /* assign, not compare */
                            while ((entry = crl->entries[iv++])) {
                                if (SECITEM_CompareItem(&reqid->serialNumber,
                                                        &entry->serialNumber) == SECEqual) {
                                    break;
                                }
                            }
                        }
                        if (entry) {
                            /* revoked status response */
                            revoked = PR_TRUE;
                            DER_DecodeTimeChoice(&revoDate, &entry->revocationDate);
                        } else {
                            /* else good status response */
                            if (!isPost && ocspMethodsAllowed == ocspGetUnknown) {
                                unknown = PR_TRUE;
                                nextUpdate = PR_Now() + (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC; /*tomorrow*/
                                revoDate = PR_Now() - (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC;   /*yesterday*/
                            }
                        }
                    }

                    {
                        PRTime now = PR_Now();
                        PLArenaPool *arena = NULL;
                        CERTOCSPSingleResponse *sr;
                        CERTOCSPSingleResponse **singleResponses;
                        SECItem *ocspResponse;

                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

                        if (unknown) {
                            sr = CERT_CreateOCSPSingleResponseUnknown(arena, reqid, now,
                                                                      &nextUpdate);
                        } else if (revoked) {
                            sr = CERT_CreateOCSPSingleResponseRevoked(arena, reqid, now,
                                                                      &nextUpdate, revoDate, NULL);
                        } else {
                            sr = CERT_CreateOCSPSingleResponseGood(arena, reqid, now,
                                                                   &nextUpdate);
                        }

                        /* meaning of value 2: one entry + one end marker */
                        singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse *, 2);
                        singleResponses[0] = sr;
                        singleResponses[1] = NULL;
                        ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena,
                                                                             revoInfo->cert, ocspResponderID_byName, now,
                                                                             singleResponses, &pwdata);

                        if (!ocspResponse) {
                            PORT_Sprintf(msgBuf, "Failed to encode response\r\n");
                            iovs[numIOVs].iov_base = msgBuf;
                            iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                            numIOVs++;
                        } else {
                            PR_Write(ssl_sock, outOcspHeader, strlen(outOcspHeader));
                            PR_Write(ssl_sock, ocspResponse->data, ocspResponse->len);
                            PORT_FreeArena(arena, PR_FALSE);
                        }
                    }
                    CERT_DestroyOCSPRequest(request);
                    break;
                }
            } else if (local_file_fd) {
                PRInt32 bytes;
                int errLen;
                bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader,
                                        sizeof outHeader - 1,
                                        PR_TRANSMITFILE_KEEP_OPEN,
                                        PR_INTERVAL_NO_TIMEOUT);
                if (bytes >= 0) {
                    bytes -= sizeof outHeader - 1;
                    FPRINTF(stderr,
                            "httpserv: PR_TransmitFile wrote %d bytes from %s\n",
                            bytes, fileName);
                    break;
                }
                errString = errWarn("PR_TransmitFile");
                errLen = PORT_Strlen(errString);
                errLen = PR_MIN(errLen, sizeof msgBuf - 1);
                PORT_Memcpy(msgBuf, errString, errLen);
                msgBuf[errLen] = 0;

                iovs[numIOVs].iov_base = msgBuf;
                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                numIOVs++;
            } else if (reqLen <= 0) { /* hit eof */
                PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n",
                             bufDat);

                iovs[numIOVs].iov_base = msgBuf;
                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                numIOVs++;
            } else if (reqLen < bufDat) {
                PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n",
                             bufDat - reqLen);

                iovs[numIOVs].iov_base = msgBuf;
                iovs[numIOVs].iov_len = PORT_Strlen(msgBuf);
                numIOVs++;
            }

            if (reqLen > 0) {
                if (verbose > 1)
                    fwrite(buf, 1, reqLen, stdout); /* display it */

                iovs[numIOVs].iov_base = buf;
                iovs[numIOVs].iov_len = reqLen;
                numIOVs++;
            }

            rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT);
            if (rv < 0) {
                errWarn("PR_Writev");
                break;
            }

        } while (0);
Example #4
0
int main(int argc, char **argv)
{
    int verbose=0, force=0;
    int ascii=0, issuerAscii=0;
    char *progName=0;
    PRFileDesc *inFile=0, *issuerCertFile=0;
    SECItem derCert, derIssuerCert;
    PLArenaPool *arena=0;
    CERTSignedData *signedData=0;
    CERTCertificate *cert=0, *issuerCert=0;
    SECKEYPublicKey *rsapubkey=0;
    SECAlgorithmID md5WithRSAEncryption, md2WithRSAEncryption;
    SECAlgorithmID sha1WithRSAEncryption, rsaEncryption;
    SECItem spk;
    int selfSigned=0;
    int invalid=0;
    char *inFileName = NULL, *issuerCertFileName = NULL;
    PLOptState *optstate;
    PLOptStatus status;
    SECStatus rv;

    PORT_Memset(&md5WithRSAEncryption, 0, sizeof(md5WithRSAEncryption));
    PORT_Memset(&md2WithRSAEncryption, 0, sizeof(md2WithRSAEncryption));
    PORT_Memset(&sha1WithRSAEncryption, 0, sizeof(sha1WithRSAEncryption));
    PORT_Memset(&rsaEncryption, 0, sizeof(rsaEncryption));

    progName = strrchr(argv[0], '/');
    progName = progName ? progName+1 : argv[0];

    optstate = PL_CreateOptState(argc, argv, "aAvf");
    while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
	switch (optstate->option) {
	  case 'v':
	    verbose = 1;
	    break;

	  case 'f':
	    force = 1;
	    break;

	  case 'a':
	    ascii = 1;
	    break;

	  case 'A':
	    issuerAscii = 1;
	    break;

	  case '\0':
	    if (!inFileName)
		inFileName = PL_strdup(optstate->value);
	    else if (!issuerCertFileName)
		issuerCertFileName = PL_strdup(optstate->value);
	    else
		Usage(progName);
	    break;
	}
    }

    if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) {
	/* insufficient or excess args */
	Usage(progName);
    }

    inFile = PR_Open(inFileName, PR_RDONLY, 0);
    if (!inFile) {
	fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
	                 progName, inFileName);
	exit(1);
    }

    issuerCertFile = PR_Open(issuerCertFileName, PR_RDONLY, 0);
    if (!issuerCertFile) {
	fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
	                 progName, issuerCertFileName);
	exit(1);
    }

    if (SECU_ReadDERFromFile(&derCert, inFile, ascii, PR_FALSE) != SECSuccess) {
	printf("Couldn't read input certificate as DER binary or base64\n");
	exit(1);
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == 0) {
	fprintf(stderr,"%s: can't allocate scratch arena!", progName);
	exit(1);
    }

    if (issuerCertFile) {
	CERTSignedData *issuerCertSD=0;
	if (SECU_ReadDERFromFile(&derIssuerCert, issuerCertFile, issuerAscii,
	                         PR_FALSE) != SECSuccess) {
	    printf("Couldn't read issuer certificate as DER binary or base64.\n");
	    exit(1);
	}
	issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData);
	if (!issuerCertSD) {
	    fprintf(stderr,"%s: can't allocate issuer signed data!", progName);
	    exit(1);
	}
	rv = SEC_ASN1DecodeItem(arena, issuerCertSD, 
	                        SEC_ASN1_GET(CERT_SignedDataTemplate),
				&derIssuerCert);
	if (rv) {
	    fprintf(stderr, "%s: Issuer cert isn't X509 SIGNED Data?\n",
		    progName);
	    exit(1);
	}
	issuerCert = createEmptyCertificate();
	if (!issuerCert) {
	    printf("%s: can't allocate space for issuer cert.", progName);
	    exit(1);
	}
	rv = SEC_ASN1DecodeItem(arena, issuerCert, 
	                    SEC_ASN1_GET(CERT_CertificateTemplate),
			    &issuerCertSD->data);
	if (rv) {
	    printf("%s: Does not appear to be an X509 Certificate.\n",
		   progName);
	    exit(1);
	}
    }

    signedData =  PORT_ArenaZNew(arena,CERTSignedData);
    if (!signedData) {
	fprintf(stderr,"%s: can't allocate signedData!", progName);
	exit(1);
    }

    rv = SEC_ASN1DecodeItem(arena, signedData, 
                            SEC_ASN1_GET(CERT_SignedDataTemplate), 
			    &derCert);
    if (rv) {
	fprintf(stderr, "%s: Does not appear to be X509 SIGNED Data.\n",
		progName);
	exit(1);
    }

    if (verbose) {
	printf("Decoded ok as X509 SIGNED data.\n");
    }

    cert = createEmptyCertificate();
    if (!cert) {
	fprintf(stderr, "%s: can't allocate cert", progName);
	exit(1);
    }

    rv = SEC_ASN1DecodeItem(arena, cert, 
                        SEC_ASN1_GET(CERT_CertificateTemplate), 
			&signedData->data);
    if (rv) {
	fprintf(stderr, "%s: Does not appear to be an X509 Certificate.\n",
		progName);
	exit(1);
    }


    if (verbose) {
	printf("Decoded ok as an X509 certificate.\n");
    }

    SECU_RegisterDynamicOids();
    rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0,
			      (SECU_PPFunc)SECU_PrintCertificate);

    if (rv) {
	fprintf(stderr, "%s: Unable to pretty print cert. Error: %d\n",
		progName, PORT_GetError());
	if (!force) {
	    exit(1);
	}
    }


    /* Do various checks on the cert */

    printf("\n");

    /* Check algorithms */
    rv = SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption,
		       SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL);
    if (rv) {
	fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION.\n",
                progName);
	exit(1);
    }

    rv = SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption,
		       SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL);
    if (rv) {
	fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION.\n",
                progName);
	exit(1);
    }

    rv = SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption,
		       SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL);
    if (rv) {
	fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION.\n",
                progName);
	exit(1);
    }

    rv = SECOID_SetAlgorithmID(arena, &rsaEncryption,
		       SEC_OID_PKCS1_RSA_ENCRYPTION, NULL);
    if (rv) {
	fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_RSA_ENCRYPTION.\n",
                progName);
	exit(1);
    }

    {
	int isMD5RSA = (SECOID_CompareAlgorithmID(&cert->signature,
					       &md5WithRSAEncryption) == 0);
	int isMD2RSA = (SECOID_CompareAlgorithmID(&cert->signature,
					       &md2WithRSAEncryption) == 0);
	int isSHA1RSA = (SECOID_CompareAlgorithmID(&cert->signature,
					       &sha1WithRSAEncryption) == 0);

	if (verbose) {
	    printf("\nDoing algorithm checks.\n");
	}

	if (!(isMD5RSA || isMD2RSA || isSHA1RSA)) {
	    printf("PROBLEM: Signature not PKCS1 MD5, MD2, or SHA1 + RSA.\n");
	} else if (!isMD5RSA) {
	    printf("WARNING: Signature not PKCS1 MD5 with RSA Encryption\n");
	}

	if (SECOID_CompareAlgorithmID(&cert->signature,
				   &signedData->signatureAlgorithm)) {
	    printf("PROBLEM: Algorithm in sig and certInfo don't match.\n");
	}
    }

    if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm,
			       &rsaEncryption)) {
	printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n");
    }

    /* Check further public key properties */
    spk = cert->subjectPublicKeyInfo.subjectPublicKey;
    DER_ConvertBitString(&spk);

    if (verbose) {
	printf("\nsubjectPublicKey DER\n");
	rv = DER_PrettyPrint(stdout, &spk, PR_FALSE);
	printf("\n");
    }

    rsapubkey = (SECKEYPublicKey *) 
	             PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey));
    if (!rsapubkey) {
	fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName);
	exit(1);
    }

    rv = SEC_ASN1DecodeItem(arena, rsapubkey, 
                            SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &spk);
    if (rv) {
	printf("PROBLEM: subjectPublicKey is not a DER PKCS1 RSAPublicKey.\n");
    } else {
	int mlen;
	int pubexp;
	if (verbose) {
	    printf("Decoded RSA Public Key ok.  Doing key checks.\n");
	}
	PORT_Assert(rsapubkey->keyType == rsaKey); /* XXX RSA */
	mlen = checkInteger(&rsapubkey->u.rsa.modulus, "Modulus", verbose);
	printf("INFO: Public Key modulus length in bits: %d\n", mlen);
	if (mlen > MAX_MODULUS) {
	    printf("PROBLEM: Modulus length exceeds %d bits.\n",
		   MAX_MODULUS);
	}
	if (mlen < 512) {
	    printf("WARNING: Short modulus.\n");
	}
	if (mlen != (1 << (ffs(mlen)-1))) {
	    printf("WARNING: Unusual modulus length (not a power of two).\n");
	}
	checkInteger(&rsapubkey->u.rsa.publicExponent, "Public Exponent",
                     verbose);
	pubexp = DER_GetInteger(&rsapubkey->u.rsa.publicExponent);
	if (pubexp != 17 && pubexp != 3 && pubexp != 65537) {
	    printf("WARNING: Public exponent not any of: 3, 17, 65537\n");
	}
    }


    /* Name checks */
    checkName(&cert->issuer, "Issuer Name", verbose);
    checkName(&cert->subject, "Subject Name", verbose);

    if (issuerCert) {
	SECComparison c =
	    CERT_CompareName(&cert->issuer, &issuerCert->subject);
	if (c) {
         printf("PROBLEM: Issuer Name and Subject in Issuing Cert differ\n");
        }
    }

    /* Check if self-signed */
    selfSigned = (CERT_CompareName(&cert->issuer, &cert->subject) == 0);
    if (selfSigned) {
	printf("INFO: Certificate is self signed.\n");
    } else {
	printf("INFO: Certificate is NOT self-signed.\n");
    }


    /* Validity time check */
    if (CERT_CertTimesValid(cert) == SECSuccess) {
	printf("INFO: Inside validity period of certificate.\n");
    } else {
	printf("PROBLEM: Not in validity period of certificate.\n");
	invalid = 1;
    }

    /* Signature check if self-signed */
    if (selfSigned && !invalid) {
	if (rsapubkey->u.rsa.modulus.len) {
	    SECStatus ver;
	    if (verbose) {
		printf("Checking self signature.\n");
	    }
	    ver = OurVerifySignedData(signedData, cert);
	    if (ver != SECSuccess) {
		printf("PROBLEM: Verification of self-signature failed!\n");
	    } else {
		printf("INFO: Self-signature verifies ok.\n");
	    }
	} else {
	    printf("INFO: Not checking signature due to key problems.\n");
	}
    } else if (!selfSigned && !invalid && issuerCert) {
	SECStatus ver;
	ver = OurVerifySignedData(signedData, issuerCert);
	if (ver != SECSuccess) {
	    printf("PROBLEM: Verification of issuer's signature failed!\n");
	} else {
	    printf("INFO: Issuer's signature verifies ok.\n");
	}
    } else {
	printf("INFO: Not checking signature.\n");
    }

    return 0;    
}