From 72347524cb1d50edfae33ad186cf369d0ddc0db1 Mon Sep 17 00:00:00 2001 From: "nicolson%netscape.com" Date: Tue, 26 Feb 2002 04:19:33 +0000 Subject: [PATCH] Initial checkin of the keystore code. It's not complete yet, but I want others to be able to see it and comment on it. --- .../provider/java/security/JSSKeyStoreSpi.c | 799 ++++++++++++++++++ .../java/security/JSSKeyStoreSpi.java | 263 ++++++ 2 files changed, 1062 insertions(+) create mode 100644 security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.c create mode 100644 security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.java diff --git a/security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.c b/security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.c new file mode 100644 index 000000000000..20e012388ad4 --- /dev/null +++ b/security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.c @@ -0,0 +1,799 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Network Security Services for Java. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "_jni/org_mozilla_jss_provider_java_security_JSSKeyStoreSpi.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static PRStatus +getTokenSlotPtr(JNIEnv *env, jobject keyStoreObj, PK11SlotInfo **ptr) +{ + return JSS_getPtrFromProxyOwner(env, keyStoreObj, + "proxy", "Lorg/mozilla/jss/pkcs11/TokenProxy;", (void**)ptr); +} + +typedef enum { + PRIVKEY=0x01, + PUBKEY=0x02, + SYMKEY=0x04, + CERT=0x08 +} TokenObjectType; +#define ALL_OBJECT_TYPES (PRIVKEY | PUBKEY | SYMKEY | CERT ) + +typedef struct { + PRStatus status; + PRBool deleteIt; + PRBool stopIterating; +} JSSTraversalStatus; +#define INIT_TRAVSTAT {PR_FAILURE, PR_FALSE, PR_FALSE} + +/* + * contract: you must throw an exception if you return PRFailure. + */ +typedef JSSTraversalStatus +(*TokenObjectTraversalCallback) +(JNIEnv* env, PK11SlotInfo *slot, TokenObjectType type, void *obj, void *data); + +/* + * objectType is bitwise-OR of all the object types you want to traverse. + */ +static PRStatus +traverseTokenObjects + (JNIEnv *env, PK11SlotInfo *slot, TokenObjectTraversalCallback cb, + int objectTypes, void *data) +{ + PRStatus status = PR_FAILURE; + JSSTraversalStatus travstat = INIT_TRAVSTAT; + SECKEYPrivateKeyList* privkList = NULL; + SECKEYPublicKeyList* pubkList = NULL; + PK11SymKey *symKey = NULL; + CERTCertList *certList = NULL; + SECStatus secstat; + + /* + * Get all private keys + */ + if( objectTypes & PRIVKEY ) { + SECKEYPrivateKeyListNode *node = NULL; + + privkList = PK11_ListPrivKeysInSlot(slot, NULL /*nickname*/, + NULL /*wincx*/); + if( privkList != NULL ) { + + for( node = PRIVKEY_LIST_HEAD(privkList); + ! PRIVKEY_LIST_END(node, privkList); + node = PRIVKEY_LIST_NEXT(node) ) + { + travstat = cb(env, slot, PRIVKEY, (void*) node->key, data); + if( travstat.status == PR_FAILURE ) { + goto finish; + } + if( travstat.deleteIt ) { + /* Setting "force" to PR_FALSE means that if there is a + * matching cert, the key won't be deleted. + * If the KeyStore API is being followed, the cert + * should have the same nickname as the key. So + * both will get deleted when we scan for matching + * certs later. + */ + PK11_DeleteTokenPrivateKey(node->key, PR_FALSE /*force*/); + node->key = NULL; + PR_REMOVE_LINK(&node->links); + /* we don't free node because it is allocated from + * the list's arena and will get freed when the list + * is freed. */ + } + if( travstat.stopIterating ) { + goto stop_early; + } + } + } + } + + /* + * Get all symmetric keys + */ + if(objectTypes & SYMKEY) { + /* this function returns a chained list of symmetric keys */ + symKey = PK11_ListFixedKeysInSlot(slot, NULL /*nickname*/, + NULL/*wincx*/); + + while( symKey != NULL ) { + PK11SymKey *deadKey; + travstat = cb(env, slot, SYMKEY, (void*) symKey, data); + if( travstat.status != PR_SUCCESS ) { + goto finish; + } + if( travstat.deleteIt ) { + /* this just deletes the PKCS #11 object. The data structure + * is NOT deleted. */ + PK11_DeleteTokenSymKey(symKey); + } + if( travstat.stopIterating ) { + goto stop_early; + } + + deadKey = symKey; + symKey = PK11_GetNextSymKey(symKey); + PK11_FreeSymKey(deadKey); + } + } + + /* + * get all public keys + */ + if( objectTypes & PUBKEY ) { + SECKEYPublicKeyListNode *node = NULL; + + pubkList = PK11_ListPublicKeysInSlot(slot, NULL /*nickname*/); + if( pubkList != NULL ) { + + for( node = PUBKEY_LIST_HEAD(pubkList); + ! PUBKEY_LIST_END(node, pubkList); + node = PUBKEY_LIST_NEXT(node) ) + { + travstat = cb(env, slot, PUBKEY, (void*) node->key, data); + if( travstat.status != PR_SUCCESS ) { + goto finish; + } + if( travstat.deleteIt ) { + /* XXX!!! + * Workaround for 125408: PK11_DeleteTokenPublic key asserts + * Don't delete the public key. + + * PK11_DeleteTokenPublicKey(node->key); + * node->key = NULL; + * PR_REMOVE_LINK(&node->links); + */ + /* node is allocated from the list's arena, it will get + * freed with the list */ + } + if( travstat.stopIterating ) { + goto stop_early; + } + } + + /* + * XXX!!! + * Destroy the list before we move on. Why bother, since we'll + * do it anyway in the finish block? Because of bug 125408. + * If we delete the cert and private key while traversing certs, + * we'll delete the public key too, and then we'll crash when + * we free the same public key while destroying the list. + */ + SECKEY_DestroyPublicKeyList(pubkList); + pubkList = NULL; + } + } + + /* + * Get all certs + */ + if( objectTypes & CERT ) { + CERTCertListNode *node = NULL; + + certList = PK11_ListCertsInSlot(slot); + if( certList == NULL ) { + JSS_throwMsg(env, TOKEN_EXCEPTION, + "Failed to list certificates on token"); + goto finish; + } + + for( node = CERT_LIST_HEAD(certList); + ! CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node) ) + { + travstat = cb(env, slot, CERT, (void*) node->cert, data); + if( travstat.status != PR_SUCCESS ) { + goto finish; + } + if( travstat.deleteIt ) { + /* + * Since, in a KeyStore, certs and private keys go together, + * remove the private key too, if there is one. + * + * The hack here is that PK11_DeleteTokenCertAndKey will + * not delete the cert if there is no matching private key. + * We want to the cert to be deleted even if the key isn't + * there. So we only call that function if we're sure the + * key is there. Otherwise we delete the cert directly. + */ + SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(node->cert, + NULL /*wincx*/); + PRBool keyPresent = (privKey != NULL); + SECKEY_DestroyPrivateKey(privKey); + if( keyPresent ) { + PK11_DeleteTokenCertAndKey(node->cert, NULL /*wincx*/); + } else { + SEC_DeletePermCertificate(node->cert); + } + PR_REMOVE_LINK(&node->links); + /* node is allocated from the list's arena, it will get freed + * with the list */ + } + if( travstat.stopIterating ) { + goto stop_early; + } + } + } + +stop_early: + status = PR_SUCCESS; +finish: + if( privkList != NULL ) { + SECKEY_DestroyPrivateKeyList(privkList); + } + if( pubkList != NULL ) { + SECKEY_DestroyPublicKeyList(pubkList); + } + while( symKey != NULL ) { + PK11SymKey *deadKey; + deadKey = symKey; + symKey = PK11_GetNextSymKey(symKey); + PK11_FreeSymKey(deadKey); + } + if( certList != NULL ) { + CERT_DestroyCertList(certList); + } + return status; +} + +/* allocates memory if type != CERT */ +static char* +getObjectNick(void *obj, TokenObjectType type) +{ + switch(type) { + case PRIVKEY: + /* NOTE: this function allocates memory for the nickname */ + return PK11_GetPrivateKeyNickname((SECKEYPrivateKey*)obj); + case SYMKEY: + /* NOTE: this function allocates memory for the nickname */ + return PK11_GetSymKeyNickname((PK11SymKey*)obj); + case PUBKEY: + /* NOTE: this function allocates memory for the nickname */ + return PK11_GetPublicKeyNickname((SECKEYPublicKey*)obj); + case CERT: + return ((CERTCertificate*)obj)->nickname; + default: + PR_ASSERT(PR_FALSE); + return NULL; + } +} + +static void +freeObjectNick(char *nick, TokenObjectType type) +{ + if( type != CERT && nick != NULL) { + PR_Free(nick); + } +} + +typedef struct { + jobject setObj; + jmethodID setAdd; +} EngineAliasesCBInfo; + +static JSSTraversalStatus +engineAliasesTraversalCallback +(JNIEnv *env, PK11SlotInfo *slot, TokenObjectType type, void *obj, void *data) +{ + JSSTraversalStatus travStat = INIT_TRAVSTAT; + EngineAliasesCBInfo *cbinfo = (EngineAliasesCBInfo*)data; + char *nickname=NULL; + + nickname = getObjectNick(obj, type); + + if( nickname != NULL ) { + jstring nickString; + + /* convert it to a string */ + nickString = (*env)->NewStringUTF(env, nickname); + freeObjectNick(nickname, type); + if( nickString == NULL ) { + /* exception was thrown */ + goto finish; + } + + /* store the string in the vector */ + (*env)->CallBooleanMethod(env, cbinfo->setObj, + cbinfo->setAdd, nickString); + if( (*env)->ExceptionOccurred(env) ) { + goto finish; + } + } + + travStat.status = PR_SUCCESS; +finish: + return travStat; +} + +JNIEXPORT jobject JNICALL +Java_org_mozilla_jss_provider_java_security_JSSKeyStoreSpi_getRawAliases + (JNIEnv *env, jobject this) +{ + PK11SlotInfo *slot = NULL; + jobject setObj=NULL; + jmethodID setAdd=NULL; + EngineAliasesCBInfo cbinfo; + + if( getTokenSlotPtr(env, this, &slot) != PR_SUCCESS ) { + /* exception was thrown */ + goto finish; + } + + /* + * Create the vector object and get its method IDs + */ + { + jclass setClass; + jmethodID setCons; + + setClass = (*env)->FindClass(env, "java/util/HashSet"); + if( setClass == NULL ) { + /* should have thrown an exception */ + goto finish; + } + setCons = (*env)->GetMethodID(env, setClass, "", "()V"); + if( setCons == NULL ) { + goto finish; + } + + setObj = (*env)->NewObject(env, setClass, setCons); + if( setObj == NULL ) { + goto finish; + } + + setAdd = (*env)->GetMethodID(env, setClass, "add", + "(Ljava/lang/Object;)Z"); + if( setAdd == NULL ) { + goto finish; + } + } + + cbinfo.setObj = setObj; + cbinfo.setAdd = setAdd; + + /* + * traverse all slot objects + */ + if( traverseTokenObjects( env, + slot, + engineAliasesTraversalCallback, + ALL_OBJECT_TYPES, + &cbinfo) + != PR_SUCCESS ) + { + goto finish; + } + +finish: + return setObj; +} + +typedef struct { + const char *targetNickname; +} EngineDeleteEntryCBInfo; + +static JSSTraversalStatus +engineDeleteEntryTraversalCallback + (JNIEnv *env, PK11SlotInfo *slot, TokenObjectType type, + void *obj, void *data) +{ + EngineDeleteEntryCBInfo *cbinfo = (EngineDeleteEntryCBInfo*)data; + JSSTraversalStatus status = INIT_TRAVSTAT; + char *nickname = getObjectNick(obj, type); + + if( nickname != NULL && + (PL_strcmp(nickname, cbinfo->targetNickname) == 0) ) { + status.deleteIt = PR_TRUE; + } + freeObjectNick(nickname, type); + status.status = PR_SUCCESS; + return status; +} + + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_provider_java_security_JSSKeyStoreSpi_engineDeleteEntry + (JNIEnv *env, jobject this, jobject aliasString) +{ + EngineDeleteEntryCBInfo cbinfo; + PK11SlotInfo *slot = NULL; + + cbinfo.targetNickname = NULL; + + if( getTokenSlotPtr(env, this, &slot) != PR_SUCCESS ) { + /* exception was thrown */ + goto finish; + } + + /* get the nickname */ + cbinfo.targetNickname = (*env)->GetStringUTFChars(env, aliasString, NULL); + if( cbinfo.targetNickname == NULL ) { + goto finish; + } + + /* traverse */ + /* + * traverse all slot objects + */ + traverseTokenObjects( env, + slot, + engineDeleteEntryTraversalCallback, + ALL_OBJECT_TYPES, + (void*) &cbinfo); + +finish: + if( cbinfo.targetNickname != NULL ) { + (*env)->ReleaseStringUTFChars(env, aliasString, cbinfo.targetNickname); + } +} + +typedef struct { + const char *targetNickname; + CERTCertificate *cert; +} EngineGetCertificateCBInfo; + +static JSSTraversalStatus +engineGetCertificateTraversalCallback + (JNIEnv *env, PK11SlotInfo *slot, TokenObjectType type, + void *obj, void *data) +{ + JSSTraversalStatus travStat = INIT_TRAVSTAT; + CERTCertificate *cert = (CERTCertificate*) obj; + EngineGetCertificateCBInfo *cbinfo = (EngineGetCertificateCBInfo*)data; + + PR_ASSERT(type == CERT); /* shouldn't get called otherwise */ + PR_ASSERT(cbinfo->cert == NULL); + + if( cert->nickname != NULL && + PL_strcmp(cert->nickname, cbinfo->targetNickname) == 0 ) + { + cbinfo->cert = CERT_DupCertificate(cert); + travStat.stopIterating = PR_TRUE; + } + travStat.status = PR_SUCCESS; + + return travStat; +} + + +JNIEXPORT jbyteArray JNICALL +Java_org_mozilla_jss_provider_java_security_JSSKeyStoreSpi_getDERCert + (JNIEnv *env, jobject this, jstring alias) +{ + PK11SlotInfo *slot; + EngineGetCertificateCBInfo cbinfo = {NULL,NULL}; + jbyteArray derCertBA = NULL; + + if( alias == NULL ) goto finish; + + if( getTokenSlotPtr(env, this, &slot) != PR_SUCCESS ) { + /* exception was thrown */ + goto finish; + } + + cbinfo.targetNickname = (*env)->GetStringUTFChars(env, alias, NULL); + if(cbinfo.targetNickname == NULL ) goto finish; + + if( traverseTokenObjects( env, + slot, + engineGetCertificateTraversalCallback, + CERT, + (void*) &cbinfo) + != PR_SUCCESS ) + { + goto finish; + } + + if( cbinfo.cert != NULL ) { + derCertBA = JSS_SECItemToByteArray(env, &cbinfo.cert->derCert); + } + +finish: + if( cbinfo.targetNickname != NULL ) { + (*env)->ReleaseStringUTFChars(env, alias, cbinfo.targetNickname); + } + if( cbinfo.cert != NULL) { + CERT_DestroyCertificate(cbinfo.cert); + } + return derCertBA; +} + +JNIEXPORT jstring JNICALL +Java_org_mozilla_jss_provider_java_security_JSSKeyStoreSpi_getCertNickname + (JNIEnv *env, jobject this, jbyteArray derCertBA) +{ + PK11SlotInfo *slot=NULL; + SECItem *derCert=NULL; + CERTCertificate * cert=NULL; + CERTCertificate searchCert; + jstring nickString = NULL; + + if( getTokenSlotPtr(env, this, &slot) != PR_SUCCESS ) { + /* exception was thrown */ + goto finish; + } + + /* convert derCert to SECItem */ + derCert = JSS_ByteArrayToSECItem(env, derCertBA); + if( derCert == NULL ) { + /* exception was thrown */ + goto finish; + } + + /* lookup cert by derCert */ + searchCert.derCert = *derCert; + cert = PK11_FindCertFromDERCert(slot, &searchCert, NULL /*wincx*/); + if( cert == NULL ) { + /* not found. return NULL */ + goto finish; + } + + /* convert cert nickname to Java string */ + nickString = (*env)->NewStringUTF(env, cert->nickname); + +finish: + if( derCert != NULL ) { + SECITEM_FreeItem(derCert, PR_TRUE /*freeit*/); + } + if( cert != NULL ) { + CERT_DestroyCertificate(cert); + } + return nickString; +} + +typedef struct { + const char *label; + SECKEYPrivateKey *privk; + PK11SymKey *symk; +} FindKeyCBInfo; + +static JSSTraversalStatus +findKeyCallback +(JNIEnv *env, PK11SlotInfo *slot, TokenObjectType type, void *obj, void *data) +{ + JSSTraversalStatus status = INIT_TRAVSTAT; + FindKeyCBInfo *cbinfo = (FindKeyCBInfo*)data; + const char *objNick = getObjectNick(obj, type); + + status.status = PR_SUCCESS; + + PR_ASSERT(cbinfo->privk==NULL && cbinfo->symk==NULL); + + if( PL_strcmp(objNick, cbinfo->label) == 0 ) { + /* found it */ + status.stopIterating = PR_TRUE; + switch( type ) { + case PRIVKEY: + cbinfo->privk = (SECKEYPrivateKey*)obj; + printf("Found private key with label '%s'\n", objNick); + break; + case SYMKEY: + cbinfo->symk = (PK11SymKey*)obj; + printf("Found summetric key with label '%s'\n", objNick); + break; + default: + PR_ASSERT(PR_FALSE); + status.status = PR_FAILURE; + } + } + + freeObjectNick(obj, type); + return status; +} + +typedef struct { + const char *targetNickname; + SECKEYPrivateKey *privk; +} GetKeyByCertNickCBInfo; + +static JSSTraversalStatus +getKeyByCertNickCallback + (JNIEnv *env, PK11SlotInfo *slot, TokenObjectType type, + void *obj, void *data) +{ + JSSTraversalStatus travStat = INIT_TRAVSTAT; + CERTCertificate *cert = (CERTCertificate*) obj; + GetKeyByCertNickCBInfo *cbinfo = (GetKeyByCertNickCBInfo*) data; + + PR_ASSERT(type == CERT); /* shouldn't get called otherwise */ + PR_ASSERT(cbinfo->privk == NULL); + + if( cert->nickname != NULL && + PL_strcmp(cert->nickname, cbinfo->targetNickname) == 0 ) + { + travStat.stopIterating = PR_TRUE; + + cbinfo->privk = PK11_FindPrivateKeyFromCert(slot, cert, NULL /*wincx*/); + if( cbinfo->privk ) { + printf("Found private key from cert with label '%s'\n", + cert->nickname); + } + } + travStat.status = PR_SUCCESS; + + return travStat; +} + + +JNIEXPORT jobject JNICALL +Java_org_mozilla_jss_provider_java_security_JSSKeyStoreSpi_engineGetKey + (JNIEnv *env, jobject this, jstring alias, jcharArray password) +{ + PK11SlotInfo *slot=NULL; + FindKeyCBInfo keyCbinfo = {NULL, NULL, NULL}; + GetKeyByCertNickCBInfo certCbinfo = {NULL, NULL}; + jobject keyObj = NULL; + + if( getTokenSlotPtr(env, this, &slot) != PR_SUCCESS ) { + /* exception was thrown */ + goto finish; + } + + if( alias == NULL ) { + goto finish; + } + + /* + * first look for a matching key + */ + + keyCbinfo.label = (*env)->GetStringUTFChars(env, alias, NULL); + if( keyCbinfo.label == NULL ) { + goto finish; + } + + if( traverseTokenObjects( env, + slot, + findKeyCallback, + PRIVKEY | SYMKEY, + &keyCbinfo) + != PR_SUCCESS ) + { + goto finish; + } + + if(keyCbinfo.privk != NULL ) { + /* found a matching private key */ + keyObj = JSS_PK11_wrapPrivKey(env, &keyCbinfo.privk); + } else if( keyCbinfo.symk != NULL ) { + /* found a matching symmetric key */ + keyObj = JSS_PK11_wrapSymKey(env, &keyCbinfo.symk); + } + + if( keyObj == NULL ) { + /* + * If we didn't find a matching key, look for a matching cert + */ + + certCbinfo.targetNickname = (*env)->GetStringUTFChars(env, alias, NULL); + if(certCbinfo.targetNickname == NULL ) goto finish; + + if( traverseTokenObjects( env, + slot, + getKeyByCertNickCallback, + CERT, + (void*) &certCbinfo) + != PR_SUCCESS ) + { + goto finish; + } + + if( certCbinfo.privk != NULL ) { + keyObj = JSS_PK11_wrapPrivKey(env, &certCbinfo.privk); + } + + } + +finish: + if( keyCbinfo.label != NULL ) { + (*env)->ReleaseStringUTFChars(env, alias, keyCbinfo.label); + } + if( certCbinfo.targetNickname != NULL ) { + (*env)->ReleaseStringUTFChars(env, alias, certCbinfo.targetNickname); + } + PR_ASSERT( keyCbinfo.privk==NULL && keyCbinfo.symk==NULL); + PR_ASSERT( certCbinfo.privk==NULL ); + + return keyObj; +} + +JNIEXPORT jboolean JNICALL +Java_org_mozilla_jss_provider_java_security_JSSKeyStoreSpi_engineIsCertificateEntry + (JNIEnv *env, jobject this, jstring alias) +{ + PK11SlotInfo *slot; + EngineGetCertificateCBInfo cbinfo = {NULL,NULL}; + jboolean retVal = JNI_FALSE; + SECKEYPrivateKey *privk = NULL; + + if( alias == NULL ) goto finish; + + if( getTokenSlotPtr(env, this, &slot) != PR_SUCCESS ) { + /* exception was thrown */ + goto finish; + } + + cbinfo.targetNickname = (*env)->GetStringUTFChars(env, alias, NULL); + if(cbinfo.targetNickname == NULL ) goto finish; + + if( traverseTokenObjects( env, + slot, + engineGetCertificateTraversalCallback, + CERT, + (void*) &cbinfo) + != PR_SUCCESS ) + { + goto finish; + } + + if( cbinfo.cert != NULL ) { + unsigned int allTrust; + CERTCertTrust trust; + SECStatus status; + + status = CERT_GetCertTrust(cbinfo.cert, &trust); + if( status != SECSuccess ) { + goto finish; + } + allTrust = trust.sslFlags | trust.emailFlags | trust.objectSigningFlags; + + if( (allTrust & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA | + CERTDB_NS_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA)) + && !(allTrust & CERTDB_USER) ) + { + /* It's a trusted cert and not a user cert. */ + retVal = JNI_TRUE; + } + } + +finish: + if( cbinfo.targetNickname != NULL ) { + (*env)->ReleaseStringUTFChars(env, alias, cbinfo.targetNickname); + } + if( cbinfo.cert != NULL) { + CERT_DestroyCertificate(cbinfo.cert); + } + return retVal; +} diff --git a/security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.java b/security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.java new file mode 100644 index 000000000000..3dae3c11aac2 --- /dev/null +++ b/security/jss/org/mozilla/jss/provider/java/security/JSSKeyStoreSpi.java @@ -0,0 +1,263 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Network Security Services for Java. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.jss.provider.java.security; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateException; +import java.security.cert.CertificateEncodingException; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import org.mozilla.jss.pkcs11.PK11Token; +import org.mozilla.jss.pkcs11.TokenProxy; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.TokenSupplierManager; + +/** + * The JSS implementation of the JCA KeyStore SPI. + * + *

Implementation notes + *

    + *
  1. deleteEntry will delete all entries with that label. If the entry is a + * cert with a matching private key, it will also delete the private key. + * + *
  2. getCertificate returns first cert with matching nickname. Converts it + * into a java.security.cert.X509Certificate (not a JSS cert). + * + *
  3. getCertificateChain only returns a single certificate. That's because + * we don't have a way to build a chain from a specific slot--only from + * the set of all slots. Kannan Bhoopathy has already indicated that he + * wants to get back whole chains from the KeyStore API, so this implementation + * is incorrect. Question: is it better to do an NSS-wide cert chain lookup + * and risk returning some certs from different tokens, or to only return + * the one cert that is guaranteed to be on the token? Probably the former, + * but I need confirmation. + * + *
  4. getCreationDate is unsupported because NSS doesn't store that + * information. Hopefully nobody cares about this. + * + *
  5. getKey first looks for a private/symmetric key with the given label. + * It returns the first one it finds. If it doesn't find one, it looks for a + * cert with the given nickname. If it finds one, it returns the private key + * for that cert. + * + *
  6. isCertificateEntry returns true if there is a cert with this nickname, + * but it doesn't have a private key. isKeyEntry returns true if there is a key + * with this nickname, or if there is a cert with this nickname and the cert + * has an associated private key. + * + *
  7. load and store are no-ops. + * + *
  8. setCertificateEntry doesn't work.NSS doesn't have a way of storing a + * certificate on a specific token unless it has an associated private key. + * That rules out trusted certificate entries. + * + *
  9. setKeyEntry not supported yet. Need to convert a temporary key + * into a permanent key. + *
+ */ +public class JSSKeyStoreSpi extends java.security.KeyStoreSpi { + + protected TokenProxy proxy; + + public JSSKeyStoreSpi() { + CryptoToken token = + TokenSupplierManager.getTokenSupplier().getThreadToken(); + PK11Token pk11tok = (PK11Token)token; + proxy = pk11tok.getProxy(); + } + + /** + * Converts an Iterator into an Enumeration. + */ + private static class IteratorEnumeration implements Enumeration { + private Iterator iter; + + public IteratorEnumeration(Iterator iter) { + this.iter = iter; + } + + public boolean hasMoreElements() { + return iter.hasNext(); + } + + public Object nextElement() { + return iter.next(); + } + } + + private native HashSet getRawAliases(); + + /** + * Returns a list of unique aliases. + */ + public Enumeration engineAliases() { + return new IteratorEnumeration( getRawAliases().iterator() ); + } + + public boolean engineContainsAlias(String alias) { + return getRawAliases().contains(alias); + } + + public native void engineDeleteEntry(String alias); + + /* + * XXX-!!! Is shared cert factory thread safe? + */ + private CertificateFactory certFactory=null; + { + try { + certFactory = CertificateFactory.getInstance("X.509"); + } catch(CertificateException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } + } + + public Certificate engineGetCertificate(String alias) { + byte[] derCert = getDERCert(alias); + if( derCert == null ) { + return null; + } else { + try { + return + certFactory.generateCertificate( + new ByteArrayInputStream(derCert) + ); + } catch( CertificateException e ) { + e.printStackTrace(); + return null; + } + } + } + + private native byte[] getDERCert(String alias); + + public String engineGetCertificateAlias(Certificate cert) { + try { + return getCertNickname( cert.getEncoded() ); + } catch(CertificateEncodingException e) { + return null; + } + } + + private native String getCertNickname(byte[] derCert); + + public Certificate[] engineGetCertificateChain(String alias) { + Certificate cert = engineGetCertificate(alias); + if( cert == null ) { + return null; + } else { + Certificate[] certs = new Certificate[1]; + certs[0] = cert; + return certs; + } + } + + /* + * Not supported. + */ + public java.util.Date engineGetCreationDate(String alias) { + return null; + } + + public native Key engineGetKey(String alias, char[] password); + + /** + * Returns true if there is a cert with this nickname but there is no + * key associated with the cert. + */ + public native boolean engineIsCertificateEntry(String alias); + + /** + * Returns true if there is a key with this alias, or if + * there is a cert with this alias that has an associated key. + */ + public boolean engineIsKeyEntry(String alias) { + /* this is somewhat wasteful but we can speed it up later */ + return ( engineGetKey(alias, null) != null ); + } + + public void engineLoad(InputStream stream, char[] password) + throws IOException + { + } + + /** + * NSS doesn't have a way of storing a certificate on a specific token + * unless it has an associated private key. That rules out + * trusted certificate entries, so we can't supply this method currently. + */ + public void engineSetCertificateEntry(String alias, Certificate cert) + throws KeyStoreException + { + throw new KeyStoreException( + "Storing trusted certificate entries to a JSS KeyStore is not" + + " supported."); + } + + + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) + throws KeyStoreException + { + throw new KeyStoreException("Storing plaintext keys is not supported."+ + "Store the key as a handle instead."); + } + + public void engineSetKeyEntry(String alias, Key key, char[] password, + Certificate[] chain) throws KeyStoreException + { + throw new KeyStoreException("Storing plaintext keys is not supported."+ + "Store the key as a handle instead."); + } + + public int engineSize() { + return getRawAliases().size(); + } + + public void engineStore(OutputStream stream, char[] password) + throws IOException + { + } +}