50aabd
# HG changeset patch
50aabd
# User Fraser Tweedale <ftweedal@redhat.com>
50aabd
# Date 1493324725 25200
50aabd
#      Thu Apr 27 13:25:25 2017 -0700
50aabd
# Node ID c8885dd6787639d74a1c9d634fd289ff17fa6f02
50aabd
# Parent  b2306481f30dcc8c0c060520805d405dd2546d14
50aabd
Bug 1355358 - CryptoStore: add methods for importing and exporting EncryptedPrivateKeyInfo, r=cfu
50aabd
50aabd
diff --git a/lib/jss.def b/lib/jss.def
50aabd
--- a/lib/jss.def
50aabd
+++ b/lib/jss.def
50aabd
@@ -324,3 +324,9 @@
50aabd
 ;+    local:
50aabd
 ;+       *;
50aabd
 ;+};
50aabd
+;+JSS_4.4.1 {     # JSS 4.4.1 release
50aabd
+;+    global:
50aabd
+Java_org_mozilla_jss_pkcs11_PK11Store_importEncryptedPrivateKeyInfo;
50aabd
+;+    local:
50aabd
+;+       *;
50aabd
+;+};
50aabd
diff --git a/org/mozilla/jss/crypto/Algorithm.c b/org/mozilla/jss/crypto/Algorithm.c
50aabd
--- a/org/mozilla/jss/crypto/Algorithm.c
50aabd
+++ b/org/mozilla/jss/crypto/Algorithm.c
50aabd
@@ -86,7 +86,13 @@
50aabd
 /* 55 */    {SEC_OID_PKCS5_PBMAC1, SEC_OID_TAG},
50aabd
 /* 56 */    {SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST, SEC_OID_TAG},
50aabd
 /* 57 */    {CKM_NSS_AES_KEY_WRAP, PK11_MECH},
50aabd
-/* 58 */    {CKM_NSS_AES_KEY_WRAP_PAD, PK11_MECH}
50aabd
+/* 58 */    {CKM_NSS_AES_KEY_WRAP_PAD, PK11_MECH},
50aabd
+/* 59 */    {SEC_OID_AES_128_ECB, SEC_OID_TAG},
50aabd
+/* 60 */    {SEC_OID_AES_128_CBC, SEC_OID_TAG},
50aabd
+/* 61 */    {SEC_OID_AES_192_ECB, SEC_OID_TAG},
50aabd
+/* 62 */    {SEC_OID_AES_192_CBC, SEC_OID_TAG},
50aabd
+/* 63 */    {SEC_OID_AES_256_ECB, SEC_OID_TAG},
50aabd
+/* 64 */    {SEC_OID_AES_256_CBC, SEC_OID_TAG}
50aabd
 /* REMEMBER TO UPDATE NUM_ALGS!!! */
50aabd
 };
50aabd
 
50aabd
diff --git a/org/mozilla/jss/crypto/Algorithm.h b/org/mozilla/jss/crypto/Algorithm.h
50aabd
--- a/org/mozilla/jss/crypto/Algorithm.h
50aabd
+++ b/org/mozilla/jss/crypto/Algorithm.h
50aabd
@@ -24,7 +24,7 @@
50aabd
     JSS_AlgType type;
50aabd
 } JSS_AlgInfo;
50aabd
 
50aabd
-#define NUM_ALGS 59
50aabd
+#define NUM_ALGS 65
50aabd
 
50aabd
 extern JSS_AlgInfo JSS_AlgTable[];
50aabd
 extern CK_ULONG JSS_symkeyUsage[];
50aabd
diff --git a/org/mozilla/jss/crypto/Algorithm.java b/org/mozilla/jss/crypto/Algorithm.java
50aabd
--- a/org/mozilla/jss/crypto/Algorithm.java
50aabd
+++ b/org/mozilla/jss/crypto/Algorithm.java
50aabd
@@ -212,4 +212,12 @@
50aabd
     protected static final short SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST=56;
50aabd
     protected static final short CKM_NSS_AES_KEY_WRAP=57;
50aabd
     protected static final short CKM_NSS_AES_KEY_WRAP_PAD=58;
50aabd
+
50aabd
+    // AES Encryption Algorithms
50aabd
+    protected static final short SEC_OID_AES_128_ECB = 59;
50aabd
+    protected static final short SEC_OID_AES_128_CBC = 60;
50aabd
+    protected static final short SEC_OID_AES_192_ECB = 61;
50aabd
+    protected static final short SEC_OID_AES_192_CBC = 62;
50aabd
+    protected static final short SEC_OID_AES_256_ECB = 63;
50aabd
+    protected static final short SEC_OID_AES_256_CBC = 64;
50aabd
 }
50aabd
diff --git a/org/mozilla/jss/crypto/CryptoStore.java b/org/mozilla/jss/crypto/CryptoStore.java
50aabd
--- a/org/mozilla/jss/crypto/CryptoStore.java
50aabd
+++ b/org/mozilla/jss/crypto/CryptoStore.java
50aabd
@@ -4,6 +4,7 @@
50aabd
 
50aabd
 package org.mozilla.jss.crypto;
50aabd
 
50aabd
+import org.mozilla.jss.CryptoManager;
50aabd
 import org.mozilla.jss.util.*;
50aabd
 import java.security.*;
50aabd
 import java.security.cert.CertificateEncodingException;
50aabd
@@ -68,9 +69,50 @@
50aabd
     public void deletePrivateKey(org.mozilla.jss.crypto.PrivateKey key)
50aabd
         throws NoSuchItemOnTokenException, TokenException;
50aabd
 
50aabd
+    /**
50aabd
+     * Get an encrypted private key for the given cert.
50aabd
+     *
50aabd
+     * @param cert Certificate of key to be exported
50aabd
+     * @param pbeAlg The PBEAlgorithm to use
50aabd
+     * @param pw The password to encrypt with
50aabd
+     * @param iteration Iteration count; default of 2000 if le 0
50aabd
+     */
50aabd
+    public byte[] getEncryptedPrivateKeyInfo(X509Certificate cert,
50aabd
+        PBEAlgorithm pbeAlg, Password pw, int iteration)
50aabd
+        throws CryptoManager.NotInitializedException,
50aabd
+            ObjectNotFoundException, TokenException;
50aabd
 
50aabd
-    public byte[] getEncryptedPrivateKeyInfo(X509Certificate cert,
50aabd
-        PBEAlgorithm pbeAlg, Password pw, int iteration);
50aabd
+    /**
50aabd
+     * Get an encrypted private key, with optional password
50aabd
+     * conversion.
50aabd
+     *
50aabd
+     * @param conv Password converter.  If null, pw.getByteCopy()
50aabd
+     *             will be used to get password bytes.
50aabd
+     * @param pw The password
50aabd
+     * @param alg The encryption algorithm
50aabd
+     * @param n Iteration count; default of 2000 if le 0
50aabd
+     * @param k The private key
50aabd
+     */
50aabd
+    public byte[] getEncryptedPrivateKeyInfo(
50aabd
+        KeyGenerator.CharToByteConverter conv,
50aabd
+        Password pw,
50aabd
+        Algorithm alg,
50aabd
+        int n,
50aabd
+        PrivateKey k);
50aabd
+
50aabd
+    /**
50aabd
+     * @param conv Password converter.  If null, pw.getByteCopy()
50aabd
+     *             will be used to get password bytes.
50aabd
+     * @param pw The password
50aabd
+     * @param nickname Nickname to use for private key
50aabd
+     * @param pubKey Public key corresponding to private key
50aabd
+     */
50aabd
+    public void importEncryptedPrivateKeyInfo(
50aabd
+        KeyGenerator.CharToByteConverter conv,
50aabd
+        Password pw,
50aabd
+        String nickname,
50aabd
+        PublicKey pubKey,
50aabd
+        byte[] epkiBytes);
50aabd
 
50aabd
     ////////////////////////////////////////////////////////////
50aabd
     // Certs
50aabd
diff --git a/org/mozilla/jss/crypto/EncryptionAlgorithm.java b/org/mozilla/jss/crypto/EncryptionAlgorithm.java
50aabd
--- a/org/mozilla/jss/crypto/EncryptionAlgorithm.java
50aabd
+++ b/org/mozilla/jss/crypto/EncryptionAlgorithm.java
50aabd
@@ -347,12 +347,14 @@
50aabd
             { 2, 16, 840, 1, 101, 3, 4, 1 } );
50aabd
 
50aabd
     public static final EncryptionAlgorithm
50aabd
-    AES_128_ECB = new EncryptionAlgorithm(CKM_AES_ECB, Alg.AES, Mode.ECB,
50aabd
+    AES_128_ECB = new EncryptionAlgorithm(SEC_OID_AES_128_ECB,
50aabd
+        Alg.AES, Mode.ECB,
50aabd
         Padding.NONE, (Class)null, 16,
50aabd
         AES_ROOT_OID.subBranch(1), 128);
50aabd
 
50aabd
     public static final EncryptionAlgorithm
50aabd
-    AES_128_CBC = new EncryptionAlgorithm(CKM_AES_CBC, Alg.AES, Mode.CBC,
50aabd
+    AES_128_CBC = new EncryptionAlgorithm(SEC_OID_AES_128_CBC,
50aabd
+        Alg.AES, Mode.CBC,
50aabd
         Padding.NONE, IVParameterSpecClasses, 16,
50aabd
         AES_ROOT_OID.subBranch(2), 128);
50aabd
 
50aabd
@@ -361,11 +363,13 @@
50aabd
         Padding.PKCS5, IVParameterSpecClasses, 16, null, 128); // no oid
50aabd
     
50aabd
     public static final EncryptionAlgorithm
50aabd
-    AES_192_ECB = new EncryptionAlgorithm(CKM_AES_ECB, Alg.AES, Mode.ECB,
50aabd
+    AES_192_ECB = new EncryptionAlgorithm(SEC_OID_AES_192_ECB,
50aabd
+        Alg.AES, Mode.ECB,
50aabd
         Padding.NONE, (Class)null, 16, AES_ROOT_OID.subBranch(21), 192);
50aabd
 
50aabd
     public static final EncryptionAlgorithm
50aabd
-    AES_192_CBC = new EncryptionAlgorithm(CKM_AES_CBC, Alg.AES, Mode.CBC,
50aabd
+    AES_192_CBC = new EncryptionAlgorithm(SEC_OID_AES_192_CBC,
50aabd
+        Alg.AES, Mode.CBC,
50aabd
         Padding.NONE, IVParameterSpecClasses, 16,
50aabd
         AES_ROOT_OID.subBranch(22), 192);
50aabd
     
50aabd
@@ -374,11 +378,13 @@
50aabd
         Padding.PKCS5, IVParameterSpecClasses, 16, null, 192); // no oid
50aabd
 
50aabd
     public static final EncryptionAlgorithm
50aabd
-    AES_256_ECB = new EncryptionAlgorithm(CKM_AES_ECB, Alg.AES, Mode.ECB,
50aabd
+    AES_256_ECB = new EncryptionAlgorithm(SEC_OID_AES_256_ECB,
50aabd
+        Alg.AES, Mode.ECB,
50aabd
         Padding.NONE, (Class)null, 16, AES_ROOT_OID.subBranch(41), 256);
50aabd
 
50aabd
     public static final EncryptionAlgorithm
50aabd
-    AES_256_CBC = new EncryptionAlgorithm(CKM_AES_CBC, Alg.AES, Mode.CBC,
50aabd
+    AES_256_CBC = new EncryptionAlgorithm(SEC_OID_AES_256_CBC,
50aabd
+        Alg.AES, Mode.CBC,
50aabd
         Padding.NONE, IVParameterSpecClasses, 16,
50aabd
         AES_ROOT_OID.subBranch(42), 256);
50aabd
     
50aabd
diff --git a/org/mozilla/jss/pkcs11/PK11Store.c b/org/mozilla/jss/pkcs11/PK11Store.c
50aabd
--- a/org/mozilla/jss/pkcs11/PK11Store.c
50aabd
+++ b/org/mozilla/jss/pkcs11/PK11Store.c
50aabd
@@ -31,6 +31,8 @@
50aabd
     char *data;
50aabd
 } secuPWData;
50aabd
 
50aabd
+SECItem *preparePassword(JNIEnv *env, jobject conv, jobject pwObj);
50aabd
+
50aabd
 /**********************************************************************
50aabd
  * PK11Store.putSymKeysInVector
50aabd
  */
50aabd
@@ -533,103 +535,293 @@
50aabd
 
50aabd
 
50aabd
 JNIEXPORT jbyteArray JNICALL
50aabd
-Java_org_mozilla_jss_pkcs11_PK11Store_getEncryptedPrivateKeyInfo
50aabd
-(JNIEnv *env, jobject this, jobject certObj, jobject algObj,
50aabd
-    jobject pwObj, jint iteration)
50aabd
+Java_org_mozilla_jss_pkcs11_PK11Store_getEncryptedPrivateKeyInfo(
50aabd
+    JNIEnv *env,
50aabd
+    jobject this,
50aabd
+    jobject conv,
50aabd
+    jobject pwObj,
50aabd
+    jobject algObj,
50aabd
+    jint iterations,
50aabd
+    jobject key)
50aabd
+{
50aabd
+    // initialisations so we can goto finish
50aabd
+    SECItem *pwItem = NULL;
50aabd
+    SECKEYEncryptedPrivateKeyInfo *epki = NULL;
50aabd
+    SECItem epkiItem;
50aabd
+    epkiItem.data = NULL;
50aabd
+    epkiItem.len = 0;
50aabd
 
50aabd
-{
50aabd
-    SECKEYEncryptedPrivateKeyInfo *epki = NULL;
50aabd
-    jbyteArray encodedEpki = NULL;
50aabd
+    PR_ASSERT(env != NULL && this != NULL);
50aabd
+
50aabd
+    if (pwObj == NULL || algObj == NULL || key == NULL) {
50aabd
+        JSS_throw(env, NULL_POINTER_EXCEPTION);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+    if (iterations <= 0) {
50aabd
+        iterations = 2000;  // set default iterations
50aabd
+    }
50aabd
+
50aabd
+    // get slot
50aabd
     PK11SlotInfo *slot = NULL;
50aabd
-    SECOidTag algTag;
50aabd
-    jclass passwordClass = NULL;
50aabd
-    jmethodID getByteCopyMethod = NULL;
50aabd
-    jbyteArray pwArray = NULL;
50aabd
-    jbyte* pwchars = NULL;
50aabd
-    SECItem pwItem;
50aabd
-    CERTCertificate *cert = NULL;
50aabd
-    SECItem epkiItem;
50aabd
-
50aabd
-    epkiItem.data = NULL;
50aabd
-
50aabd
-    /* get slot */
50aabd
     if( JSS_PK11_getStoreSlotPtr(env, this, &slot) != PR_SUCCESS) {
50aabd
         ASSERT_OUTOFMEM(env);
50aabd
         goto finish;
50aabd
     }
50aabd
     PR_ASSERT(slot!=NULL);
50aabd
-    
50aabd
-    /* get algorithm */
50aabd
-    algTag = JSS_getOidTagFromAlg(env, algObj);
50aabd
-    if( algTag == SEC_OID_UNKNOWN ) {
50aabd
-        JSS_throwMsg(env, NO_SUCH_ALG_EXCEPTION, "Unrecognized PBE algorithm");
50aabd
+
50aabd
+    // get algorithm
50aabd
+    SECOidTag algTag = JSS_getOidTagFromAlg(env, algObj);
50aabd
+    if (algTag == SEC_OID_UNKNOWN) {
50aabd
+        JSS_throwMsg(env, NO_SUCH_ALG_EXCEPTION, "Unrecognized algorithm");
50aabd
         goto finish;
50aabd
     }
50aabd
 
50aabd
-    /*
50aabd
-     * get password
50aabd
-     */
50aabd
-    passwordClass = (*env)->GetObjectClass(env, pwObj);
50aabd
-    if(passwordClass == NULL) {
50aabd
-        ASSERT_OUTOFMEM(env);
50aabd
-        goto finish;
50aabd
-    }
50aabd
-    getByteCopyMethod = (*env)->GetMethodID(
50aabd
-                                            env,
50aabd
-                                            passwordClass,
50aabd
-                                            PW_GET_BYTE_COPY_NAME,
50aabd
-                                            PW_GET_BYTE_COPY_SIG);
50aabd
-    if(getByteCopyMethod==NULL) {
50aabd
+    pwItem = preparePassword(env, conv, pwObj);
50aabd
+    if (pwItem == NULL) {
50aabd
         ASSERT_OUTOFMEM(env);
50aabd
         goto finish;
50aabd
     }
50aabd
-    pwArray = (*env)->CallObjectMethod( env, pwObj, getByteCopyMethod);
50aabd
-    pwchars = (*env)->GetByteArrayElements(env, pwArray, NULL);
50aabd
-    /* !!! Include the NULL byte or not? */
50aabd
-    pwItem.data = (unsigned char*) pwchars;
50aabd
-    pwItem.len = strlen((const char*)pwchars) + 1;
50aabd
 
50aabd
-    /*
50aabd
-     * get cert
50aabd
-     */
50aabd
-    if( JSS_PK11_getCertPtr(env, certObj, &cert) != PR_SUCCESS ) {
50aabd
-        /* exception was thrown */
50aabd
+    // get key
50aabd
+    SECKEYPrivateKey *privk;
50aabd
+    if (JSS_PK11_getPrivKeyPtr(env, key, &privk) != PR_SUCCESS) {
50aabd
+        PR_ASSERT( (*env)->ExceptionOccurred(env) != NULL);
50aabd
         goto finish;
50aabd
     }
50aabd
 
50aabd
-    /*
50aabd
-     * export the epki
50aabd
-     */
50aabd
-    epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algTag, &pwItem,
50aabd
-            cert, iteration, NULL /*wincx*/);
50aabd
-
50aabd
+    // export the epki
50aabd
+    epki = PK11_ExportEncryptedPrivKeyInfo(
50aabd
+        slot, algTag, pwItem, privk, iterations, NULL /*wincx*/);
50aabd
 
50aabd
-    /*
50aabd
-     * DER-encode the epki
50aabd
-     */
50aabd
-    epkiItem.data = NULL;
50aabd
-    epkiItem.len = 0;
50aabd
-    if( SEC_ASN1EncodeItem(NULL, &epkiItem, epki,
50aabd
-        SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate) )  == NULL ) {
50aabd
-        JSS_throwMsg(env, TOKEN_EXCEPTION, "Failed to ASN1-encode "
50aabd
-            "EncryptedPrivateKeyInfo");
50aabd
+    // DER-encode the epki
50aabd
+    if (SEC_ASN1EncodeItem(NULL, &epkiItem, epki,
50aabd
+        SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate)) == NULL) {
50aabd
+        JSS_throwMsg(
50aabd
+            env, TOKEN_EXCEPTION,
50aabd
+            "Failed to ASN1-encode EncryptedPrivateKeyInfo");
50aabd
         goto finish;
50aabd
     }
50aabd
 
50aabd
-    /*
50aabd
-     * convert to Java byte array
50aabd
-     */
50aabd
-    encodedEpki = JSS_SECItemToByteArray(env, &epkiItem);
50aabd
+    // convert to Java byte array
50aabd
+    jbyteArray encodedEpki = JSS_SECItemToByteArray(env, &epkiItem);
50aabd
 
50aabd
 finish:
50aabd
-    if( epki != NULL ) {
50aabd
+    if (epki != NULL) {
50aabd
         SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE /*freeit*/);
50aabd
     }
50aabd
-    if( pwchars != NULL ) {
50aabd
-        (*env)->ReleaseByteArrayElements(env, pwArray, pwchars, JNI_ABORT);
50aabd
+    if (epkiItem.data != NULL) {
50aabd
+        SECITEM_FreeItem(&epkiItem, PR_FALSE /*freeit*/);
50aabd
     }
50aabd
-    if(epkiItem.data != NULL) {
50aabd
-        PR_Free(epkiItem.data);
50aabd
+    if (pwItem != NULL) {
50aabd
+        SECITEM_FreeItem(pwItem, PR_TRUE /*freeit*/);
50aabd
     }
50aabd
     return encodedEpki;
50aabd
 }
50aabd
+
50aabd
+
50aabd
+JNIEXPORT void JNICALL
50aabd
+Java_org_mozilla_jss_pkcs11_PK11Store_importEncryptedPrivateKeyInfo(
50aabd
+    JNIEnv *env,
50aabd
+    jobject this,
50aabd
+    jobject conv,
50aabd
+    jobject pwObj,
50aabd
+    jstring nickname,
50aabd
+    jobject pubKeyObj,
50aabd
+    jbyteArray epkiBytes)
50aabd
+{
50aabd
+    // initialisations so we can goto finish
50aabd
+    SECItem *epkiItem = NULL;
50aabd
+    SECKEYEncryptedPrivateKeyInfo *epki = NULL;
50aabd
+    SECItem *pwItem = NULL;
50aabd
+    SECItem *spkiItem = NULL;
50aabd
+    CERTSubjectPublicKeyInfo *spki = NULL;
50aabd
+    SECKEYPublicKey *pubKey = NULL;
50aabd
+    const char *nicknameChars = NULL;
50aabd
+
50aabd
+    PR_ASSERT(env != NULL && this != NULL);
50aabd
+
50aabd
+    if (pwObj == NULL || nickname == NULL || pubKeyObj == NULL) {
50aabd
+        JSS_throw(env, NULL_POINTER_EXCEPTION);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+    // get slot
50aabd
+    PK11SlotInfo *slot = NULL;
50aabd
+    if (JSS_PK11_getStoreSlotPtr(env, this, &slot) != PR_SUCCESS) {
50aabd
+        ASSERT_OUTOFMEM(env);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+    PR_ASSERT(slot != NULL);
50aabd
+
50aabd
+    // decode EncryptedPrivateKeyInfo
50aabd
+    epkiItem = JSS_ByteArrayToSECItem(env, epkiBytes);
50aabd
+    epki = PR_Calloc(1, sizeof(SECKEYEncryptedPrivateKeyInfo));
50aabd
+    if (SEC_ASN1DecodeItem(
50aabd
+                NULL,
50aabd
+                epki,
50aabd
+                SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate),
50aabd
+                epkiItem
50aabd
+            ) != SECSuccess) {
50aabd
+        JSS_throwMsg(env, INVALID_DER_EXCEPTION,
50aabd
+            "Failed to decode EncryptedPrivateKeyInfo");
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+    pwItem = preparePassword(env, conv, pwObj);
50aabd
+    if (pwItem == NULL) {
50aabd
+        ASSERT_OUTOFMEM(env);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+    // get public key value
50aabd
+    jclass pubKeyClass = (*env)->GetObjectClass(env, pubKeyObj);
50aabd
+    if (pubKeyClass == NULL) {
50aabd
+        ASSERT_OUTOFMEM(env);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+    jmethodID getEncoded = (*env)->GetMethodID(
50aabd
+        env, pubKeyClass, "getEncoded", "()[B");
50aabd
+    if (getEncoded == NULL) {
50aabd
+        ASSERT_OUTOFMEM(env);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+    jbyteArray spkiBytes = (*env)->CallObjectMethod(
50aabd
+        env, pubKeyObj, getEncoded);
50aabd
+    spkiItem = JSS_ByteArrayToSECItem(env, spkiBytes);
50aabd
+    spki = PR_Calloc(1, sizeof(CERTSubjectPublicKeyInfo));
50aabd
+    if (SEC_ASN1DecodeItem(
50aabd
+                NULL,
50aabd
+                spki,
50aabd
+                SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate),
50aabd
+                spkiItem
50aabd
+            ) != SECSuccess) {
50aabd
+        JSS_throwMsg(env, INVALID_DER_EXCEPTION,
50aabd
+            "Failed to decode SubjectPublicKeyInfo");
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+    pubKey = SECKEY_ExtractPublicKey(spki);
50aabd
+    if (pubKey == NULL) {
50aabd
+        JSS_throwMsgPrErr(env, INVALID_DER_EXCEPTION,
50aabd
+            "Failed to extract public key from SubjectPublicKeyInfo");
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+    SECItem *pubValue;
50aabd
+    switch (pubKey->keyType) {
50aabd
+        case dsaKey:
50aabd
+            pubValue = &pubKey->u.dsa.publicValue;
50aabd
+            break;
50aabd
+        case dhKey:
50aabd
+            pubValue = &pubKey->u.dh.publicValue;
50aabd
+            break;
50aabd
+        case rsaKey:
50aabd
+            pubValue = &pubKey->u.rsa.modulus;
50aabd
+            break;
50aabd
+        case ecKey:
50aabd
+            pubValue = &pubKey->u.ec.publicValue;
50aabd
+            break;
50aabd
+        default:
50aabd
+            pubValue = NULL;
50aabd
+    }
50aabd
+
50aabd
+    // prepare nickname
50aabd
+    nicknameChars = (*env)->GetStringUTFChars(env, nickname, NULL);
50aabd
+    if (nicknameChars == NULL) {
50aabd
+        ASSERT_OUTOFMEM(env);
50aabd
+        goto finish;
50aabd
+    }
50aabd
+    SECItem nickItem;
50aabd
+    nickItem.data = nicknameChars;
50aabd
+    nickItem.len = (*env)->GetStringUTFLength(env, nickname);
50aabd
+
50aabd
+    // if keyUsage = 0, defaults to signing and encryption/key agreement.
50aabd
+    //   see pk11akey.c in NSS
50aabd
+    int keyUsage = 0;
50aabd
+
50aabd
+    // perform import
50aabd
+    SECStatus result = PK11_ImportEncryptedPrivateKeyInfo(
50aabd
+        slot, epki, pwItem, &nickItem, pubValue,
50aabd
+        PR_TRUE /* isperm */, PR_TRUE /* isprivate */,
50aabd
+        pubKey->keyType, keyUsage, NULL /* wincx */);
50aabd
+    if (result != SECSuccess) {
50aabd
+        JSS_throwMsg(
50aabd
+            env, TOKEN_EXCEPTION,
50aabd
+            "Failed to import EncryptedPrivateKeyInfo to token");
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
+finish:
50aabd
+    if (epkiItem != NULL) {
50aabd
+        SECITEM_FreeItem(epkiItem, PR_TRUE /*freeit*/);
50aabd
+    }
50aabd
+    if (epki != NULL) {
50aabd
+        SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE /*freeit*/);
50aabd
+    }
50aabd
+    if (spkiItem != NULL) {
50aabd
+        SECITEM_FreeItem(spkiItem, PR_TRUE /*freeit*/);
50aabd
+    }
50aabd
+    if (spki != NULL) {
50aabd
+        SECKEY_DestroySubjectPublicKeyInfo(spki);
50aabd
+    }
50aabd
+    if (pwItem != NULL) {
50aabd
+        SECITEM_FreeItem(pwItem, PR_TRUE /*freeit*/);
50aabd
+    }
50aabd
+    if (pubKey != NULL) {
50aabd
+        SECKEY_DestroyPublicKey(pubKey);
50aabd
+    }
50aabd
+    if (nicknameChars != NULL) {
50aabd
+        (*env)->ReleaseStringUTFChars(env, nickname, nicknameChars);
50aabd
+    }
50aabd
+}
50aabd
+
50aabd
+/* Process the given password through the given PasswordConverter,
50aabd
+ * returning a new SECItem* on success.
50aabd
+ *
50aabd
+ * After use, the caller should free the SECItem:
50aabd
+ *
50aabd
+ *   SECITEM_FreeItem(pwItem, PR_TRUE).
50aabd
+ */
50aabd
+SECItem *preparePassword(JNIEnv *env, jobject conv, jobject pwObj) {
50aabd
+    jclass passwordClass = (*env)->GetObjectClass(env, pwObj);
50aabd
+    if (passwordClass == NULL) {
50aabd
+        ASSERT_OUTOFMEM(env);
50aabd
+        return NULL;
50aabd
+    }
50aabd
+
50aabd
+    jbyteArray pwBytes;
50aabd
+
50aabd
+    if (conv == NULL) {
50aabd
+        jmethodID getByteCopy = (*env)->GetMethodID(
50aabd
+            env, passwordClass, PW_GET_BYTE_COPY_NAME, PW_GET_BYTE_COPY_SIG);
50aabd
+        if (getByteCopy == NULL) {
50aabd
+            ASSERT_OUTOFMEM(env);
50aabd
+            return NULL;
50aabd
+        }
50aabd
+        pwBytes = (*env)->CallObjectMethod(env, pwObj, getByteCopy);
50aabd
+    } else {
50aabd
+        jmethodID getChars = (*env)->GetMethodID(
50aabd
+            env, passwordClass, "getChars", "()[C");
50aabd
+        if (getChars == NULL) {
50aabd
+            ASSERT_OUTOFMEM(env);
50aabd
+            return NULL;
50aabd
+        }
50aabd
+        jcharArray pwChars = (*env)->CallObjectMethod(env, pwObj, getChars);
50aabd
+
50aabd
+        jclass convClass = (*env)->GetObjectClass(env, conv);
50aabd
+        if (conv == NULL) {
50aabd
+            ASSERT_OUTOFMEM(env);
50aabd
+            return NULL;
50aabd
+        }
50aabd
+        jmethodID convert = (*env)->GetMethodID(
50aabd
+            env, convClass, "convert", "([C)[B");
50aabd
+        if (convert == NULL) {
50aabd
+            ASSERT_OUTOFMEM(env);
50aabd
+            return NULL;
50aabd
+        }
50aabd
+        pwBytes = (*env)->CallObjectMethod(env, conv, convert, pwChars);
50aabd
+    }
50aabd
+
50aabd
+    return JSS_ByteArrayToSECItem(env, pwBytes);
50aabd
+}
50aabd
diff --git a/org/mozilla/jss/pkcs11/PK11Store.java b/org/mozilla/jss/pkcs11/PK11Store.java
50aabd
--- a/org/mozilla/jss/pkcs11/PK11Store.java
50aabd
+++ b/org/mozilla/jss/pkcs11/PK11Store.java
50aabd
@@ -4,8 +4,10 @@
50aabd
 
50aabd
 package org.mozilla.jss.pkcs11;
50aabd
 
50aabd
+import org.mozilla.jss.CryptoManager;
50aabd
 import org.mozilla.jss.crypto.*;
50aabd
 import org.mozilla.jss.util.*;
50aabd
+import java.security.PublicKey;
50aabd
 import java.security.cert.CertificateEncodingException;
50aabd
 import java.util.Vector;
50aabd
 
50aabd
@@ -53,8 +55,35 @@
50aabd
     public native void deletePrivateKey(PrivateKey key)
50aabd
         throws NoSuchItemOnTokenException, TokenException;
50aabd
 
50aabd
-    public native byte[] getEncryptedPrivateKeyInfo(X509Certificate cert,
50aabd
-        PBEAlgorithm pbeAlg, Password pw, int iteration);
50aabd
+    public byte[] getEncryptedPrivateKeyInfo(
50aabd
+            X509Certificate cert,
50aabd
+            PBEAlgorithm pbeAlg,
50aabd
+            Password pw,
50aabd
+            int iteration)
50aabd
+            throws CryptoManager.NotInitializedException,
50aabd
+                ObjectNotFoundException, TokenException {
50aabd
+        return getEncryptedPrivateKeyInfo(
50aabd
+            null,
50aabd
+            pw,
50aabd
+            pbeAlg,
50aabd
+            iteration,
50aabd
+            CryptoManager.getInstance().findPrivKeyByCert(cert)
50aabd
+        );
50aabd
+    }
50aabd
+
50aabd
+    public native byte[] getEncryptedPrivateKeyInfo(
50aabd
+        KeyGenerator.CharToByteConverter conv,
50aabd
+        Password pw,
50aabd
+        Algorithm alg,
50aabd
+        int n,
50aabd
+        PrivateKey k);
50aabd
+
50aabd
+    public native void importEncryptedPrivateKeyInfo(
50aabd
+        KeyGenerator.CharToByteConverter conv,
50aabd
+        Password pw,
50aabd
+        String nickname,
50aabd
+        PublicKey pubKey,
50aabd
+        byte[] epkiBytes);
50aabd
 
50aabd
     ////////////////////////////////////////////////////////////
50aabd
     // Certs
50aabd
diff --git a/org/mozilla/jss/util/jss_exceptions.h b/org/mozilla/jss/util/jss_exceptions.h
50aabd
--- a/org/mozilla/jss/util/jss_exceptions.h
50aabd
+++ b/org/mozilla/jss/util/jss_exceptions.h
50aabd
@@ -47,6 +47,8 @@
50aabd
 
50aabd
 #define INTERRUPTED_IO_EXCEPTION "java/io/InterruptedIOException"
50aabd
 
50aabd
+#define INVALID_DER_EXCEPTION "org/mozilla/jss/crypto/InvalidDERException"
50aabd
+
50aabd
 #define INVALID_NICKNAME_EXCEPTION "org/mozilla/jss/util/InvalidNicknameException"
50aabd
 
50aabd
 #define INVALID_KEY_FORMAT_EXCEPTION "org/mozilla/jss/crypto/InvalidKeyFormatException"
50aabd
# HG changeset patch
50aabd
# User Fraser Tweedale <ftweedal@redhat.com>
50aabd
# Date 1493335326 25200
50aabd
#      Thu Apr 27 16:22:06 2017 -0700
50aabd
# Node ID ead2ea094c98ddc708169c3de411ca8d8883cab8
50aabd
# Parent  c8885dd6787639d74a1c9d634fd289ff17fa6f02
50aabd
Bug 1359731 - CryptoStore.importPrivateKey enhancements, r=cfu
50aabd
50aabd
- Enhance CryptoStore.importPrivateKey to support temporary import, and
50aabd
- returning the private key to the caller.
50aabd
- Also remove some validation of the unused keyType argument.
50aabd
50aabd
diff --git a/org/mozilla/jss/crypto/CryptoStore.java b/org/mozilla/jss/crypto/CryptoStore.java
50aabd
--- a/org/mozilla/jss/crypto/CryptoStore.java
50aabd
+++ b/org/mozilla/jss/crypto/CryptoStore.java
50aabd
@@ -21,17 +21,30 @@
50aabd
     ////////////////////////////////////////////////////////////
50aabd
 
50aabd
     /**
50aabd
-     * Imports a raw private key into this token.
50aabd
+     * Imports a raw private key into this token (permanently).
50aabd
      *
50aabd
      * @param key The private key.
50aabd
      * @exception TokenException If the key cannot be imported to this token.
50aabd
      * @exception KeyAlreadyImportedException If the key already exists on this token.
50aabd
      */
50aabd
-    public void
50aabd
+    public PrivateKey
50aabd
     importPrivateKey(  byte[] key,
50aabd
                        PrivateKey.Type type       )
50aabd
         throws TokenException, KeyAlreadyImportedException;
50aabd
 
50aabd
+    /**
50aabd
+     * Imports a raw private key into this token.
50aabd
+     *
50aabd
+     * @param key The private key.
50aabd
+     * @param temporary Whether the key should be temporary.
50aabd
+     * @exception TokenException If the key cannot be imported to this token.
50aabd
+     * @exception KeyAlreadyImportedException If the key already exists on this token.
50aabd
+     */
50aabd
+    public PrivateKey
50aabd
+    importPrivateKey(  byte[] key,
50aabd
+                       PrivateKey.Type type, boolean temporary)
50aabd
+        throws TokenException, KeyAlreadyImportedException;
50aabd
+
50aabd
 
50aabd
     /**
50aabd
      * Returns all private keys stored on this token.
50aabd
diff --git a/org/mozilla/jss/pkcs11/PK11Store.c b/org/mozilla/jss/pkcs11/PK11Store.c
50aabd
--- a/org/mozilla/jss/pkcs11/PK11Store.c
50aabd
+++ b/org/mozilla/jss/pkcs11/PK11Store.c
50aabd
@@ -429,22 +429,22 @@
50aabd
 int PK11_NumberObjectsFor(PK11SlotInfo*, CK_ATTRIBUTE*, int);
50aabd
 
50aabd
 /***********************************************************************
50aabd
- * importPrivateKey
50aabd
+ * PK11Store.importdPrivateKey
50aabd
  */
50aabd
-static void
50aabd
-importPrivateKey
50aabd
+JNIEXPORT jobject JNICALL
50aabd
+Java_org_mozilla_jss_pkcs11_PK11Store_importPrivateKey
50aabd
     (   JNIEnv *env,
50aabd
         jobject this,
50aabd
         jbyteArray keyArray,
50aabd
         jobject keyTypeObj,
50aabd
-        PRBool temporary            )
50aabd
+        jboolean temporary            )
50aabd
 {
50aabd
     SECItem derPK;
50aabd
     PK11SlotInfo *slot;
50aabd
     jthrowable excep;
50aabd
-    KeyType keyType;
50aabd
     SECStatus status;
50aabd
     SECItem nickname;
50aabd
+    jobject privkObj = NULL;
50aabd
 
50aabd
     /*
50aabd
      * initialize so we can goto finish
50aabd
@@ -452,13 +452,6 @@
50aabd
     derPK.data = NULL;
50aabd
     derPK.len = 0;
50aabd
 
50aabd
-
50aabd
-    keyType = JSS_PK11_getKeyType(env, keyTypeObj);
50aabd
-    if( keyType == nullKey ) {
50aabd
-        /* exception was thrown */
50aabd
-        goto finish;
50aabd
-    }
50aabd
-
50aabd
     PR_ASSERT(env!=NULL && this!=NULL);
50aabd
 
50aabd
     if(keyArray == NULL) {
50aabd
@@ -492,14 +485,22 @@
50aabd
     nickname.len = 0;
50aabd
     nickname.data = NULL;
50aabd
 
50aabd
-    status = PK11_ImportDERPrivateKeyInfo(slot, &derPK, &nickname,
50aabd
-                NULL /*public value*/, PR_TRUE /*isPerm*/,
50aabd
-                PR_TRUE /*isPrivate*/, 0 /*keyUsage*/, NULL /*wincx*/);
50aabd
+    SECKEYPrivateKey *privk = NULL;
50aabd
+    status = PK11_ImportDERPrivateKeyInfoAndReturnKey(
50aabd
+                slot, &derPK, &nickname,
50aabd
+                NULL /*public value*/, !temporary /*isPerm*/,
50aabd
+                PR_TRUE /*isPrivate*/, 0 /*keyUsage*/,
50aabd
+                &privk, NULL /*wincx*/);
50aabd
     if(status != SECSuccess) {
50aabd
         JSS_throwMsg(env, TOKEN_EXCEPTION, "Failed to import private key info");
50aabd
         goto finish;
50aabd
     }
50aabd
 
50aabd
+    privkObj = JSS_PK11_wrapPrivKey(env, &privk);
50aabd
+    if (privkObj == NULL) {
50aabd
+        goto finish;
50aabd
+    }
50aabd
+
50aabd
 finish:
50aabd
     /* Save any exceptions */
50aabd
     if( (excep=(*env)->ExceptionOccurred(env)) ) {
50aabd
@@ -515,24 +516,11 @@
50aabd
     if( excep ) {
50aabd
         (*env)->Throw(env, excep);
50aabd
     }
50aabd
+    return privkObj;
50aabd
 }
50aabd
 
50aabd
 extern const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[];
50aabd
 
50aabd
-/***********************************************************************
50aabd
- * PK11Store.importdPrivateKey
50aabd
- */
50aabd
-JNIEXPORT void JNICALL
50aabd
-Java_org_mozilla_jss_pkcs11_PK11Store_importPrivateKey
50aabd
-    (   JNIEnv *env,
50aabd
-        jobject this,
50aabd
-        jbyteArray keyArray,
50aabd
-        jobject keyTypeObj        )
50aabd
-{
50aabd
-    importPrivateKey(env, this, keyArray,
50aabd
-        keyTypeObj, PR_FALSE /* not temporary */);
50aabd
-}
50aabd
-
50aabd
 
50aabd
 JNIEXPORT jbyteArray JNICALL
50aabd
 Java_org_mozilla_jss_pkcs11_PK11Store_getEncryptedPrivateKeyInfo(
50aabd
diff --git a/org/mozilla/jss/pkcs11/PK11Store.java b/org/mozilla/jss/pkcs11/PK11Store.java
50aabd
--- a/org/mozilla/jss/pkcs11/PK11Store.java
50aabd
+++ b/org/mozilla/jss/pkcs11/PK11Store.java
50aabd
@@ -23,9 +23,15 @@
50aabd
      * @exception TokenException If the key cannot be imported to this token.
50aabd
      * @exception KeyAlreadyImportedException If the key already on this token.
50aabd
      */
50aabd
-    public native void
50aabd
-    importPrivateKey(  byte[] key,
50aabd
-                       PrivateKey.Type type       )
50aabd
+    public PrivateKey
50aabd
+    importPrivateKey(byte[] key, PrivateKey.Type type)
50aabd
+            throws TokenException,KeyAlreadyImportedException {
50aabd
+        return importPrivateKey(key, type, false);
50aabd
+    }
50aabd
+
50aabd
+    public native PrivateKey
50aabd
+    importPrivateKey(
50aabd
+        byte[] key, PrivateKey.Type type, boolean temporary)
50aabd
         throws TokenException,KeyAlreadyImportedException;
50aabd
 
50aabd
     public synchronized PrivateKey[]
50aabd
# HG changeset patch
50aabd
# User Matthew Harmsen <mharmsen@redhat.com>
50aabd
# Date 1493389838 25200
50aabd
#      Fri Apr 28 07:30:38 2017 -0700
50aabd
# Node ID 4ee5af07d6d8fd7efe60d130d3e7593f6e12e642
50aabd
# Parent  ead2ea094c98ddc708169c3de411ca8d8883cab8
50aabd
Bug 1352476 - RFE: Document on the README how to create a release tag, r=emaldona
50aabd
50aabd
diff --git a/README b/README
50aabd
--- a/README
50aabd
+++ b/README
50aabd
@@ -158,7 +158,40 @@
50aabd
                be necessary.
50aabd
 
50aabd
 
50aabd
-(7) Known Issues
50aabd
+(7) Tagging the Source Code for a Release
50aabd
+
50aabd
+    During development, several releases may be made.  Consequently, it is
50aabd
+    good practice to create a "regular tag" to the source code at these
50aabd
+    various points in time using the following format:
50aabd
+
50aabd
+        # hg tag -m "message" JSS_<major>_<minor>_YYYYMMDD
50aabd
+
50aabd
+        where:  <major> = JSS Major Version Number
50aabd
+                <minor> = JSS Minor Version Number
50aabd
+                YYYY    = 4-digit year (e. g. - 2017)
50aabd
+                MM      = 2-digit month (e. g. - 01, ..., 12)
50aabd
+                DD      = 2-digit day of the month (e. g. - 01, ..., 31)
50aabd
+    
50aabd
+        For example:
50aabd
+
50aabd
+            # hg id
50aabd
+            b3e864205ff0+ tip
50aabd
+
50aabd
+            # hg tag -m "Added tag JSS_4_4_20170328 for changeset b3e864205ff0" JSS_4_4_20170328
50aabd
+
50aabd
+    At the appropriate time, a new major.minor version may be created.  At this
50aabd
+    time, it is important to create a maintenance branch for any future changes
50aabd
+    to the previous major.minor version:
50aabd
+
50aabd
+    For example:
50aabd
+
50aabd
+        # hg id
50aabd
+        f00f00f00f00+ tip
50aabd
+
50aabd
+        # hg branch -m "Created branch JSS_4_4_BRANCH for changeset f00f00f00f00" JSS_4_4_BRANCH
50aabd
+
50aabd
+
50aabd
+(8) Known Issues
50aabd
 
50aabd
     * Mozilla Bug #1346410 - Load JSS libraries appropriately
50aabd