changeset 11387:8bef978e2374 jdk8u76-b03

Merge
author asaha
date Fri, 15 Jan 2016 16:23:58 -0800
parents 3fc3108f886f 8fe85977d5a6
children c7d016751fa5
files src/share/classes/sun/security/ssl/ServerHandshaker.java src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java src/share/classes/sun/security/x509/X509CertImpl.java
diffstat 18 files changed, 272 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/make/CompileDemos.gmk	Tue Jan 05 08:49:57 2016 -0800
+++ b/make/CompileDemos.gmk	Fri Jan 15 16:23:58 2016 -0800
@@ -229,7 +229,7 @@
     BUILD_DEMO_JVMTI_$1_LANG := $4
   endif
   ifeq (C++, $4)
-    $1_EXTRA_CXX := $(LDFLAGS_CXX_JDK) $(LIBCXX)
+    $1_EXTRA_CXX := $$(LDFLAGS_CXX_JDK) $(LIBCXX)
   endif
 
   $1_CXXFLAGS := $(CXXFLAGS_JDKLIB) -I$(JDK_TOPDIR)/src/share/demo/jvmti/$1 \
@@ -251,8 +251,8 @@
       LANG := $$(BUILD_DEMO_JVMTI_$1_LANG), \
       OPTIMIZATION := LOW, \
       CXXFLAGS := $$($1_CXXFLAGS), \
-      LDFLAGS := $(filter-out -incremental:no -opt:ref, $(LDFLAGS_JDKLIB)), \
-      LDFLAGS_macosx := $(call SET_EXECUTABLE_ORIGIN), \
+      LDFLAGS := $(filter-out -incremental:no -opt:ref, $$(LDFLAGS_JDKLIB)), \
+      LDFLAGS_macosx := $$(call SET_EXECUTABLE_ORIGIN), \
       LDFLAGS_SUFFIX := $$($1_EXTRA_CXX), \
       LDFLAGS_SUFFIX_posix := $5, \
       LDFLAGS_SUFFIX_windows := $6, \
--- a/src/share/classes/java/util/AbstractMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/AbstractMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -304,9 +304,28 @@
      * Each of these fields are initialized to contain an instance of the
      * appropriate view the first time this view is requested.  The views are
      * stateless, so there's no reason to create more than one of each.
+     *
+     * <p>Since there is no synchronization performed while accessing these fields,
+     * it is expected that java.util.Map view classes using these fields have
+     * no non-final fields (or any fields at all except for outer-this). Adhering
+     * to this rule would make the races on these fields benign.
+     *
+     * <p>It is also imperative that implementations read the field only once,
+     * as in:
+     *
+     * <pre> {@code
+     * public Set<K> keySet() {
+     *   Set<K> ks = keySet;  // single racy read
+     *   if (ks == null) {
+     *     ks = new KeySet();
+     *     keySet = ks;
+     *   }
+     *   return ks;
+     * }
+     *}</pre>
      */
-    transient volatile Set<K>        keySet;
-    transient volatile Collection<V> values;
+    transient Set<K>        keySet;
+    transient Collection<V> values;
 
     /**
      * {@inheritDoc}
@@ -325,8 +344,9 @@
      * method will not all return the same set.
      */
     public Set<K> keySet() {
-        if (keySet == null) {
-            keySet = new AbstractSet<K>() {
+        Set<K> ks = keySet;
+        if (ks == null) {
+            ks = new AbstractSet<K>() {
                 public Iterator<K> iterator() {
                     return new Iterator<K>() {
                         private Iterator<Entry<K,V>> i = entrySet().iterator();
@@ -361,8 +381,9 @@
                     return AbstractMap.this.containsKey(k);
                 }
             };
+            keySet = ks;
         }
-        return keySet;
+        return ks;
     }
 
     /**
@@ -382,8 +403,9 @@
      * method will not all return the same collection.
      */
     public Collection<V> values() {
-        if (values == null) {
-            values = new AbstractCollection<V>() {
+        Collection<V> vals = values;
+        if (vals == null) {
+            vals = new AbstractCollection<V>() {
                 public Iterator<V> iterator() {
                     return new Iterator<V>() {
                         private Iterator<Entry<K,V>> i = entrySet().iterator();
@@ -418,8 +440,9 @@
                     return AbstractMap.this.containsValue(v);
                 }
             };
+            values = vals;
         }
-        return values;
+        return vals;
     }
 
     public abstract Set<Entry<K,V>> entrySet();
--- a/src/share/classes/java/util/EnumMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/EnumMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -380,10 +380,11 @@
      */
     public Set<K> keySet() {
         Set<K> ks = keySet;
-        if (ks != null)
-            return ks;
-        else
-            return keySet = new KeySet();
+        if (ks == null) {
+            ks = new KeySet();
+            keySet = ks;
+        }
+        return ks;
     }
 
     private class KeySet extends AbstractSet<K> {
@@ -418,10 +419,11 @@
      */
     public Collection<V> values() {
         Collection<V> vs = values;
-        if (vs != null)
-            return vs;
-        else
-            return values = new Values();
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
     }
 
     private class Values extends AbstractCollection<V> {
--- a/src/share/classes/java/util/HashMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/HashMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -902,8 +902,12 @@
      * @return a set view of the keys contained in this map
      */
     public Set<K> keySet() {
-        Set<K> ks;
-        return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
+        Set<K> ks = keySet;
+        if (ks == null) {
+            ks = new KeySet();
+            keySet = ks;
+        }
+        return ks;
     }
 
     final class KeySet extends AbstractSet<K> {
@@ -949,8 +953,12 @@
      * @return a view of the values contained in this map
      */
     public Collection<V> values() {
-        Collection<V> vs;
-        return (vs = values) == null ? (values = new Values()) : vs;
+        Collection<V> vs = values;
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
     }
 
     final class Values extends AbstractCollection<V> {
--- a/src/share/classes/java/util/IdentityHashMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/IdentityHashMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -964,10 +964,11 @@
      */
     public Set<K> keySet() {
         Set<K> ks = keySet;
-        if (ks != null)
-            return ks;
-        else
-            return keySet = new KeySet();
+        if (ks == null) {
+            ks = new KeySet();
+            keySet = ks;
+        }
+        return ks;
     }
 
     private class KeySet extends AbstractSet<K> {
@@ -1069,10 +1070,11 @@
      */
     public Collection<V> values() {
         Collection<V> vs = values;
-        if (vs != null)
-            return vs;
-        else
-            return values = new Values();
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
     }
 
     private class Values extends AbstractCollection<V> {
--- a/src/share/classes/java/util/LinkedHashMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/LinkedHashMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -528,8 +528,12 @@
      * @return a set view of the keys contained in this map
      */
     public Set<K> keySet() {
-        Set<K> ks;
-        return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
+        Set<K> ks = keySet;
+        if (ks == null) {
+            ks = new LinkedKeySet();
+            keySet = ks;
+        }
+        return ks;
     }
 
     final class LinkedKeySet extends AbstractSet<K> {
@@ -577,8 +581,12 @@
      * @return a view of the values contained in this map
      */
     public Collection<V> values() {
-        Collection<V> vs;
-        return (vs = values) == null ? (values = new LinkedValues()) : vs;
+        Collection<V> vs = values;
+        if (vs == null) {
+            vs = new LinkedValues();
+            values = vs;
+        }
+        return vs;
     }
 
     final class LinkedValues extends AbstractCollection<V> {
--- a/src/share/classes/java/util/TreeMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/TreeMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -855,7 +855,11 @@
      */
     public Collection<V> values() {
         Collection<V> vs = values;
-        return (vs != null) ? vs : (values = new Values());
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
     }
 
     /**
--- a/src/share/classes/java/util/WeakHashMap.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/java/util/WeakHashMap.java	Fri Jan 15 16:23:58 2016 -0800
@@ -865,7 +865,11 @@
      */
     public Set<K> keySet() {
         Set<K> ks = keySet;
-        return (ks != null ? ks : (keySet = new KeySet()));
+        if (ks == null) {
+            ks = new KeySet();
+            keySet = ks;
+        }
+        return ks;
     }
 
     private class KeySet extends AbstractSet<K> {
@@ -914,7 +918,11 @@
      */
     public Collection<V> values() {
         Collection<V> vs = values;
-        return (vs != null) ? vs : (values = new Values());
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
     }
 
     private class Values extends AbstractCollection<V> {
--- a/src/share/classes/sun/security/pkcs10/PKCS10.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/pkcs10/PKCS10.java	Fri Jan 15 16:23:58 2016 -0800
@@ -290,8 +290,9 @@
             throw new SignatureException("Cert request was not signed");
 
 
+        byte[] CRLF = new byte[] {'\r', '\n'};
         out.println("-----BEGIN NEW CERTIFICATE REQUEST-----");
-        out.println(Base64.getMimeEncoder().encodeToString(encoded));
+        out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded));
         out.println("-----END NEW CERTIFICATE REQUEST-----");
     }
 
--- a/src/share/classes/sun/security/provider/X509Factory.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/provider/X509Factory.java	Fri Jan 15 16:23:58 2016 -0800
@@ -28,6 +28,8 @@
 import java.io.*;
 import java.util.*;
 import java.security.cert.*;
+
+import sun.security.util.Pem;
 import sun.security.x509.X509CertImpl;
 import sun.security.x509.X509CRLImpl;
 import sun.security.pkcs.PKCS7;
@@ -633,7 +635,7 @@
 
             checkHeaderFooter(header.toString(), footer.toString());
 
-            return Base64.getMimeDecoder().decode(new String(data, 0, pos));
+            return Pem.decode(new String(data, 0, pos));
         }
     }
 
--- a/src/share/classes/sun/security/ssl/HandshakeHash.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/ssl/HandshakeHash.java	Fri Jan 15 16:23:58 2016 -0800
@@ -246,7 +246,7 @@
         try {
             return cloneDigest(finMD).digest();
         } catch (Exception e) {
-            throw new Error("BAD");
+            throw new Error("Error during hash calculation", e);
         }
     }
 }
--- a/src/share/classes/sun/security/ssl/ServerHandshaker.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/ssl/ServerHandshaker.java	Fri Jan 15 16:23:58 2016 -0800
@@ -1259,8 +1259,8 @@
                 }
             }
 
-            // need EC cert signed using EC
-            if (setupPrivateKeyAndChain("EC_EC") == false) {
+            // need EC cert
+            if (setupPrivateKeyAndChain("EC") == false) {
                 return false;
             }
             if (setupEphemeralECDHKeys() == false) {
@@ -1268,15 +1268,15 @@
             }
             break;
         case K_ECDH_RSA:
-            // need EC cert signed using RSA
-            if (setupPrivateKeyAndChain("EC_RSA") == false) {
+            // need EC cert
+            if (setupPrivateKeyAndChain("EC") == false) {
                 return false;
             }
             setupStaticECDHKeys();
             break;
         case K_ECDH_ECDSA:
-            // need EC cert signed using EC
-            if (setupPrivateKeyAndChain("EC_EC") == false) {
+            // need EC cert
+            if (setupPrivateKeyAndChain("EC") == false) {
                 return false;
             }
             setupStaticECDHKeys();
--- a/src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java	Fri Jan 15 16:23:58 2016 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, Oracle and/or its affiliates. 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
@@ -152,13 +152,11 @@
             getSupportedAlgorithms(AlgorithmConstraints constraints) {
 
         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
-        synchronized (priorityMap) {
-            for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
-                if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
-                        constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                                sigAlg.algorithm, null)) {
-                    supported.add(sigAlg);
-                }
+        for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
+            if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
+                    constraints.permits(SIGNATURE_PRIMITIVE_SET,
+                            sigAlg.algorithm, null)) {
+                supported.add(sigAlg);
             }
         }
 
--- a/src/share/classes/sun/security/tools/keytool/Main.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/tools/keytool/Main.java	Fri Jan 15 16:23:58 2016 -0800
@@ -79,6 +79,7 @@
 import sun.security.tools.KeyStoreUtil;
 import sun.security.tools.PathList;
 import sun.security.util.DerValue;
+import sun.security.util.Pem;
 import sun.security.x509.*;
 
 import static java.security.KeyStore.*;
@@ -99,6 +100,8 @@
  */
 public final class Main {
 
+    private static final byte[] CRLF = new byte[] {'\r', '\n'};
+
     private boolean debug = false;
     private Command command = null;
     private String sigAlgName = null;
@@ -1205,7 +1208,7 @@
                 sb.append(s);
             }
         }
-        byte[] rawReq = Base64.getMimeDecoder().decode(new String(sb));
+        byte[] rawReq = Pem.decode(new String(sb));
         PKCS10 req = new PKCS10(rawReq);
 
         info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
@@ -1282,7 +1285,7 @@
         crl.sign(privateKey, sigAlgName);
         if (rfc) {
             out.println("-----BEGIN X509 CRL-----");
-            out.println(Base64.getMimeEncoder().encodeToString(crl.getEncodedInternal()));
+            out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal()));
             out.println("-----END X509 CRL-----");
         } else {
             out.write(crl.getEncodedInternal());
@@ -2251,7 +2254,7 @@
         if (rfc) {
             X509CRL xcrl = (X509CRL)crl;
             out.println("-----BEGIN X509 CRL-----");
-            out.println(Base64.getMimeEncoder().encodeToString(xcrl.getEncoded()));
+            out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
             out.println("-----END X509 CRL-----");
         } else {
             out.println(crl.toString());
@@ -2278,7 +2281,7 @@
                 sb.append(s);
             }
         }
-        PKCS10 req = new PKCS10(Base64.getMimeDecoder().decode(new String(sb)));
+        PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
 
         PublicKey pkey = req.getSubjectPublicKeyInfo();
         out.printf(rb.getString("PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key."),
@@ -3059,7 +3062,7 @@
     {
         if (rfc) {
             out.println(X509Factory.BEGIN_CERT);
-            out.println(Base64.getMimeEncoder().encodeToString(cert.getEncoded()));
+            out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));
             out.println(X509Factory.END_CERT);
         } else {
             out.write(cert.getEncoded()); // binary
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/util/Pem.java	Fri Jan 15 16:23:58 2016 -0800
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+import java.io.IOException;
+import java.util.Base64;
+
+/**
+ * The Length interface defines the length of an object
+ */
+public class Pem {
+
+    /**
+     * Decodes a PEM-encoded block.
+     *
+     * @param input the input string, according to RFC 1421, can only contain
+     *              characters in the base-64 alphabet and whitespaces.
+     * @return the decoded bytes
+     * @throws java.io.IOException if input is invalid
+     */
+    public static byte[] decode(String input) throws IOException {
+        byte[] src = input.replaceAll("\\s+", "").getBytes();
+        try {
+            return Base64.getDecoder().decode(src);
+        } catch (IllegalArgumentException e) {
+            throw new IOException(e);
+        }
+    }
+}
--- a/src/share/classes/sun/security/x509/X509CertImpl.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/src/share/classes/sun/security/x509/X509CertImpl.java	Fri Jan 15 16:23:58 2016 -0800
@@ -271,7 +271,7 @@
                         der = new DerValue(decstream.toByteArray());
                         break;
                     } else {
-                        decstream.write(Base64.getMimeDecoder().decode(line));
+                        decstream.write(Pem.decode(line));
                     }
                 }
             } catch (IOException ioe2) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/provider/X509Factory/BadPem.java	Fri Jan 15 16:23:58 2016 -0800
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8074935
+ * @summary jdk8 keytool doesn't validate pem files for RFC 1421 correctness, as jdk7 did
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.util.Arrays;
+import java.util.Base64;
+
+import sun.security.provider.X509Factory;
+import java.security.cert.CertificateFactory;
+import java.io.ByteArrayInputStream;
+
+public class BadPem {
+
+    public static void main(String[] args) throws Exception {
+        String ks = System.getProperty("test.src", ".")
+                + "/../../ssl/etc/keystore";
+        String pass = "passphrase";
+        String alias = "dummy";
+
+        KeyStore keyStore = KeyStore.getInstance("JKS");
+        keyStore.load(new FileInputStream(ks), pass.toCharArray());
+        byte[] cert = keyStore.getCertificate(alias).getEncoded();
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        PrintStream pout = new PrintStream(bout);
+        byte[] CRLF = new byte[] {'\r', '\n'};
+        pout.println(X509Factory.BEGIN_CERT);
+        for (int i=0; i<cert.length; i += 48) {
+            int blockLen = (cert.length > i + 48) ? 48 : (cert.length - i);
+            pout.println("!" + Base64.getEncoder()
+                    .encodeToString(Arrays.copyOfRange(cert, i, i + blockLen)));
+        }
+        pout.println(X509Factory.END_CERT);
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        try {
+            cf.generateCertificate(new ByteArrayInputStream(bout.toByteArray()));
+            throw new Exception("Should fail");
+        } catch (CertificateException e) {
+            // Good
+        }
+    }
+}
+
--- a/test/sun/security/tools/keytool/KeyToolTest.java	Tue Jan 05 08:49:57 2016 -0800
+++ b/test/sun/security/tools/keytool/KeyToolTest.java	Fri Jan 15 16:23:58 2016 -0800
@@ -55,6 +55,8 @@
  * NSS PKCS11 config file are changed, DSA not supported now.
  */
 
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.security.KeyStore;
 import sun.security.x509.*;
 import java.io.*;
@@ -844,6 +846,24 @@
         remove("mykey.cert");
     }
 
+    // 8074935: jdk8 keytool doesn't validate pem files for RFC 1421 correctness
+    static void checkPem(String file) throws Exception {
+        boolean maybeLast = false;
+        for (String s: Files.readAllLines(Paths.get(file))) {
+            if (s.isEmpty()) continue;
+            if (s.startsWith("---")) continue;
+            if (maybeLast) {
+                throw new Exception("Last line already seen");
+            }
+            if (s.length() > 64) {
+                throw new Exception(s);
+            }
+            if (s.length() < 64) {
+                maybeLast = true;
+            }
+        }
+    }
+
     void v3extTest(String keyAlg) throws Exception {
         KeyStore ks;
         remove("x.jks");
@@ -1153,11 +1173,14 @@
                 "-rfc -file test.req");
         // printcertreq
         testOK("", "-printcertreq -file test.req");
-        // issue: deny KU, change criticality of 1.2.3 and 1.2.4, change content of BC, add 2.3.4
+        checkPem("test.req");
+        // issue: deny KU, change criticality of 1.2.3 and 1.2.4,
+        // change content of BC, add 2.3.4
         testOK("", simple+"-gencert -alias ca -infile test.req -ext " +
                 "honored=all,-KU,1.2.3:critical,1.2.4:non-critical " +
                 "-ext BC=2 -ext 2.3.4=01020304 " +
                 "-debug -rfc -outfile test.cert");
+        checkPem("test.cert");
         testOK("", simple+"-importcert -file test.cert -alias a");
         ks = loadStore("x.jks", "changeit", "JKS");
         X509CertImpl a = (X509CertImpl)ks.getCertificate("a");