Merge
authorlana
Tue, 20 Apr 2010 16:51:13 -0700
changeset 5302 27f1f6b3108a
parent 5287 4114baee5fe9 (current diff)
parent 5301 1bc2a3a47839 (diff)
child 5309 2cd65a5d9fdb
Merge
jdk/make/tools/CharsetMapping/Big5.c2b
--- a/jdk/make/tools/CharsetMapping/Big5.c2b	Tue Apr 20 16:50:10 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#Add the following 5 characters which are duplicated
-#or have conflicts with other characters.
-0xA1C4	0xFF3F	#REGWARN Fallback 0xA1C4 SPACING UNDERSCORE
-0xA2AC	0x2571	#REGWARN Fallback 0xA2AC LT DIAG UP RIGHT TO LOW LEFT
-0xA2AD	0x2572	#REGWARN Fallback 0xA2AD LT DIAG UP LEFT TO LOW RIGHT
-0xA451	0x5341	#REGWARN Fallback 0xA451 HANGZHOU NUMERAL TEN
-0xA4CA	0x5345	#REGWARN Fallback 0xA4CA HANGZHOU NUMERAL THIRTY
-#
--- a/jdk/make/tools/CharsetMapping/Big5.map	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/make/tools/CharsetMapping/Big5.map	Tue Apr 20 16:51:13 2010 -0700
@@ -13830,8 +13830,21 @@
 0xF9D4	0x9F49	# <CJK>
 0xF9D5	0x9F98	# <CJK>
 #
+# from Big5.b2c-irreversible
+#
 0xA15A	0xFF3F #SPACING UNDERSCORE
 0xA1FE	0x2571 #LT DIAG UP RIGHT TO LOW LEFT
 0xA240	0x2572 #LT DIAG UP LEFT TO LOW RIGHTG
 0xA2CC	0x5341 #HANGHZOU NUMERAL TEN
 0xA2CE	0x5345 #HANGZHOU NUMERAL THIRTY
+#
+# Add the following 5 characters from Big5.c2b-irreversible
+# It appears these 5 should be here to be the round-trip
+# for these 5 characters. Above 5 are the b->c only nrt.
+#
+0xA1C4	0xFF3F
+0xA2AC	0x2571
+0xA2AD	0x2572
+0xA451	0x5341
+0xA4CA	0x5345
+#
--- a/jdk/src/share/classes/java/net/NetworkInterface.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/java/net/NetworkInterface.java	Tue Apr 20 16:51:13 2010 -0700
@@ -221,11 +221,12 @@
      * A display name is a human readable String describing the network
      * device.
      *
-     * @return the display name of this network interface,
-     *         or null if no display name is available.
+     * @return a non-empty string representing the display name of this network
+     *         interface, or null if no display name is available.
      */
     public String getDisplayName() {
-        return displayName;
+        /* strict TCK conformance */
+        return "".equals(displayName) ? null : displayName;
     }
 
     /**
--- a/jdk/src/share/classes/sun/misc/SharedSecrets.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/misc/SharedSecrets.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2002-2010 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
@@ -29,6 +29,7 @@
 import java.io.Console;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.security.ProtectionDomain;
 
 /** A repository of "shared secrets", which are a mechanism for
     calling implementation-private methods in another package without
@@ -121,6 +122,8 @@
 
     public static JavaSecurityProtectionDomainAccess
         getJavaSecurityProtectionDomainAccess() {
+            if (javaSecurityProtectionDomainAccess == null)
+                unsafe.ensureClassInitialized(ProtectionDomain.class);
             return javaSecurityProtectionDomainAccess;
     }
 }
--- a/jdk/src/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2010 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
@@ -77,26 +77,34 @@
                             if (DEBUG) err.printStackTrace();
                             return null;
                         }
-                        String gssLib = System.getProperty(LIB_PROP);
-                        if (gssLib == null || gssLib.trim().equals("")) {
+                        String gssLibs[] = new String[0];
+                        String defaultLib = System.getProperty(LIB_PROP);
+                        if (defaultLib == null || defaultLib.trim().equals("")) {
                             String osname = System.getProperty("os.name");
                             if (osname.startsWith("SunOS")) {
-                                gssLib = "libgss.so";
+                                gssLibs = new String[]{ "libgss.so" };
                             } else if (osname.startsWith("Linux")) {
-                                gssLib = "libgssapi.so";
+                                gssLibs = new String[]{
+                                    "libgssapi.so",
+                                    "libgssapi_krb5.so",
+                                };
                             }
+                        } else {
+                            gssLibs = new String[]{ defaultLib };
                         }
-                        if (GSSLibStub.init(gssLib)) {
-                            debug("Loaded GSS library: " + gssLib);
-                            Oid[] mechs = GSSLibStub.indicateMechs();
-                            HashMap<String, String> map =
-                                        new HashMap<String, String>();
-                            for (int i = 0; i < mechs.length; i++) {
-                                debug("Native MF for " + mechs[i]);
-                                map.put("GssApiMechanism." + mechs[i],
-                                        MF_CLASS);
+                        for (String libName: gssLibs) {
+                            if (GSSLibStub.init(libName)) {
+                                debug("Loaded GSS library: " + libName);
+                                Oid[] mechs = GSSLibStub.indicateMechs();
+                                HashMap<String, String> map =
+                                            new HashMap<String, String>();
+                                for (int i = 0; i < mechs.length; i++) {
+                                    debug("Native MF for " + mechs[i]);
+                                    map.put("GssApiMechanism." + mechs[i],
+                                            MF_CLASS);
+                                }
+                                return map;
                             }
-                            return map;
                         }
                         return null;
                     }
--- a/jdk/src/share/classes/sun/security/pkcs11/P11Cipher.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11Cipher.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -192,7 +192,6 @@
             // should not happen
             throw new ProviderException(nspe);
         }
-        session = token.getOpSession();
     }
 
     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
@@ -847,18 +846,6 @@
         return n;
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if ((session != null) && token.isValid()) {
-                cancelOperation();
-                session = token.releaseSession(session);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
     private final void bufferInputBytes(byte[] in, int inOfs, int len) {
         System.arraycopy(in, inOfs, padBuffer, padBufferLen, len);
         padBufferLen += len;
--- a/jdk/src/share/classes/sun/security/pkcs11/P11Digest.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11Digest.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -308,16 +308,4 @@
             throw new ProviderException("update() failed", e);
         }
     }
-
-    protected void finalize() throws Throwable {
-        try {
-            if ((session != null) && token.isValid()) {
-                cancelOperation();
-                session = token.releaseSession(session);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
 }
--- a/jdk/src/share/classes/sun/security/pkcs11/P11Key.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11Key.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -85,7 +85,7 @@
     // flags indicating whether the key is a token object, sensitive, extractable
     final boolean tokenObject, sensitive, extractable;
 
-    // weak reference notification clean up for session keys
+    // phantom reference notification clean up for session keys
     private final SessionKeyRef sessionKeyRef;
 
     P11Key(String type, Session session, long keyID, String algorithm,
@@ -1051,7 +1051,12 @@
     }
 }
 
-final class SessionKeyRef extends WeakReference<P11Key>
+/*
+ * NOTE: Must use PhantomReference here and not WeakReference
+ * otherwise the key maybe cleared before other objects which
+ * still use these keys during finalization such as SSLSocket.
+ */
+final class SessionKeyRef extends PhantomReference<P11Key>
     implements Comparable<SessionKeyRef> {
     private static ReferenceQueue<P11Key> refQueue =
         new ReferenceQueue<P11Key>();
@@ -1062,14 +1067,11 @@
         return refQueue;
     }
 
-    static final private int MAX_ITERATIONS = 2;
-
     private static void drainRefQueueBounded() {
-        int iterations = 0;
-        while (iterations < MAX_ITERATIONS) {
+        while (true) {
             SessionKeyRef next = (SessionKeyRef) refQueue.poll();
-            if (next != null) next.dispose();
-            ++iterations;
+            if (next == null) break;
+            next.dispose();
         }
     }
 
@@ -1087,7 +1089,7 @@
         drainRefQueueBounded();
     }
 
-    void dispose() {
+    private void dispose() {
         refList.remove(this);
         if (session.token.isValid()) {
             Session newSession = null;
@@ -1097,6 +1099,7 @@
             } catch (PKCS11Exception e) {
                 // ignore
             } finally {
+                this.clear();
                 session.token.releaseSession(newSession);
                 session.removeObject();
             }
--- a/jdk/src/share/classes/sun/security/pkcs11/P11Mac.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11Mac.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -263,16 +263,4 @@
             throw new ProviderException("update() failed", e);
         }
     }
-
-    protected void finalize() throws Throwable {
-        try {
-            if ((session != null) && token.isValid()) {
-                cancelOperation();
-                session = token.releaseSession(session);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
 }
--- a/jdk/src/share/classes/sun/security/pkcs11/P11RSACipher.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11RSACipher.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -485,18 +485,6 @@
         int n = P11KeyFactory.convertKey(token, key, algorithm).keyLength();
         return n;
     }
-
-    protected void finalize() throws Throwable {
-        try {
-            if ((session != null) && token.isValid()) {
-                cancelOperation();
-                session = token.releaseSession(session);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
 }
 
 final class ConstructKeys {
--- a/jdk/src/share/classes/sun/security/pkcs11/P11Signature.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11Signature.java	Tue Apr 20 16:51:13 2010 -0700
@@ -226,7 +226,6 @@
         this.buffer = buffer;
         this.digestOID = digestOID;
         this.md = md;
-        session = token.getOpSession();
     }
 
     private void ensureInitialized() {
@@ -732,16 +731,4 @@
             throws InvalidParameterException {
         throw new UnsupportedOperationException("getParameter() not supported");
     }
-
-    protected void finalize() throws Throwable {
-        try {
-            if ((session != null) && token.isValid()) {
-                cancelOperation();
-                session = token.releaseSession(session);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
 }
--- a/jdk/src/share/classes/sun/security/pkcs11/Session.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/Session.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -25,6 +25,7 @@
 
 package sun.security.pkcs11;
 
+import java.lang.ref.*;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -59,11 +60,14 @@
     // this could lead to idle sessions being closed early, but that is harmless
     private long lastAccess;
 
+    private final SessionRef sessionRef;
+
     Session(Token token, long id) {
         this.token = token;
         this.id = id;
         createdObjects = new AtomicInteger();
         id();
+        sessionRef = new SessionRef(this, id, token);
     }
 
     public int compareTo(Session other) {
@@ -108,4 +112,76 @@
         return createdObjects.get() != 0;
     }
 
+    void close() {
+        if (hasObjects()) {
+            throw new ProviderException(
+                "Internal error: close session with active objects");
+        }
+        sessionRef.dispose();
+    }
 }
+
+/*
+ * NOTE: Use PhantomReference here and not WeakReference
+ * otherwise the sessions maybe closed before other objects
+ * which are still being finalized.
+ */
+final class SessionRef extends PhantomReference<Session>
+        implements Comparable<SessionRef> {
+
+    private static ReferenceQueue<Session> refQueue =
+        new ReferenceQueue<Session>();
+
+    private static Set<SessionRef> refList =
+        Collections.synchronizedSortedSet(new TreeSet<SessionRef>());
+
+    static ReferenceQueue<Session> referenceQueue() {
+        return refQueue;
+    }
+
+    static int totalCount() {
+        return refList.size();
+    }
+
+    private static void drainRefQueueBounded() {
+        while (true) {
+            SessionRef next = (SessionRef) refQueue.poll();
+            if (next == null) break;
+            next.dispose();
+        }
+    }
+
+    // handle to the native session
+    private long id;
+    private Token token;
+
+    SessionRef(Session session, long id, Token token) {
+        super(session, refQueue);
+        this.id = id;
+        this.token = token;
+        refList.add(this);
+        // TBD: run at some interval and not every time?
+        drainRefQueueBounded();
+    }
+
+    void dispose() {
+        refList.remove(this);
+        try {
+            token.p11.C_CloseSession(id);
+        } catch (PKCS11Exception e1) {
+            // ignore
+        } catch (ProviderException e2) {
+            // ignore
+        } finally {
+            this.clear();
+        }
+    }
+
+    public int compareTo(SessionRef other) {
+        if (this.id == other.id) {
+            return 0;
+        } else {
+            return (this.id < other.id) ? -1 : 1;
+        }
+    }
+}
--- a/jdk/src/share/classes/sun/security/pkcs11/SessionManager.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/SessionManager.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 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
@@ -51,10 +51,12 @@
  * number of such sessions low. Note that we occasionally want to explicitly
  * close a session, see P11Signature.
  *
- * NOTE that all sessions obtained from this class MUST be returned using
- * either releaseSession() or closeSession() using a finally block or a
- * finalizer where appropriate. Otherwise, they will be "lost", i.e. there
- * will be a resource leak eventually leading to exhaustion.
+ * NOTE that sessions obtained from this class SHOULD be returned using
+ * either releaseSession() or closeSession() using a finally block when
+ * not needed anymore. Otherwise, they will be left for cleanup via the
+ * PhantomReference mechanism when GC kicks in, but it's best not to rely
+ * on that since GC may not run timely enough since the native PKCS11 library
+ * is also consuming memory.
  *
  * Note that sessions are automatically closed when they are not used for a
  * period of time, see Session.
@@ -74,9 +76,6 @@
     // maximum number of sessions to open with this token
     private final int maxSessions;
 
-    // total number of active sessions
-    private int activeSessions;
-
     // pool of available object sessions
     private final Pool objSessions;
 
@@ -116,6 +115,11 @@
         return (maxSessions <= DEFAULT_MAX_SESSIONS);
     }
 
+    // returns the total number of active sessions
+    int totalSessionCount() {
+        return SessionRef.totalCount();
+    }
+
     synchronized Session getObjSession() throws PKCS11Exception {
         Session session = objSessions.poll();
         if (session != null) {
@@ -136,7 +140,8 @@
         }
         // create a new session rather than re-using an obj session
         // that avoids potential expensive cancels() for Signatures & RSACipher
-        if (activeSessions < maxSessions) {
+        if (maxSessions == Integer.MAX_VALUE ||
+                totalSessionCount() < maxSessions) {
             session = openSession();
             return ensureValid(session);
         }
@@ -159,14 +164,10 @@
         if (debug != null) {
             String location = new Exception().getStackTrace()[2].toString();
             System.out.println("Killing session (" + location + ") active: "
-                + activeSessions);
+                + totalSessionCount());
         }
-        try {
-            closeSession(session);
-            return null;
-        } catch (PKCS11Exception e) {
-            throw new ProviderException(e);
-        }
+        closeSession(session);
+        return null;
     }
 
     synchronized Session releaseSession(Session session) {
@@ -187,7 +188,8 @@
             return;
         }
         if (debug != null) {
-            System.out.println("Demoting session, active: " + activeSessions);
+            System.out.println("Demoting session, active: " +
+                totalSessionCount());
         }
         boolean present = objSessions.remove(session);
         if (present == false) {
@@ -199,16 +201,17 @@
     }
 
     private Session openSession() throws PKCS11Exception {
-        if (activeSessions >= maxSessions) {
+        if ((maxSessions != Integer.MAX_VALUE) &&
+                (totalSessionCount() >= maxSessions)) {
             throw new ProviderException("No more sessions available");
         }
         long id = token.p11.C_OpenSession
                     (token.provider.slotID, openSessionFlags, null, null);
         Session session = new Session(token, id);
-        activeSessions++;
         if (debug != null) {
-            if (activeSessions > maxActiveSessions) {
-                maxActiveSessions = activeSessions;
+            int currTotal = totalSessionCount();
+            if (currTotal > maxActiveSessions) {
+                maxActiveSessions = currTotal;
                 if (maxActiveSessions % 10 == 0) {
                     System.out.println("Open sessions: " + maxActiveSessions);
                 }
@@ -217,13 +220,8 @@
         return session;
     }
 
-    private void closeSession(Session session) throws PKCS11Exception {
-        if (session.hasObjects()) {
-            throw new ProviderException
-                ("Internal error: close session with active objects");
-        }
-        token.p11.C_CloseSession(session.id());
-        activeSessions--;
+    private void closeSession(Session session) {
+        session.close();
     }
 
     private static final class Pool {
@@ -267,28 +265,20 @@
             }
             Collections.sort(pool);
             int i = 0;
-            PKCS11Exception exc = null;
             while (i < n - 1) { // always keep at least 1 session open
                 oldestSession = pool.get(i);
                 if (oldestSession.isLive(time)) {
                     break;
                 }
                 i++;
-                try {
-                    mgr.closeSession(oldestSession);
-                } catch (PKCS11Exception e) {
-                    exc = e;
-                }
+                mgr.closeSession(oldestSession);
             }
             if (debug != null) {
                 System.out.println("Closing " + i + " idle sessions, active: "
-                        + mgr.activeSessions);
+                        + mgr.totalSessionCount());
             }
             List<Session> subList = pool.subList(0, i);
             subList.clear();
-            if (exc != null) {
-                throw new ProviderException(exc);
-            }
         }
 
     }
--- a/jdk/src/share/classes/sun/security/pkcs11/wrapper/PKCS11Exception.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/wrapper/PKCS11Exception.java	Tue Apr 20 16:51:13 2010 -0700
@@ -148,6 +148,7 @@
             0x00000115,
             0x00000120,
             0x00000121,
+            0x00000130,
             0x00000150,
             0x00000160,
             0x00000170,
@@ -156,6 +157,7 @@
             0x00000191,
             0x000001A0,
             0x000001A1,
+            0x00000200,
             0x80000000,
         };
         String[] errorMessages = new String[] {
@@ -234,6 +236,7 @@
             "CKR_WRAPPING_KEY_TYPE_INCONSISTENT",
             "CKR_RANDOM_SEED_NOT_SUPPORTED",
             "CKR_RANDOM_NO_RNG",
+            "CKR_DOMAIN_PARAMS_INVALID",
             "CKR_BUFFER_TOO_SMALL",
             "CKR_SAVED_STATE_INVALID",
             "CKR_INFORMATION_SENSITIVE",
@@ -242,6 +245,7 @@
             "CKR_CRYPTOKI_ALREADY_INITIALIZED",
             "CKR_MUTEX_BAD",
             "CKR_MUTEX_NOT_LOCKED",
+            "CKR_FUNCTION_REJECTED",
             "CKR_VENDOR_DEFINED",
         };
         errorMap = new HashMap<Long,String>();
--- a/jdk/src/share/classes/sun/security/smartcardio/CardImpl.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/smartcardio/CardImpl.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2010 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
@@ -214,7 +214,7 @@
             SCardEndTransaction(cardId, SCARD_LEAVE_CARD);
         } catch (PCSCException e) {
             handleError(e);
-            throw new CardException("beginExclusive() failed", e);
+            throw new CardException("endExclusive() failed", e);
         } finally {
             exclusiveThread = null;
         }
--- a/jdk/src/share/classes/sun/security/tools/KeyTool.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/tools/KeyTool.java	Tue Apr 20 16:51:13 2010 -0700
@@ -1211,6 +1211,14 @@
         X509CertImpl cert = new X509CertImpl(info);
         cert.sign(privateKey, sigAlgName);
         dumpCert(cert, out);
+        for (Certificate ca: keyStore.getCertificateChain(alias)) {
+            if (ca instanceof X509Certificate) {
+                X509Certificate xca = (X509Certificate)ca;
+                if (!isSelfSigned(xca)) {
+                    dumpCert(xca, out);
+                }
+            }
+        }
     }
 
     /**
@@ -2640,19 +2648,33 @@
     }
 
     /**
-     * Returns true if the given certificate is trusted, false otherwise.
+     * Locates a signer for a given certificate from a given keystore and
+     * returns the signer's certificate.
+     * @param cert the certificate whose signer is searched, not null
+     * @param ks the keystore to search with, not null
+     * @return <code>cert</code> itself if it's already inside <code>ks</code>,
+     * or a certificate inside <code>ks</code> who signs <code>cert</code>,
+     * or null otherwise.
      */
-    private boolean isTrusted(Certificate cert)
-        throws Exception
-    {
-        if (keyStore.getCertificateAlias(cert) != null) {
-            return true; // found in own keystore
+    private static Certificate getTrustedSigner(Certificate cert, KeyStore ks)
+            throws Exception {
+        if (ks.getCertificateAlias(cert) != null) {
+            return cert;
         }
-        if (trustcacerts && (caks != null) &&
-                (caks.getCertificateAlias(cert) != null)) {
-            return true; // found in CA keystore
+        for (Enumeration<String> aliases = ks.aliases();
+                aliases.hasMoreElements(); ) {
+            String name = aliases.nextElement();
+            Certificate trustedCert = ks.getCertificate(name);
+            if (trustedCert != null) {
+                try {
+                    cert.verify(trustedCert.getPublicKey());
+                    return trustedCert;
+                } catch (Exception e) {
+                    // Not verified, skip to the next one
+                }
+            }
         }
-        return false;
+        return null;
     }
 
     /**
@@ -2985,48 +3007,33 @@
             return replyCerts;
         }
 
-        // do we trust the (root) cert at the top?
+        // do we trust the cert at the top?
         Certificate topCert = replyCerts[replyCerts.length-1];
-        if (!isTrusted(topCert)) {
-            boolean verified = false;
-            Certificate rootCert = null;
-            if (trustcacerts && (caks!= null)) {
-                for (Enumeration<String> aliases = caks.aliases();
-                     aliases.hasMoreElements(); ) {
-                    String name = aliases.nextElement();
-                    rootCert = caks.getCertificate(name);
-                    if (rootCert != null) {
-                        try {
-                            topCert.verify(rootCert.getPublicKey());
-                            verified = true;
-                            break;
-                        } catch (Exception e) {
-                        }
-                    }
-                }
+        Certificate root = getTrustedSigner(topCert, keyStore);
+        if (root == null && trustcacerts && caks != null) {
+            root = getTrustedSigner(topCert, caks);
+        }
+        if (root == null) {
+            System.err.println();
+            System.err.println
+                    (rb.getString("Top-level certificate in reply:\n"));
+            printX509Cert((X509Certificate)topCert, System.out);
+            System.err.println();
+            System.err.print(rb.getString("... is not trusted. "));
+            String reply = getYesNoReply
+                    (rb.getString("Install reply anyway? [no]:  "));
+            if ("NO".equals(reply)) {
+                return null;
             }
-            if (!verified) {
-                System.err.println();
-                System.err.println
-                        (rb.getString("Top-level certificate in reply:\n"));
-                printX509Cert((X509Certificate)topCert, System.out);
-                System.err.println();
-                System.err.print(rb.getString("... is not trusted. "));
-                String reply = getYesNoReply
-                        (rb.getString("Install reply anyway? [no]:  "));
-                if ("NO".equals(reply)) {
-                    return null;
-                }
-            } else {
-                if (!isSelfSigned((X509Certificate)topCert)) {
-                    // append the (self-signed) root CA cert to the chain
-                    Certificate[] tmpCerts =
-                        new Certificate[replyCerts.length+1];
-                    System.arraycopy(replyCerts, 0, tmpCerts, 0,
-                                     replyCerts.length);
-                    tmpCerts[tmpCerts.length-1] = rootCert;
-                    replyCerts = tmpCerts;
-                }
+        } else {
+            if (root != topCert) {
+                // append the root CA cert to the chain
+                Certificate[] tmpCerts =
+                    new Certificate[replyCerts.length+1];
+                System.arraycopy(replyCerts, 0, tmpCerts, 0,
+                                 replyCerts.length);
+                tmpCerts[tmpCerts.length-1] = root;
+                replyCerts = tmpCerts;
             }
         }
 
--- a/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java	Tue Apr 20 16:51:13 2010 -0700
@@ -82,6 +82,11 @@
     private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
 
     /*
+     * Object identifier for extendedKeyUsage extension
+     */
+    private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
+
+    /*
      * Object identifier for the timestamping access descriptors.
      */
     private static final ObjectIdentifier AD_TIMESTAMPING_Id;
@@ -357,34 +362,26 @@
         }
 
         // Examine the TSA's certificate (if present)
-        List<String> keyPurposes = null;
-        X509Certificate[] certs = tsToken.getCertificates();
-        if (certs != null && certs.length > 0) {
-            // Use certficate from the TSP reply
-            // Pick out the cert for the TS server, which is the end-entity
-            // one inside the chain.
-            for (X509Certificate cert: certs) {
-                boolean isSigner = false;
-                for (X509Certificate cert2: certs) {
-                    if (cert != cert2) {
-                        if (cert.getSubjectDN().equals(cert2.getIssuerDN())) {
-                            isSigner = true;
-                            break;
-                        }
-                    }
+        for (SignerInfo si: tsToken.getSignerInfos()) {
+            X509Certificate cert = si.getCertificate(tsToken);
+            if (cert == null) {
+                // Error, we've already set tsRequestCertificate = true
+                throw new CertificateException(
+                "Certificate not included in timestamp token");
+            } else {
+                if (!cert.getCriticalExtensionOIDs().contains(
+                        EXTENDED_KEY_USAGE_OID)) {
+                    throw new CertificateException(
+                    "Certificate is not valid for timestamping");
                 }
-                if (!isSigner) {
-                    keyPurposes = cert.getExtendedKeyUsage();
-                    if (keyPurposes == null ||
-                            ! keyPurposes.contains(KP_TIMESTAMPING_OID)) {
-                        throw new CertificateException(
-                            "Certificate is not valid for timestamping");
-                    }
-                    break;
+                List keyPurposes = cert.getExtendedKeyUsage();
+                if (keyPurposes == null ||
+                        ! keyPurposes.contains(KP_TIMESTAMPING_OID)) {
+                    throw new CertificateException(
+                    "Certificate is not valid for timestamping");
                 }
             }
         }
-
         return tsReply.getEncodedToken();
     }
 }
--- a/jdk/src/share/classes/sun/security/x509/X509Key.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/src/share/classes/sun/security/x509/X509Key.java	Tue Apr 20 16:51:13 2010 -0700
@@ -171,7 +171,7 @@
                                       in.data.getUnalignedBitString());
 
         } catch (InvalidKeyException e) {
-            throw new IOException("subject key, " + e.getMessage());
+            throw new IOException("subject key, " + e.getMessage(), e);
         }
 
         if (in.data.available() != 0)
@@ -224,7 +224,7 @@
         } catch (NoSuchAlgorithmException e) {
             // Return generic X509Key with opaque key data (see below)
         } catch (InvalidKeySpecException e) {
-            throw new InvalidKeyException(e.getMessage());
+            throw new InvalidKeyException(e.getMessage(), e);
         }
 
         /*
--- a/jdk/test/java/util/regex/RegExTest.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/java/util/regex/RegExTest.java	Tue Apr 20 16:51:13 2010 -0700
@@ -32,7 +32,7 @@
  * 4872664 4803179 4892980 4900747 4945394 4938995 4979006 4994840 4997476
  * 5013885 5003322 4988891 5098443 5110268 6173522 4829857 5027748 6376940
  * 6358731 6178785 6284152 6231989 6497148 6486934 6233084 6504326 6635133
- * 6350801 6676425 6878475 6919132
+ * 6350801 6676425 6878475 6919132 6931676
  */
 
 import java.util.regex.*;
@@ -3515,7 +3515,7 @@
         report("NamedGroupCapture");
     }
 
-    // This is for bug 6969132
+    // This is for bug 6919132
     private static void nonBmpClassComplementTest() throws Exception {
         Pattern p = Pattern.compile("\\P{Lu}");
         Matcher m = p.matcher(new String(new int[] {0x1d400}, 0, 1));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/Krb5NameEquals.java	Tue Apr 20 16:51:13 2010 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2007-2010 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.
+ */
+
+/*
+ * @bug 4634392
+ * @summary JDK code doesn't respect contract for equals and hashCode
+ * @author Andrew Fan
+ */
+
+import org.ietf.jgss.*;
+
+public class Krb5NameEquals {
+
+    private static String NAME_STR1 = "service@host";
+    private static String NAME_STR2 = "service@host2";
+    private static final Oid MECH;
+
+    static {
+        Oid temp = null;
+        try {
+            temp = new Oid("1.2.840.113554.1.2.2"); // KRB5
+        } catch (Exception e) {
+            // should never happen
+        }
+        MECH = temp;
+    }
+
+    public static void main(String[] argv) throws Exception {
+        GSSManager mgr = GSSManager.getInstance();
+
+        boolean result = true;
+        // Create GSSName and check their equals(), hashCode() impl
+        GSSName name1 = mgr.createName(NAME_STR1,
+            GSSName.NT_HOSTBASED_SERVICE, MECH);
+        GSSName name2 = mgr.createName(NAME_STR2,
+            GSSName.NT_HOSTBASED_SERVICE, MECH);
+        GSSName name3 = mgr.createName(NAME_STR1,
+            GSSName.NT_HOSTBASED_SERVICE, MECH);
+
+        if (!name1.equals(name3) || !name1.equals(name3) ||
+            !name1.equals((Object) name1) ||
+            !name1.equals((Object) name3)) {
+            System.out.println("Error: should be the same name");
+            result = false;
+        } else if (name1.hashCode() != name3.hashCode()) {
+            System.out.println("Error: should have same hash");
+            result = false;
+        }
+
+        if (name1.equals(name2) || name1.equals((Object) name2)) {
+            System.out.println("Error: should be different names");
+            result = false;
+        }
+        if (result) {
+            System.out.println("Done");
+        } else System.exit(1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/runNameEquals.sh	Tue Apr 20 16:51:13 2010 -0700
@@ -0,0 +1,106 @@
+#
+# Copyright 2009-2010 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 6317711 6944847
+# @summary Ensure the GSSName has the correct impl which respects
+# the contract for equals and hashCode across different configurations.
+
+# set a few environment variables so that the shell-script can run stand-alone
+# in the source directory
+
+if [ "${TESTSRC}" = "" ] ; then
+   TESTSRC="."
+fi
+
+if [ "${TESTCLASSES}" = "" ] ; then
+   TESTCLASSES="."
+fi
+
+if [ "${TESTJAVA}" = "" ] ; then
+   echo "TESTJAVA not set.  Test cannot execute."
+   echo "FAILED!!!"
+   exit 1
+fi
+
+NATIVE=false
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  SunOS )
+    PATHSEP=":"
+    FILESEP="/"
+    NATIVE=true
+    ;;
+  Linux )
+    PATHSEP=":"
+    FILESEP="/"
+    NATIVE=true
+    ;;
+  CYGWIN* )
+    PATHSEP=";"
+    FILESEP="/"
+    ;;
+  Windows* )
+    PATHSEP=";"
+    FILESEP="\\"
+    ;;
+  * )
+    echo "Unrecognized system!"
+    exit 1;
+    ;;
+esac
+
+TEST=Krb5NameEquals
+
+${TESTJAVA}${FILESEP}bin${FILESEP}javac \
+    -d ${TESTCLASSES}${FILESEP} \
+    ${TESTSRC}${FILESEP}${TEST}.java
+
+EXIT_STATUS=0
+
+if [ "${NATIVE}" = "true" ] ; then
+    echo "Testing native provider"
+    ${TESTJAVA}${FILESEP}bin${FILESEP}java \
+        -classpath ${TESTCLASSES} \
+        -Dsun.security.jgss.native=true \
+        ${TEST}
+    if [ $? != 0 ] ; then
+        echo "Native provider fails"
+        EXIT_STATUS=1
+    fi
+fi
+
+echo "Testing java provider"
+${TESTJAVA}${FILESEP}bin${FILESEP}java \
+        -classpath ${TESTCLASSES} \
+        -Djava.security.krb5.realm=R \
+        -Djava.security.krb5.kdc=127.0.0.1 \
+        ${TEST}
+if [ $? != 0 ] ; then
+    echo "Java provider fails"
+    EXIT_STATUS=1
+fi
+
+exit ${EXIT_STATUS}
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/DNSIdentities.java	Tue Apr 20 16:51:13 2010 -0700
@@ -624,6 +624,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -652,9 +657,6 @@
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
         sslSocket.setNeedClientAuth(true);
-        if (sslSocket instanceof SSLSocketImpl) {
-            ((SSLSocketImpl)sslSocket).trySetHostnameVerification("HTTPS");
-        }
 
         PrintStream out =
                 new PrintStream(sslSocket.getOutputStream());
@@ -670,11 +672,14 @@
             out.print("Testing\r\n");
             out.flush();
         } finally {
-             // close the socket
-             Thread.sleep(2000);
-             System.out.println("Server closing socket");
-             sslSocket.close();
-             serverReady = false;
+            // close the socket
+            while (!closeReady) {
+                Thread.sleep(50);
+            }
+
+            System.out.println("Server closing socket");
+            sslSocket.close();
+            serverReady = false;
         }
 
     }
@@ -704,12 +709,17 @@
         URL url = new URL("https://localhost:" + serverPort+"/");
         System.out.println("url is "+url.toString());
 
-        http = (HttpsURLConnection)url.openConnection();
+        try {
+            http = (HttpsURLConnection)url.openConnection();
 
-        int respCode = http.getResponseCode();
-        System.out.println("respCode = "+respCode);
-
-        http.disconnect();
+            int respCode = http.getResponseCode();
+            System.out.println("respCode = "+respCode);
+        } finally {
+            if (http != null) {
+                http.disconnect();
+            }
+            closeReady = true;
+        }
     }
 
     /*
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/HttpsPost.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/HttpsPost.java	Tue Apr 20 16:51:13 2010 -0700
@@ -61,6 +61,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -98,25 +103,34 @@
         serverReady = true;
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
-        InputStream sslIS = sslSocket.getInputStream();
-        OutputStream sslOS = sslSocket.getOutputStream();
-        BufferedReader br = new BufferedReader(new InputStreamReader(sslIS));
-        PrintStream ps = new PrintStream(sslOS);
-        // process HTTP POST request from client
-        System.out.println("status line: "+br.readLine());
-        String msg = null;
-        while ((msg = br.readLine()) != null && msg.length() > 0);
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+            BufferedReader br =
+                        new BufferedReader(new InputStreamReader(sslIS));
+            PrintStream ps = new PrintStream(sslOS);
+
+            // process HTTP POST request from client
+            System.out.println("status line: "+br.readLine());
+            String msg = null;
+            while ((msg = br.readLine()) != null && msg.length() > 0);
 
-        msg = br.readLine();
-        if (msg.equals(postMsg)) {
-            ps.println("HTTP/1.1 200 OK\n\n");
-        } else {
-            ps.println("HTTP/1.1 500 Not OK\n\n");
+            msg = br.readLine();
+            if (msg.equals(postMsg)) {
+                ps.println("HTTP/1.1 200 OK\n\n");
+            } else {
+                ps.println("HTTP/1.1 500 Not OK\n\n");
+            }
+            ps.flush();
+
+            // close the socket
+            while (!closeReady) {
+                Thread.sleep(50);
+            }
+        } finally {
+            sslSocket.close();
+            sslServerSocket.close();
         }
-        ps.flush();
-        Thread.sleep(2000);
-        sslSocket.close();
-        sslServerSocket.close();
     }
 
     /*
@@ -144,12 +158,17 @@
 
         http.setRequestMethod("POST");
         PrintStream ps = new PrintStream(http.getOutputStream());
-        ps.println(postMsg);
-        ps.flush();
-        if (http.getResponseCode() != 200) {
-            throw new RuntimeException("test Failed");
+        try {
+            ps.println(postMsg);
+            ps.flush();
+            if (http.getResponseCode() != 200) {
+                throw new RuntimeException("test Failed");
+            }
+        } finally {
+            ps.close();
+            http.disconnect();
+            closeReady = true;
         }
-        ps.close();
     }
 
     static class NameVerifier implements HostnameVerifier {
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/IPAddressDNSIdentities.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/IPAddressDNSIdentities.java	Tue Apr 20 16:51:13 2010 -0700
@@ -624,6 +624,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -652,9 +657,6 @@
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
         sslSocket.setNeedClientAuth(true);
-        if (sslSocket instanceof SSLSocketImpl) {
-            ((SSLSocketImpl)sslSocket).trySetHostnameVerification("HTTPS");
-        }
 
         PrintStream out =
                 new PrintStream(sslSocket.getOutputStream());
@@ -670,11 +672,14 @@
             out.print("Testing\r\n");
             out.flush();
         } finally {
-             // close the socket
-             Thread.sleep(2000);
-             System.out.println("Server closing socket");
-             sslSocket.close();
-             serverReady = false;
+            // close the socket
+            while (!closeReady) {
+                Thread.sleep(50);
+            }
+
+            System.out.println("Server closing socket");
+            sslSocket.close();
+            serverReady = false;
         }
 
     }
@@ -716,7 +721,10 @@
             // no subject alternative names matching IP address 127.0.0.1 found
             // that's the expected exception, ignore it.
         } finally {
-            http.disconnect();
+            if (http != null) {
+                http.disconnect();
+            }
+            closeReady = true;
         }
     }
 
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/IPAddressIPIdentities.java	Tue Apr 20 16:51:13 2010 -0700
@@ -625,6 +625,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -653,9 +658,6 @@
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
         sslSocket.setNeedClientAuth(true);
-        if (sslSocket instanceof SSLSocketImpl) {
-            ((SSLSocketImpl)sslSocket).trySetHostnameVerification("HTTPS");
-        }
 
         PrintStream out =
                 new PrintStream(sslSocket.getOutputStream());
@@ -672,7 +674,10 @@
             out.flush();
         } finally {
              // close the socket
-             Thread.sleep(2000);
+             while (!closeReady) {
+                 Thread.sleep(50);
+             }
+
              System.out.println("Server closing socket");
              sslSocket.close();
              serverReady = false;
@@ -705,12 +710,17 @@
         URL url = new URL("https://127.0.0.1:" + serverPort+"/");
         System.out.println("url is "+url.toString());
 
-        http = (HttpsURLConnection)url.openConnection();
+        try {
+            http = (HttpsURLConnection)url.openConnection();
 
-        int respCode = http.getResponseCode();
-        System.out.println("respCode = "+respCode);
-
-        http.disconnect();
+            int respCode = http.getResponseCode();
+            System.out.println("respCode = "+respCode);
+        } finally {
+            if (http != null) {
+                http.disconnect();
+            }
+            closeReady = true;
+        }
     }
 
     /*
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/IPIdentities.java	Tue Apr 20 16:51:13 2010 -0700
@@ -625,6 +625,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -653,9 +658,6 @@
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
         sslSocket.setNeedClientAuth(true);
-        if (sslSocket instanceof SSLSocketImpl) {
-            ((SSLSocketImpl)sslSocket).trySetHostnameVerification("HTTPS");
-        }
 
         PrintStream out =
                 new PrintStream(sslSocket.getOutputStream());
@@ -672,7 +674,10 @@
             out.flush();
         } finally {
              // close the socket
-             Thread.sleep(2000);
+             while (!closeReady) {
+                 Thread.sleep(50);
+             }
+
              System.out.println("Server closing socket");
              sslSocket.close();
              serverReady = false;
@@ -705,12 +710,17 @@
         URL url = new URL("https://localhost:" + serverPort+"/");
         System.out.println("url is "+url.toString());
 
-        http = (HttpsURLConnection)url.openConnection();
+        try {
+            http = (HttpsURLConnection)url.openConnection();
 
-        int respCode = http.getResponseCode();
-        System.out.println("respCode = "+respCode);
-
-        http.disconnect();
+            int respCode = http.getResponseCode();
+            System.out.println("respCode = "+respCode);
+        } finally {
+            if (http != null) {
+                http.disconnect();
+            }
+            closeReady = true;
+        }
     }
 
     /*
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/Identities.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/Identities.java	Tue Apr 20 16:51:13 2010 -0700
@@ -624,6 +624,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -652,9 +657,6 @@
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
         sslSocket.setNeedClientAuth(true);
-        if (sslSocket instanceof SSLSocketImpl) {
-            ((SSLSocketImpl)sslSocket).trySetHostnameVerification("HTTPS");
-        }
 
         PrintStream out =
                 new PrintStream(sslSocket.getOutputStream());
@@ -671,7 +673,10 @@
             out.flush();
         } finally {
              // close the socket
-             Thread.sleep(2000);
+             while (!closeReady) {
+                 Thread.sleep(50);
+             }
+
              System.out.println("Server closing socket");
              sslSocket.close();
              serverReady = false;
@@ -704,12 +709,17 @@
         URL url = new URL("https://localhost:" + serverPort+"/");
         System.out.println("url is "+url.toString());
 
-        http = (HttpsURLConnection)url.openConnection();
+        try {
+            http = (HttpsURLConnection)url.openConnection();
 
-        int respCode = http.getResponseCode();
-        System.out.println("respCode = "+respCode);
-
-        http.disconnect();
+            int respCode = http.getResponseCode();
+            System.out.println("respCode = "+respCode);
+        } finally {
+            if (http != null) {
+                http.disconnect();
+            }
+            closeReady = true;
+        }
     }
 
     /*
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/Redirect.java	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/Redirect.java	Tue Apr 20 16:51:13 2010 -0700
@@ -61,6 +61,11 @@
     volatile static boolean serverReady = false;
 
     /*
+     * Is the connection ready to close?
+     */
+    volatile static boolean closeReady = false;
+
+    /*
      * Turn on SSL debugging?
      */
     static boolean debug = false;
@@ -98,24 +103,33 @@
         serverReady = true;
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
-        InputStream sslIS = sslSocket.getInputStream();
-        OutputStream sslOS = sslSocket.getOutputStream();
-        BufferedReader br = new BufferedReader(new InputStreamReader(sslIS));
-        PrintStream ps = new PrintStream(sslOS);
-        // process HTTP POST request from client
-        System.out.println("status line: "+br.readLine());
+        try {
+            InputStream sslIS = sslSocket.getInputStream();
+            OutputStream sslOS = sslSocket.getOutputStream();
+            BufferedReader br =
+                        new BufferedReader(new InputStreamReader(sslIS));
+            PrintStream ps = new PrintStream(sslOS);
+
+            // process HTTP POST request from client
+            System.out.println("status line: "+br.readLine());
 
-        ps.println("HTTP/1.1 307 Redirect");
-        ps.println("Location: https://localhost:"+serverPort+"/index.html\n\n");
-        ps.flush();
-        sslSocket = (SSLSocket) sslServerSocket.accept();
-        sslOS = sslSocket.getOutputStream();
-        ps = new PrintStream(sslOS);
-        ps.println("HTTP/1.1 200 Redirect succeeded\n\n");
-        ps.flush();
-        Thread.sleep(2000);
-        sslSocket.close();
-        sslServerSocket.close();
+            ps.println("HTTP/1.1 307 Redirect");
+            ps.println("Location: https://localhost:" + serverPort +
+                                                            "/index.html\n\n");
+            ps.flush();
+            sslSocket = (SSLSocket) sslServerSocket.accept();
+            sslOS = sslSocket.getOutputStream();
+            ps = new PrintStream(sslOS);
+            ps.println("HTTP/1.1 200 Redirect succeeded\n\n");
+            ps.flush();
+        } finally {
+            // close the socket
+            while (!closeReady) {
+                Thread.sleep(50);
+            }
+            sslSocket.close();
+            sslServerSocket.close();
+        }
     }
 
     /*
@@ -139,10 +153,14 @@
         HttpsURLConnection.setDefaultHostnameVerifier(
                                       new NameVerifier());
         HttpsURLConnection http = (HttpsURLConnection)url.openConnection();
-
-        System.out.println("response header: "+http.getHeaderField(0));
-        if (http.getResponseCode() != 200) {
-            throw new RuntimeException("test Failed");
+        try {
+            System.out.println("response header: "+http.getHeaderField(0));
+            if (http.getResponseCode() != 200) {
+                throw new RuntimeException("test Failed");
+            }
+        } finally {
+            http.disconnect();
+            closeReady = true;
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java	Tue Apr 20 16:51:13 2010 -0700
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2003-2010 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 com.sun.net.httpserver.*;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import sun.security.pkcs.ContentInfo;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+import sun.security.util.ObjectIdentifier;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.X500Name;
+
+public class TimestampCheck {
+    static final String TSKS = "tsks";
+    static final String JAR = "old.jar";
+
+    static class Handler implements HttpHandler {
+        public void handle(HttpExchange t) throws IOException {
+            int len = 0;
+            for (String h: t.getRequestHeaders().keySet()) {
+                if (h.equalsIgnoreCase("Content-length")) {
+                    len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
+                }
+            }
+            byte[] input = new byte[len];
+            t.getRequestBody().read(input);
+
+            try {
+                int path = 0;
+                if (t.getRequestURI().getPath().length() > 1) {
+                    path = Integer.parseInt(
+                            t.getRequestURI().getPath().substring(1));
+                }
+                byte[] output = sign(input, path);
+                Headers out = t.getResponseHeaders();
+                out.set("Content-Type", "application/timestamp-reply");
+
+                t.sendResponseHeaders(200, output.length);
+                OutputStream os = t.getResponseBody();
+                os.write(output);
+            } catch (Exception e) {
+                e.printStackTrace();
+                t.sendResponseHeaders(500, 0);
+            }
+            t.close();
+        }
+
+        /**
+         * @param input The data to sign
+         * @param path different cases to simulate, impl on URL path
+         * 0: normal
+         * 1: Missing nonce
+         * 2: Different nonce
+         * 3: Bad digets octets in messageImprint
+         * 4: Different algorithmId in messageImprint
+         * 5: whole chain in cert set
+         * 6: extension is missing
+         * 7: extension is non-critical
+         * 8: extension does not have timestamping
+         * @returns the signed
+         */
+        byte[] sign(byte[] input, int path) throws Exception {
+            // Read TSRequest
+            DerValue value = new DerValue(input);
+            System.err.println("\nIncoming Request\n===================");
+            System.err.println("Version: " + value.data.getInteger());
+            DerValue messageImprint = value.data.getDerValue();
+            AlgorithmId aid = AlgorithmId.parse(
+                    messageImprint.data.getDerValue());
+            System.err.println("AlgorithmId: " + aid);
+
+            BigInteger nonce = null;
+            while (value.data.available() > 0) {
+                DerValue v = value.data.getDerValue();
+                if (v.tag == DerValue.tag_Integer) {
+                    nonce = v.getBigInteger();
+                    System.err.println("nonce: " + nonce);
+                } else if (v.tag == DerValue.tag_Boolean) {
+                    System.err.println("certReq: " + v.getBoolean());
+                }
+            }
+
+            // Write TSResponse
+            System.err.println("\nResponse\n===================");
+            KeyStore ks = KeyStore.getInstance("JKS");
+            ks.load(new FileInputStream(TSKS), "changeit".toCharArray());
+
+            String alias = "ts";
+            if (path == 6) alias = "tsbad1";
+            if (path == 7) alias = "tsbad2";
+            if (path == 8) alias = "tsbad3";
+
+            DerOutputStream statusInfo = new DerOutputStream();
+            statusInfo.putInteger(0);
+
+            DerOutputStream token = new DerOutputStream();
+            AlgorithmId[] algorithms = {aid};
+            Certificate[] chain = ks.getCertificateChain(alias);
+            X509Certificate[] signerCertificateChain = null;
+            X509Certificate signer = (X509Certificate)chain[0];
+            if (path == 5) {   // Only case 5 uses full chain
+                signerCertificateChain = new X509Certificate[chain.length];
+                for (int i=0; i<chain.length; i++) {
+                    signerCertificateChain[i] = (X509Certificate)chain[i];
+                }
+            } else if (path == 9) {
+                signerCertificateChain = new X509Certificate[0];
+            } else {
+                signerCertificateChain = new X509Certificate[1];
+                signerCertificateChain[0] = (X509Certificate)chain[0];
+            }
+
+            DerOutputStream tst = new DerOutputStream();
+
+            tst.putInteger(1);
+            tst.putOID(new ObjectIdentifier("1.2.3.4"));    // policy
+
+            if (path != 3 && path != 4) {
+                tst.putDerValue(messageImprint);
+            } else {
+                byte[] data = messageImprint.toByteArray();
+                if (path == 4) {
+                    data[6] = (byte)0x01;
+                } else {
+                    data[data.length-1] = (byte)0x01;
+                    data[data.length-2] = (byte)0x02;
+                    data[data.length-3] = (byte)0x03;
+                }
+                tst.write(data);
+            }
+
+            tst.putInteger(1);
+
+            Calendar cal = Calendar.getInstance();
+            tst.putGeneralizedTime(cal.getTime());
+
+            if (path == 2) {
+                tst.putInteger(1234);
+            } else if (path == 1) {
+                // do nothing
+            } else {
+                tst.putInteger(nonce);
+            }
+
+            DerOutputStream tstInfo = new DerOutputStream();
+            tstInfo.write(DerValue.tag_Sequence, tst);
+
+            DerOutputStream tstInfo2 = new DerOutputStream();
+            tstInfo2.putOctetString(tstInfo.toByteArray());
+
+            Signature sig = Signature.getInstance("SHA1withDSA");
+            sig.initSign((PrivateKey)(ks.getKey(
+                    alias, "changeit".toCharArray())));
+            sig.update(tstInfo.toByteArray());
+
+            ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(
+                    "1.2.840.113549.1.9.16.1.4"),
+                    new DerValue(tstInfo2.toByteArray()));
+
+            System.err.println("Signing...");
+            System.err.println(new X500Name(signer
+                    .getIssuerX500Principal().getName()));
+            System.err.println(signer.getSerialNumber());
+
+            SignerInfo signerInfo = new SignerInfo(
+                    new X500Name(signer.getIssuerX500Principal().getName()),
+                    signer.getSerialNumber(),
+                    aid, AlgorithmId.get("DSA"), sig.sign());
+
+            SignerInfo[] signerInfos = {signerInfo};
+            PKCS7 p7 =
+                    new PKCS7(algorithms, contentInfo, signerCertificateChain,
+                    signerInfos);
+            ByteArrayOutputStream p7out = new ByteArrayOutputStream();
+            p7.encodeSignedData(p7out);
+
+            DerOutputStream response = new DerOutputStream();
+            response.write(DerValue.tag_Sequence, statusInfo);
+            response.putDerValue(new DerValue(p7out.toByteArray()));
+
+            DerOutputStream out = new DerOutputStream();
+            out.write(DerValue.tag_Sequence, response);
+
+            return out.toByteArray();
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        Handler h = new Handler();
+        HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
+        int port = server.getAddress().getPort();
+        HttpContext ctx = server.createContext("/", h);
+        server.start();
+
+        String cmd = null;
+        // Use -J-Djava.security.egd=file:/dev/./urandom to speed up
+        // nonce generation in timestamping request. Not avaibale on
+        // Windows and defaults to thread seed generator, not too bad.
+        if (System.getProperty("java.home").endsWith("jre")) {
+            cmd = System.getProperty("java.home") + "/../bin/jarsigner" +
+                " -J-Djava.security.egd=file:/dev/./urandom" +
+                " -debug -keystore " + TSKS + " -storepass changeit" +
+                " -tsa http://localhost:" + port + "/%d" +
+                " -signedjar new.jar " + JAR + " old";
+        } else {
+            cmd = System.getProperty("java.home") + "/bin/jarsigner" +
+                " -J-Djava.security.egd=file:/dev/./urandom" +
+                " -debug -keystore " + TSKS + " -storepass changeit" +
+                " -tsa http://localhost:" + port + "/%d" +
+                " -signedjar new.jar " + JAR + " old";
+        }
+
+        try {
+            if (args.length == 0) {         // Run this test
+                jarsigner(cmd, 0, true);    // Success, normal call
+                jarsigner(cmd, 1, false);   // These 4 should fail
+                jarsigner(cmd, 2, false);
+                jarsigner(cmd, 3, false);
+                jarsigner(cmd, 4, false);
+                jarsigner(cmd, 5, true);    // Success, 6543440 solved.
+                jarsigner(cmd, 6, false);   // tsbad1
+                jarsigner(cmd, 7, false);   // tsbad2
+                jarsigner(cmd, 8, false);   // tsbad3
+                jarsigner(cmd, 9, false);   // no cert in timestamp
+            } else {                        // Run as a standalone server
+                System.err.println("Press Enter to quit server");
+                System.in.read();
+            }
+        } finally {
+            server.stop(0);
+            new File("x.jar").delete();
+        }
+    }
+
+    /**
+     * @param cmd the command line (with a hole to plug in)
+     * @param path the path in the URL, i.e, http://localhost/path
+     * @param expected if this command should succeed
+     */
+    static void jarsigner(String cmd, int path, boolean expected)
+            throws Exception {
+        System.err.println("Test " + path);
+        Process p = Runtime.getRuntime().exec(String.format(cmd, path));
+        BufferedReader reader = new BufferedReader(
+                new InputStreamReader(p.getErrorStream()));
+        while (true) {
+            String s = reader.readLine();
+            if (s == null) break;
+            System.err.println(s);
+        }
+        int result = p.waitFor();
+        if (expected && result != 0 || !expected && result == 0) {
+            throw new Exception("Failed");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/ts.sh	Tue Apr 20 16:51:13 2010 -0700
@@ -0,0 +1,91 @@
+#
+# Copyright 2007-2010 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 6543842 6543440 6939248
+# @summary checking response of timestamp
+#
+# @run shell/timeout=600 ts.sh
+
+# Run for a long time because jarsigner with timestamp needs to create a
+# 64-bit random number and it might be extremely slow on a machine with
+# not enough entropy pool
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  Windows_* )
+    FS="\\"
+    ;;
+  * )
+    FS="/"
+    ;;
+esac
+
+if [ "${TESTSRC}" = "" ] ; then
+  TESTSRC="."
+fi
+if [ "${TESTJAVA}" = "" ] ; then
+  JAVAC_CMD=`which javac`
+  TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+JAR="${TESTJAVA}${FS}bin${FS}jar"
+JAVA="${TESTJAVA}${FS}bin${FS}java"
+JAVAC="${TESTJAVA}${FS}bin${FS}javac"
+KT="${TESTJAVA}${FS}bin${FS}keytool -keystore tsks -storepass changeit -keypass changeit"
+
+rm tsks
+echo Nothing > A
+rm old.jar
+$JAR cvf old.jar A
+
+# ca is CA
+# old is signer for code
+# ts is signer for timestamp
+# tsbad1 has no extendedKeyUsage
+# tsbad2's extendedKeyUsage is non-critical
+# tsbad3's extendedKeyUsage has no timestamping
+
+$KT -alias ca -genkeypair -ext bc -dname CN=CA
+$KT -alias old -genkeypair -dname CN=old
+$KT -alias ts -genkeypair -dname CN=ts
+$KT -alias tsbad1 -genkeypair -dname CN=tsbad1
+$KT -alias tsbad2 -genkeypair -dname CN=tsbad2
+$KT -alias tsbad3 -genkeypair -dname CN=tsbad3
+$KT -alias ts -certreq | \
+        $KT -alias ca -gencert -ext eku:critical=ts | \
+        $KT -alias ts -importcert
+$KT -alias tsbad1 -certreq | \
+        $KT -alias ca -gencert | \
+        $KT -alias tsbad1 -importcert
+$KT -alias tsbad2 -certreq | \
+        $KT -alias ca -gencert -ext eku=ts | \
+        $KT -alias tsbad2 -importcert
+$KT -alias tsbad3 -certreq | \
+        $KT -alias ca -gencert -ext eku:critical=cs | \
+        $KT -alias tsbad3 -importcert
+
+$JAVAC -d . ${TESTSRC}/TimestampCheck.java
+$JAVA TimestampCheck
+
--- a/jdk/test/sun/security/tools/keytool/selfissued.sh	Tue Apr 20 16:50:10 2010 -0700
+++ b/jdk/test/sun/security/tools/keytool/selfissued.sh	Tue Apr 20 16:51:13 2010 -0700
@@ -1,5 +1,5 @@
 #
-# Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+# Copyright 2009-2010 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
@@ -22,8 +22,8 @@
 #
 
 # @test
-# @bug 6825352
-# @summary support self-issued certificate in keytool
+# @bug 6825352 6937978
+# @summary support self-issued certificate in keytool and let -gencert generate the chain
 #
 # @run shell selfissued.sh
 #
@@ -50,20 +50,22 @@
 rm $KS
 
 $KT -alias ca -dname CN=CA -genkeypair
-$KT -alias me -dname CN=CA -genkeypair
+$KT -alias ca1 -dname CN=CA -genkeypair
+$KT -alias ca2 -dname CN=CA -genkeypair
 $KT -alias e1 -dname CN=E1 -genkeypair
-$KT -alias e2 -dname CN=E2 -genkeypair
-
-# me signed by ca, self-issued
-$KT -alias me -certreq | $KT -alias ca -gencert | $KT -alias me -importcert
 
-# Import e1 signed by me, should add me and ca
-$KT -alias e1 -certreq | $KT -alias me -gencert | $KT -alias e1 -importcert
-$KT -alias e1 -list -v | grep '\[3\]' || { echo Bad E1; exit 1; }
+# ca signs ca1, ca1 signs ca2, all self-issued
+$KT -alias ca1 -certreq | $KT -alias ca -gencert -ext san=dns:ca1 \
+        | $KT -alias ca1 -importcert
+$KT -alias ca2 -certreq | $KT -alias ca1 -gencert -ext san=dns:ca2 \
+        | $KT -alias ca2 -importcert
 
-# Import (e2 signed by me,ca,me), should reorder to (e2,me,ca)
-( $KT -alias e2 -certreq | $KT -alias me -gencert; $KT -exportcert -alias ca; $KT -exportcert -alias me ) | $KT -alias e2 -importcert
-$KT -alias e2 -list -v | grep '\[3\]' || { echo Bad E2; exit 1; }
+# Import e1 signed by ca2, should add ca2 and ca1, at least 3 certs in the chain
+$KT -alias e1 -certreq | $KT -alias ca2 -gencert > e1.cert
+$KT -alias ca1 -delete
+$KT -alias ca2 -delete
+cat e1.cert | $KT -alias e1 -importcert
+$KT -alias e1 -list -v | grep '\[3\]' || { echo Bad E1; exit 1; }
 
 echo Good