Bug 1308026 - JSS certificate validation does not pass up exact error from NSS, edewata
authorElio Maldonado <emaldona@redhat.com>
Wed, 08 Feb 2017 19:47:17 -0800
changeset 2137 5cd3eb611cebe7bc336446d3a0d3707c483ef2a5
parent 2136 af1a5fa71a69e1f2a021764d3113d00b167b1828
child 2138 f125bb5adbf68090aa69a25457133ff43efbc1b3
push id21
push useremaldona@redhat.com
push dateThu, 09 Feb 2017 14:42:36 +0000
bugs1308026
Bug 1308026 - JSS certificate validation does not pass up exact error from NSS, edewata - The following was in the original version of the patch - - From 3c4ca8a2010889fe292704ebcc8b922f77f2f7c2 Mon Sep 17 00:00:00 2001 - From: "Endi S. Dewata" <edewata@redhat.com> - Date: Wed, 9 Dec 2015 00:30:50 +0100 - Subject: [PATCH] Added verifyCertificate() method. - - A new CryptoManager.verifyCertificate() method has been added as - an alternative to isCertValid(). If there is a certificate - validation problem, the method will throw a CertificateValidation - exception that contains the NSS error message and code. The - exception will also provide a stack trace to help troubleshoot - validation issues. - - https://fedorahosted.org/pki/ticket/850 - --- - .../jss/org/mozilla/jss/CryptoManager.java | 54 ++++++++------ - mozilla/security/jss/org/mozilla/jss/PK11Finder.c | 83 +++++++++++++++++++--- - .../jss/org/mozilla/jss/util/jss_exceptions.h | 2 + - 3 files changed, 110 insertions(+), 29 deletions(-) - - which was lost when it was regenerated with hg.
org/mozilla/jss/CryptoManager.java
org/mozilla/jss/PK11Finder.c
org/mozilla/jss/util/jss_exceptions.h
--- a/org/mozilla/jss/CryptoManager.java
+++ b/org/mozilla/jss/CryptoManager.java
@@ -1540,28 +1540,53 @@ public final class CryptoManager impleme
      * @exception ObjectNotFoundException If no certificate could be found
      *      with the given nickname.
      */
 
     public boolean isCertValid(String nickname, boolean checkSig,
             CertificateUsage certificateUsage)
         throws ObjectNotFoundException, InvalidNicknameException
     {
-        if (nickname==null) {
-            throw new InvalidNicknameException("Nickname must be non-null");
+        try {
+            verifyCertificate(nickname, checkSig, certificateUsage);
+            return true;
+        } catch (ObjectNotFoundException | InvalidNicknameException e) {
+            throw e;
+        } catch (CertificateException e) {
+            return false;
         }
-        // 0 certificate usage was supposed to get current usage, however,
-        // it is not exposed at this point
-        return verifyCertificateNowNative(nickname,
-              checkSig,
-              (certificateUsage == null) ? 0:certificateUsage.getUsage());
     }
 
-    private native boolean verifyCertificateNowNative(String nickname,
-        boolean checkSig, int certificateUsage) throws ObjectNotFoundException;
+    /**
+     * Verify a certificate that exists in the given cert database,
+     * check if it's valid and that we trust the issuer. Verify time
+     * against now.
+     * @param nickname nickname of the certificate to verify.
+     * @param checkSig verify the signature of the certificate
+     * @param certificateUsage see certificate usage defined to verify certificate
+     *
+     * @exception InvalidNicknameException If the nickname is null.
+     * @exception ObjectNotFoundException If no certificate could be found
+     *      with the given nickname.
+     * @exception CertificateException If certificate is invalid.
+     */
+    public void verifyCertificate(String nickname,
+            boolean checkSig,
+            CertificateUsage certificateUsage)
+                    throws ObjectNotFoundException, InvalidNicknameException, CertificateException {
+        int usage = certificateUsage == null ? 0 : certificateUsage.getUsage();
+        verifyCertificateNowNative(nickname, checkSig, usage);
+    }
+
+    private native void verifyCertificateNowNative(
+            String nickname,
+            boolean checkSig,
+            int certificateUsage)
+                    throws ObjectNotFoundException, InvalidNicknameException, CertificateException;
+
 
     /**
      * note: this method calls obsolete function in NSS
      *
      * Verify a certificate that exists in the given cert database,
      * check if is valid and that we trust the issuer. Verify time
      * against Now.
      * @param nickname The nickname of the certificate to verify.
--- a/org/mozilla/jss/PK11Finder.c
+++ b/org/mozilla/jss/PK11Finder.c
@@ -1544,60 +1544,102 @@ finish:
     if (crl) {
         SEC_DestroyCrl(crl);
     }
 }
 
 /***********************************************************************
  * CryptoManager.verifyCertificateNowNative
  *
- * Returns JNI_TRUE if success, JNI_FALSE otherwise
+ * Verify a certificate that exists in the given cert database,
+ * check if it's valid and that we trust the issuer. Verify time
+ * against now.
+ * @param nickname nickname of the certificate to verify.
+ * @param checkSig verify the signature of the certificate
+ * @param certificateUsage see certificate usage defined to verify certificate
+ *
+ * @exception InvalidNicknameException If the nickname is null.
+ * @exception ObjectNotFoundException If no certificate could be found
+ *      with the given nickname.
+ * @exception CertificateException If certificate is invalid.
  */
-JNIEXPORT jboolean JNICALL
+JNIEXPORT void JNICALL
 Java_org_mozilla_jss_CryptoManager_verifyCertificateNowNative(JNIEnv *env,
-        jobject self, jstring nickString, jboolean checkSig, jint required_certificateUsage)
+        jobject self, jstring nickString, jboolean checkSig, jint certificateUsage)
 {
-    SECStatus         rv    = SECFailure;
-    SECCertificateUsage      certificateUsage;
-    SECCertificateUsage      currUsage;  /* unexposed for now */
-    CERTCertificate   *cert=NULL;
-    char *nickname=NULL;
+    SECCertificateUsage      currUsage = 0x000;  /* unexposed for now */
+    SECStatus                rv = SECFailure;
+    CERTCertificate          *cert = NULL;
+    char                     *nickname = NULL;
+    
+    if (nickString == NULL) {
+        JSS_throwMsg(env, INVALID_NICKNAME_EXCEPTION, "Missing certificate nickname");
+        goto finish;
+    }
 
     nickname = (char *) (*env)->GetStringUTFChars(env, nickString, NULL);
-    if( nickname == NULL ) {
-         goto finish;
+    if (nickname == NULL) {
+        JSS_throwMsg(env, INVALID_NICKNAME_EXCEPTION, "Missing certificate nickname");
+        goto finish;
     }
 
-    certificateUsage = required_certificateUsage;
-
     cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), nickname);
 
     if (cert == NULL) {
         JSS_throw(env, OBJECT_NOT_FOUND_EXCEPTION);
         goto finish;
-    } else {
-    /* 0 for certificateUsage in call to CERT_VerifyCertificateNow to
-     * just get the current usage (which we are not passing back for now
-     * but will bypass the certificate usage check
+    }
+
+    cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), nickname);
+
+    if (cert == NULL) {
+        char *msgBuf;
+        msgBuf = PR_smprintf("Certificate not found: %s", nickname);
+        JSS_throwMsg(env, OBJECT_NOT_FOUND_EXCEPTION, msgBuf);
+        PR_Free(msgBuf);
+        goto finish;
+    }
+
+    /* 0 for certificateUsage in call to CERT_VerifyCertificateNow will
+     * retrieve the current valid usage into currUsage
      */
-        rv = CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), cert,
-            checkSig, certificateUsage, NULL, &currUsage );
+    rv = CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), cert,
+        checkSig, certificateUsage, NULL, &currUsage);
+
+    if (rv != SECSuccess) {
+        JSS_throwMsgPrErr(env, CERTIFICATE_EXCEPTION, "Invalid certificate");
+        goto finish;
+    }
+
+    if ((certificateUsage == 0x0000) &&
+        (currUsage ==
+            ( certUsageUserCertImport |
+            certUsageVerifyCA |
+            certUsageProtectedObjectSigner |
+            certUsageAnyCA ))) {
+
+        /* The certificate is good for nothing.
+         * The following usages cannot be verified:
+         *   certUsageAnyCA
+         *   certUsageProtectedObjectSigner
+         *   certUsageUserCertImport
+         *   certUsageVerifyCA
+         *   (0x0b80)
+         */
+
+        JSS_throwMsgPrErr(env, CERTIFICATE_EXCEPTION, "Unusable certificate");
+        goto finish;
     }
 
 finish:
-    if(nickname != NULL) {
-      (*env)->ReleaseStringUTFChars(env, nickString, nickname);
+    if (nickname != NULL) {
+        (*env)->ReleaseStringUTFChars(env, nickString, nickname);
     }
-    if(cert != NULL) {
-       CERT_DestroyCertificate(cert);
-    }
-    if( rv == SECSuccess) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
+    if (cert != NULL) {
+        CERT_DestroyCertificate(cert);
     }
 }
 
 
 /***********************************************************************
  * CryptoManager.verifyCertNowNative
  * note: this calls obsolete NSS function
  * Returns JNI_TRUE if success, JNI_FALSE otherwise
@@ -1647,17 +1689,16 @@ finish:
  */
 JNIEXPORT jboolean JNICALL
 Java_org_mozilla_jss_CryptoManager_verifyCertTempNative(JNIEnv *env,
      jobject self, jbyteArray packageArray,jboolean checkSig, jint cUsage)
 {
     SECStatus         rv    = SECFailure;
     SECCertUsage      certUsage;
     SECItem *derCerts[2] = { NULL, NULL };
-    SECStatus status;
     CERTCertificate **certArray = NULL;
     CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
 
     /***************************************************
      * Validate arguments
      ***************************************************/
     if (packageArray == NULL) {
         JSS_throwMsg(env, CERTIFICATE_ENCODING_EXCEPTION,
--- a/org/mozilla/jss/util/jss_exceptions.h
+++ b/org/mozilla/jss/util/jss_exceptions.h
@@ -42,16 +42,18 @@ PR_BEGIN_EXTERN_C
 #define ILLEGAL_ARGUMENT_EXCEPTION "java/lang/IllegalArgumentException"
 
 #define ILLEGAL_BLOCK_SIZE_EXCEPTION "org/mozilla/jss/crypto/IllegalBlockSizeException"
 
 #define INCORRECT_PASSWORD_EXCEPTION "org/mozilla/jss/util/IncorrectPasswordException"
 
 #define INTERRUPTED_IO_EXCEPTION "java/io/InterruptedIOException"
 
+#define INVALID_NICKNAME_EXCEPTION "org/mozilla/jss/util/InvalidNicknameException"
+
 #define INVALID_KEY_FORMAT_EXCEPTION "org/mozilla/jss/crypto/InvalidKeyFormatException"
 
 #define INVALID_PARAMETER_EXCEPTION "java/security/InvalidParameterException"
 
 #define IO_EXCEPTION "java/io/IOException"
 
 #define KEY_DATABASE_EXCEPTION "org/mozilla/jss/KeyDatabaseException"