6599979: KeyStore.setEntry/setKeyEntry() do not override existing entry for secret key objects
authorvaleriep
Thu, 20 Mar 2008 18:41:05 -0700
changeset 291 be2e0a87d658
parent 290 519d4185fbe2
child 292 ec0a200b3723
6599979: KeyStore.setEntry/setKeyEntry() do not override existing entry for secret key objects Summary: Override existing secret key entry when setEntry/setKeyEntry() is called Reviewed-by: andreas
jdk/src/share/classes/sun/security/pkcs11/P11KeyStore.java
jdk/src/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java
jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java
jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.sh
--- a/jdk/src/share/classes/sun/security/pkcs11/P11KeyStore.java	Thu Mar 20 17:17:10 2008 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11KeyStore.java	Thu Mar 20 18:41:05 2008 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -156,10 +156,10 @@
         // CKA_CLASS - entry type
         private CK_ATTRIBUTE type = null;
 
-        // CKA_LABEL of cert
+        // CKA_LABEL of cert and secret key
         private String label = null;
 
-        // CKA_ID - of private key/cert
+        // CKA_ID of the private key/cert pair
         private byte[] id = null;
 
         // CKA_TRUSTED - true if cert is trusted
@@ -871,10 +871,8 @@
         if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {
             token.provider.login(null, handler);
         } else {
-
             // token supports protected authentication path
             // (external pin-pad, for example)
-
             if (handler != null &&
                 !token.config.getKeyStoreCompatibilityMode()) {
                 throw new LoginException("can not specify password if token " +
@@ -1130,19 +1128,14 @@
                 SecretKey skey = ske.getSecretKey();
 
                 try {
-                    // first see if the key already exists.
-                    // if so, update the CKA_LABEL
-                    if (!updateSkey(alias)) {
+                    // first check if the key already exists
+                    AliasInfo aliasInfo = aliasMap.get(alias);
 
-                        // key entry does not exist.
-                        // delete existing entry for alias and
-                        // create new secret key entry
-                        // (new entry might be a secret key
-                        // session object converted into a token object)
+                    if (aliasInfo != null) {
+                        engineDeleteEntry(alias);
+                    }
+                    storeSkey(alias, ske);
 
-                        engineDeleteEntry(alias);
-                        storeSkey(alias, ske);
-                    }
                 } catch (PKCS11Exception pe) {
                     throw new KeyStoreException(pe);
                 }
@@ -1396,41 +1389,6 @@
         }
     }
 
-    /**
-     * return true if update occurred
-     */
-    private boolean updateSkey(String alias)
-                throws KeyStoreException, PKCS11Exception {
-
-        Session session = null;
-        try {
-            session = token.getOpSession();
-
-            // first update existing secret key CKA_LABEL
-
-            THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);
-            if (h.type != ATTR_CLASS_SKEY) {
-                if (debug != null) {
-                    debug.println("did not find secret key " +
-                        "with CKA_LABEL [" + alias + "]");
-                }
-                return false;
-            }
-            CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
-                                new CK_ATTRIBUTE(CKA_LABEL, alias) };
-            token.p11.C_SetAttributeValue(session.id(), h.handle, attrs);
-
-            if (debug != null) {
-                debug.println("updateSkey set new alias [" +
-                                alias +
-                                "] for secret key entry");
-            }
-
-            return true;
-        } finally {
-            token.releaseSession(session);
-        }
-    }
 
     /**
      * XXX  On ibutton, when you C_SetAttribute(CKA_ID) for a private key
@@ -1532,30 +1490,6 @@
         }
     }
 
-    private void updateP11Skey(String alias, P11Key key)
-                throws PKCS11Exception {
-
-        Session session = null;
-        try {
-            session = token.getOpSession();
-
-            // session key - convert to token key and set CKA_LABEL
-
-            CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
-                                ATTR_TOKEN_TRUE,
-                                new CK_ATTRIBUTE(CKA_LABEL, alias) };
-            token.p11.C_CopyObject(session.id(), key.keyID, attrs);
-            if (debug != null) {
-                    debug.println("updateP11Skey copied secret session key " +
-                                "for [" +
-                                alias +
-                                "] to token entry");
-            }
-        } finally {
-            token.releaseSession(session);
-        }
-    }
-
     private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
                 throws PKCS11Exception {
 
@@ -1689,48 +1623,26 @@
                 throws PKCS11Exception, KeyStoreException {
 
         SecretKey skey = ske.getSecretKey();
-        long keyType = CKK_GENERIC_SECRET;
-
-        if (skey instanceof P11Key && this.token == ((P11Key)skey).token) {
-            updateP11Skey(alias, (P11Key)skey);
-            return;
-        }
-
-        if ("AES".equalsIgnoreCase(skey.getAlgorithm())) {
-            keyType = CKK_AES;
-        } else if ("Blowfish".equalsIgnoreCase(skey.getAlgorithm())) {
-            keyType = CKK_BLOWFISH;
-        } else if ("DES".equalsIgnoreCase(skey.getAlgorithm())) {
-            keyType = CKK_DES;
-        } else if ("DESede".equalsIgnoreCase(skey.getAlgorithm())) {
-            keyType = CKK_DES3;
-        } else if ("RC4".equalsIgnoreCase(skey.getAlgorithm()) ||
-                    "ARCFOUR".equalsIgnoreCase(skey.getAlgorithm())) {
-            keyType = CKK_RC4;
+        // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since
+        // they are handled in P11SecretKeyFactory.createKey() method.
+        CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+            ATTR_SKEY_TOKEN_TRUE,
+            ATTR_PRIVATE_TRUE,
+            new CK_ATTRIBUTE(CKA_LABEL, alias),
+        };
+        try {
+            P11SecretKeyFactory.convertKey(token, skey, null, attrs);
+        } catch (InvalidKeyException ike) {
+            // re-throw KeyStoreException to match javadoc
+            throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);
         }
 
-        CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
-                ATTR_SKEY_TOKEN_TRUE,
-                ATTR_CLASS_SKEY,
-                ATTR_PRIVATE_TRUE,
-                new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
-                new CK_ATTRIBUTE(CKA_LABEL, alias),
-                new CK_ATTRIBUTE(CKA_VALUE, skey.getEncoded()) };
-        attrs = token.getAttributes
-                (TemplateManager.O_IMPORT, CKO_SECRET_KEY, keyType, attrs);
+        // update global alias map
+        aliasMap.put(alias, new AliasInfo(alias));
 
-        // create the new entry
-        Session session = null;
-        try {
-            session = token.getOpSession();
-            token.p11.C_CreateObject(session.id(), attrs);
-            if (debug != null) {
-                debug.println("storeSkey created token secret key for [" +
-                                alias +
-                                "]");
-            }
-        } finally {
-            token.releaseSession(session);
+        if (debug != null) {
+            debug.println("storeSkey created token secret key for [" +
+                          alias + "]");
         }
     }
 
@@ -2492,7 +2404,8 @@
             // if there are duplicates (either between secret keys,
             // or between a secret key and another object),
             // throw an exception
-            HashSet<String> sKeySet = new HashSet<String>();
+            HashMap<String, AliasInfo> sKeyMap =
+                    new HashMap<String, AliasInfo>();
 
             attrs = new CK_ATTRIBUTE[] {
                 ATTR_SKEY_TOKEN_TRUE,
@@ -2507,8 +2420,8 @@
 
                     // there is a CKA_LABEL
                     String cka_label = new String(attrs[0].getCharArray());
-                    if (!sKeySet.contains(cka_label)) {
-                        sKeySet.add(cka_label);
+                    if (sKeyMap.get(cka_label) == null) {
+                        sKeyMap.put(cka_label, new AliasInfo(cka_label));
                     } else {
                         throw new KeyStoreException("invalid KeyStore state: " +
                                 "found multiple secret keys sharing same " +
@@ -2523,7 +2436,7 @@
             ArrayList<AliasInfo> matchedCerts =
                                 mapPrivateKeys(pkeyIDs, certMap);
             boolean sharedLabel = mapCerts(matchedCerts, certMap);
-            mapSecretKeys(sKeySet);
+            mapSecretKeys(sKeyMap);
 
             return sharedLabel;
 
@@ -2547,7 +2460,7 @@
                         HashMap<String, HashSet<AliasInfo>> certMap)
                 throws PKCS11Exception, CertificateException {
 
-        // global alias map
+        // reset global alias map
         aliasMap = new HashMap<String, AliasInfo>();
 
         // list of matched certs that we will return
@@ -2722,18 +2635,17 @@
      * If the secret key shares a CKA_LABEL with another entry,
      * throw an exception
      */
-    private void mapSecretKeys(HashSet<String> sKeySet)
+    private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)
                 throws KeyStoreException {
-        for (String label : sKeySet) {
-            if (!aliasMap.containsKey(label)) {
-                aliasMap.put(label, new AliasInfo(label));
-            } else {
+        for (String label : sKeyMap.keySet()) {
+            if (aliasMap.containsKey(label)) {
                 throw new KeyStoreException("invalid KeyStore state: " +
                         "found secret key sharing CKA_LABEL [" +
                         label +
                         "] with another token object");
             }
         }
+        aliasMap.putAll(sKeyMap);
     }
 
     private void dumpTokenMap() {
--- a/jdk/src/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java	Thu Mar 20 17:17:10 2008 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java	Thu Mar 20 18:41:05 2008 -0700
@@ -108,6 +108,17 @@
      */
     static P11Key convertKey(Token token, Key key, String algo)
             throws InvalidKeyException {
+        return convertKey(token, key, algo, null);
+    }
+
+    /**
+     * Convert an arbitrary key of algorithm w/ custom attributes into a
+     * P11Key of provider.
+     * Used in P11KeyStore.storeSkey.
+     */
+    static P11Key convertKey(Token token, Key key, String algo,
+            CK_ATTRIBUTE[] extraAttrs)
+            throws InvalidKeyException {
         token.ensureValid();
         if (key == null) {
             throw new InvalidKeyException("Key must not be null");
@@ -134,6 +145,22 @@
         if (key instanceof P11Key) {
             P11Key p11Key = (P11Key)key;
             if (p11Key.token == token) {
+                if (extraAttrs != null) {
+                    Session session = null;
+                    try {
+                        session = token.getObjSession();
+                        long newKeyID = token.p11.C_CopyObject(session.id(),
+                                p11Key.keyID, extraAttrs);
+                        p11Key = (P11Key) (P11Key.secretKey(p11Key.session,
+                                newKeyID, p11Key.algorithm, p11Key.keyLength,
+                                extraAttrs));
+                    } catch (PKCS11Exception p11e) {
+                        throw new InvalidKeyException
+                                ("Cannot duplicate the PKCS11 key", p11e);
+                    } finally {
+                        token.releaseSession(session);
+                    }
+                }
                 return p11Key;
             }
         }
@@ -141,11 +168,11 @@
         if (p11Key != null) {
             return p11Key;
         }
-        if ("RAW".equals(key.getFormat()) == false) {
+        if ("RAW".equalsIgnoreCase(key.getFormat()) == false) {
             throw new InvalidKeyException("Encoded format must be RAW");
         }
         byte[] encoded = key.getEncoded();
-        p11Key = createKey(token, encoded, algo, algoType);
+        p11Key = createKey(token, encoded, algo, algoType, extraAttrs);
         token.secretCache.put(key, p11Key);
         return p11Key;
     }
@@ -159,7 +186,8 @@
     }
 
     private static P11Key createKey(Token token, byte[] encoded,
-            String algorithm, long keyType) throws InvalidKeyException {
+            String algorithm, long keyType, CK_ATTRIBUTE[] extraAttrs)
+            throws InvalidKeyException {
         int n = encoded.length << 3;
         int keyLength = n;
         try {
@@ -220,11 +248,17 @@
         }
         Session session = null;
         try {
-            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
-                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
-                new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
-                new CK_ATTRIBUTE(CKA_VALUE, encoded)
-            };
+            CK_ATTRIBUTE[] attributes;
+            if (extraAttrs != null) {
+                attributes = new CK_ATTRIBUTE[3 + extraAttrs.length];
+                System.arraycopy(extraAttrs, 0, attributes, 3,
+                        extraAttrs.length);
+            } else {
+                attributes = new CK_ATTRIBUTE[3];
+            }
+            attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
+            attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType);
+            attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded);
             attributes = token.getAttributes
                 (O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
             session = token.getObjSession();
@@ -273,7 +307,7 @@
     private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException {
         try {
             key = engineTranslateKey(key);
-            if ("RAW".equals(key.getFormat()) == false) {
+            if ("RAW".equalsIgnoreCase(key.getFormat()) == false) {
                 throw new InvalidKeySpecException
                     ("Could not obtain key bytes");
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java	Thu Mar 20 18:41:05 2008 -0700
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.util.*;
+import java.lang.reflect.*;
+
+import java.security.*;
+import java.security.cert.*;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import java.math.BigInteger;
+
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class SecretKeysBasic extends PKCS11Test {
+
+    private static final char SEP = File.separatorChar;
+    private static char[] tokenPwd;
+    private static final char[] nssPwd =
+            new char[]{'t', 'e', 's', 't', '1', '2'};
+    private static final char[] solarisPwd =
+            new char[]{'p', 'i', 'n'};
+    private static SecretKey sk1;
+    private static SecretKey sk2;
+    private static SecretKey softkey;
+    private static KeyStore ks;
+    private static final String KS_TYPE = "PKCS11";
+    private static Provider provider;
+
+    public static void main(String[] args) throws Exception {
+        main(new SecretKeysBasic());
+    }
+
+    public void main(Provider p) throws Exception {
+        this.provider = p;
+
+        // create secret key
+        byte[] keyVal = new byte[16];
+        (new SecureRandom()).nextBytes(keyVal);
+        // NSS will throw CKR_HOST_MEMORY if calling C_DecryptInit w/
+        // (keyVal[0] == 0)
+        if (keyVal[0] == 0) {
+            keyVal[0] = 1;
+        }
+        softkey = new SecretKeySpec(keyVal, "AES");
+        dumpKey("softkey", softkey);
+
+        KeyGenerator kg = KeyGenerator.getInstance("DESede", provider);
+        sk1 = kg.generateKey();
+        dumpKey("skey1", sk1);
+        sk2 = kg.generateKey();
+        dumpKey("skey2", sk2);
+
+        String token = System.getProperty("TOKEN");
+
+        if (token == null || token.length() == 0) {
+            System.out.println("Error: missing TOKEN system property");
+            throw new Exception("token arg required");
+        }
+
+        if ("nss".equals(token)) {
+            tokenPwd = nssPwd;
+        } else if ("solaris".equals(token)) {
+            tokenPwd = solarisPwd;
+        }
+
+        int testnum = 1;
+        doTest();
+    }
+
+    private static boolean checkSecretKeyEntry(String alias,
+            SecretKey expected,
+            boolean saveBeforeCheck)
+            throws Exception {
+        if (saveBeforeCheck) {
+            ks.setKeyEntry(alias, expected, null, null);
+        }
+        SecretKey result = (SecretKey) (ks.getKey(alias, null));
+        String keyEncFormat = result.getFormat();
+        if (keyEncFormat == null) {
+            // sensitive or un-extractable keys - verify by encrypt/decrypt
+            byte[] data = new byte[64];
+            Cipher c =
+                    Cipher.getInstance(result.getAlgorithm() + "/CBC/NoPadding",
+                    provider);
+            c.init(Cipher.ENCRYPT_MODE, expected);
+            byte[] encOut = c.doFinal(data);
+            c.init(Cipher.DECRYPT_MODE, result, c.getParameters());
+            byte[] decOut = c.doFinal(encOut);
+            if (!Arrays.equals(data, decOut)) {
+                return false;
+            }
+        } else if (keyEncFormat.toUpperCase().equals("RAW")) {
+            if (!Arrays.equals(result.getEncoded(), expected.getEncoded())) {
+                dumpKey("\texpected:", expected);
+                dumpKey("\treturns:", result);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static void dumpKey(String info, SecretKey key) {
+        System.out.println(info + "> " + key);
+        System.out.println("\tALGO=" + key.getAlgorithm());
+        if (key.getFormat() != null) {
+            System.out.println("\t[" + key.getFormat() + "] VALUE=" +
+                    new BigInteger(key.getEncoded()));
+        } else {
+            System.out.println("\tVALUE=n/a");
+        }
+    }
+
+    private static void doTest() throws Exception {
+        if (ks == null) {
+            ks = KeyStore.getInstance(KS_TYPE, provider);
+            ks.load(null, tokenPwd);
+        }
+
+        System.out.println("Number of entries: " + ks.size());
+        if (ks.size() != 0) {
+            System.out.println("Deleting entries under aliases: ");
+            for (Enumeration<String> aliases = ks.aliases();
+                    aliases.hasMoreElements();) {
+                String alias = aliases.nextElement();
+                System.out.println("\t" + alias);
+                ks.deleteEntry(alias);
+            }
+        }
+
+        String alias = "testSKey";
+
+        boolean testResult = checkSecretKeyEntry(alias, softkey, true);
+        if (!testResult) {
+            System.out.println("FAILURE: setKey() w/ softSecretKey failed");
+        }
+
+        if (!checkSecretKeyEntry(alias, sk1, true)) {
+            testResult = false;
+            System.out.println("FAILURE: setKey() w/ skey1 failed");
+        }
+        if (!checkSecretKeyEntry(alias, sk2, true)) {
+            testResult = false;
+            System.out.println("FAILURE: setKey() w/ skey2 failed");
+        }
+
+        ks.store(null);
+        System.out.println("Reloading keystore...");
+
+        ks.load(null, "whatever".toCharArray());
+        if (ks.size() != 1) {
+            System.out.println("FAILURE: reload#1 ks.size() != 1");
+        }
+        if (!checkSecretKeyEntry(alias, sk2, false)) {
+            testResult = false;
+            System.out.println("FAILURE: reload#1 ks entry check failed");
+        }
+
+        ks.deleteEntry(alias);
+        ks.store(null);
+
+        System.out.println("Reloading keystore...");
+        ks.load(null, "whatever".toCharArray());
+        if (ks.size() != 0) {
+            testResult = false;
+            System.out.println("FAILURE: reload#2 ks.size() != 0");
+        }
+        if (!testResult) {
+            throw new Exception("One or more test failed!");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.sh	Thu Mar 20 18:41:05 2008 -0700
@@ -0,0 +1,164 @@
+#
+# Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6599979
+# @summary Ensure that re-assigning the alias works
+#
+# @run shell SecretKeysBasic.sh
+#
+# To run by hand:
+#    %sh SecretKeysBasic.sh
+#
+# Note:
+#    . test only runs on solaris at the moment
+
+# set a few environment variables so that the shell-script can run stand-alone
+# in the source directory
+
+# if running by hand on windows, change TESTSRC and TESTCLASSES to "."
+if [ "${TESTSRC}" = "" ] ; then
+    TESTSRC=`pwd`
+fi
+if [ "${TESTCLASSES}" = "" ] ; then
+    TESTCLASSES=`pwd`
+fi
+
+# if running by hand on windows, change this to appropriate value
+if [ "${TESTJAVA}" = "" ] ; then
+    TESTJAVA="/net/shimmer/export/valeriep/jdk7/build/solaris-sparc"
+fi
+echo TESTSRC=${TESTSRC}
+echo TESTCLASSES=${TESTCLASSES}
+echo TESTJAVA=${TESTJAVA}
+echo ""
+
+#DEBUG=sunpkcs11,pkcs11keystore
+
+echo DEBUG=${DEBUG}
+echo ""
+
+OS=`uname -s`
+case "$OS" in
+  SunOS )
+    FS="/"
+    PS=":"
+    SCCS="${FS}usr${FS}ccs${FS}bin${FS}sccs"
+    CP="${FS}bin${FS}cp -f"
+    RM="${FS}bin${FS}rm -rf"
+    MKDIR="${FS}bin${FS}mkdir -p"
+    CHMOD="${FS}bin${FS}chmod"
+    ;;
+  * )
+    echo "Unsupported System ${OS} - Test only runs on Solaris"
+    exit 0;
+    ;;
+esac
+
+TOKENS="nss solaris"
+STATUS=0
+for token in ${TOKENS}
+do
+
+if [ ${token} = "nss" ]
+then
+    # make cert/key DBs writable if token is NSS
+    ${CP} ${TESTSRC}${FS}..${FS}nss${FS}db${FS}cert8.db ${TESTCLASSES}
+    ${CHMOD} +w ${TESTCLASSES}${FS}cert8.db
+
+    ${CP} ${TESTSRC}${FS}..${FS}nss${FS}db${FS}key3.db ${TESTCLASSES}
+    ${CHMOD} +w ${TESTCLASSES}${FS}key3.db
+    USED_FILE_LIST="${TESTCLASSES}${FS}cert8.db ${TESTCLASSES}${FS}key3.db"
+elif [ ${token} = "solaris" ]
+then
+    OS_VERSION=`uname -r`
+    case "${OS_VERSION}" in
+      5.1* )
+        SOFTTOKEN_DIR=${TESTCLASSES}
+        export SOFTTOKEN_DIR
+        ;;
+      * )
+        echo "Unsupported Version ${OS_VERSION} - Test only runs on Solaris"
+        exit 0;
+        ;;
+    esac
+
+    # copy keystore into write-able location
+    if [ -d ${TESTCLASSES}${FS}pkcs11_softtoken ]
+    then
+        echo "Removing old pkcs11_keystore, creating new pkcs11_keystore"
+
+        echo ${RM} ${TESTCLASSES}${FS}pkcs11_softtoken
+        ${RM} ${TESTCLASSES}${FS}pkcs11_softtoken
+    fi
+    echo ${MKDIR} ${TESTCLASSES}${FS}pkcs11_softtoken${FS}private
+    ${MKDIR} ${TESTCLASSES}${FS}pkcs11_softtoken${FS}private
+
+    echo ${MKDIR} ${TESTCLASSES}${FS}pkcs11_softtoken${FS}public
+    ${MKDIR} ${TESTCLASSES}${FS}pkcs11_softtoken${FS}public
+
+    echo ${CP} ${TESTSRC}${FS}BasicData${FS}pkcs11_softtoken${FS}objstore_info \
+	${TESTCLASSES}${FS}pkcs11_softtoken
+    ${CP} ${TESTSRC}${FS}BasicData${FS}pkcs11_softtoken${FS}objstore_info \
+	${TESTCLASSES}${FS}pkcs11_softtoken
+
+    echo ${CHMOD} +w ${TESTCLASSES}${FS}pkcs11_softtoken${FS}objstore_info
+    ${CHMOD} 600 ${TESTCLASSES}${FS}pkcs11_softtoken${FS}objstore_info
+    USED_FILE_LIST="${TESTCLASSES}${FS}pkcs11_softtoken"
+fi
+
+cd ${TESTCLASSES}
+${TESTJAVA}${FS}bin${FS}javac \
+        -classpath ${TESTCLASSES} \
+        -d ${TESTCLASSES} \
+        ${TESTSRC}${FS}SecretKeysBasic.java
+
+# run test
+cd ${TESTSRC}
+${TESTJAVA}${FS}bin${FS}java \
+	-DDIR=${TESTSRC}${FS}BasicData${FS} \
+        -classpath ${TESTCLASSES}${PS}${TESTSRC}${FS}loader.jar \
+        -DCUSTOM_DB_DIR=${TESTCLASSES} \
+        -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}BasicData${FS}p11-${token}.txt \
+	-DNO_DEFAULT=true \
+	-DNO_DEIMOS=true \
+	-DTOKEN=${token} \
+	-Djava.security.debug=${DEBUG} \
+	SecretKeysBasic
+
+#	-DCUSTOM_P11_CONFIG=${TESTSRC}${FS}BasicData${FS}p11-${token}.txt \
+
+# save error status
+if [ $? != 0 ]
+then
+    echo "Test against " ${token} " Failed!"
+    STATUS=1
+fi
+
+# clean up
+${RM} ${USED_FILE_LIST}
+
+done
+
+# return
+exit ${STATUS}