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