diff --git a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java index 4b615ca9af1..38ba68d3eee 100644 --- a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java +++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java @@ -306,9 +306,12 @@ public class PKCS10 { byte[] CRLF = new byte[] {'\r', '\n'}; - out.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); - out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded)); - out.println("-----END NEW CERTIFICATE REQUEST-----"); + out.print("-----BEGIN NEW CERTIFICATE REQUEST-----"); + out.print("\r\n"); + out.print(Base64.getMimeEncoder(64, CRLF).encodeToString(encoded)); + out.print("\r\n"); + out.print("-----END NEW CERTIFICATE REQUEST-----"); + out.print("\r\n"); } /** diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index b094acc51fe..b2527a7fc2c 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -1610,9 +1610,12 @@ public final class Main { new X509CRLImpl.TBSCertList(owner, firstDate, lastDate, badCerts), privateKey, sigAlgName); if (rfc) { - out.println("-----BEGIN X509 CRL-----"); - out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal())); - out.println("-----END X509 CRL-----"); + out.print("-----BEGIN X509 CRL-----"); + out.print("\r\n"); + out.print(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal())); + out.print("\r\n"); + out.print("-----END X509 CRL-----"); + out.print("\r\n"); } else { out.write(crl.getEncodedInternal()); } @@ -2784,9 +2787,12 @@ public final class Main { throws Exception { X509CRL xcrl = (X509CRL)crl; if (rfc) { - out.println("-----BEGIN X509 CRL-----"); - out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded())); - out.println("-----END X509 CRL-----"); + out.print("-----BEGIN X509 CRL-----"); + out.print("\r\n"); + out.print(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded())); + out.print("\r\n"); + out.print("-----END X509 CRL-----"); + out.print("\r\n"); } else { String s; if (crl instanceof X509CRLImpl x509crl) { @@ -3800,9 +3806,12 @@ public final class Main { throws IOException, CertificateException { if (rfc) { - out.println(X509Factory.BEGIN_CERT); - out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded())); - out.println(X509Factory.END_CERT); + out.print(X509Factory.BEGIN_CERT); + out.print("\r\n"); + out.print(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded())); + out.print("\r\n"); + out.print(X509Factory.END_CERT); + out.print("\r\n"); } else { out.write(cert.getEncoded()); // binary } diff --git a/test/jdk/sun/security/tools/keytool/LineEndings.java b/test/jdk/sun/security/tools/keytool/LineEndings.java new file mode 100644 index 00000000000..621102ae73c --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/LineEndings.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, 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. + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.SecurityTools; +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.ByteArrayOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * @test + * @bug 8202598 + * @summary PEM outputs should have consistent line endings + * @library /test/lib + */ + +public class LineEndings { + + public static void main(String[] args) throws Exception { + keytool("-genkeypair -dname CN=A -keyalg ec"); + + keytool("-certreq -file a.csr -rfc"); + checkFile("a.csr"); + + keytool("-exportcert -file a.crt -rfc"); + checkFile("a.crt"); + + keytool("-gencrl -id 1 -rfc -file a.crl"); + checkFile("a.crl"); + + // `keytool -printcrl` shows "Verified by ..." at the end. Remove it. + String print = keytool("-printcrl -file a.crl -rfc").getStdout(); + print = print.substring(0, print.indexOf("Verified by")); + Files.writeString(Path.of("print"), print); + checkFile("print"); + } + + private static OutputAnalyzer keytool(String cmd) throws Exception { + return SecurityTools.keytool( + "-keystore ks -storepass changeit -alias a " + cmd) + .shouldHaveExitValue(0); + } + + // Make sure only CRLF is used inside the file. + private static void checkFile(String name) throws Exception { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + for (byte b : Files.readAllBytes(Path.of(name))) { + // Collect all non-printable bytes in an array + if (b < 32) bout.write(b); + } + // There should only be a series of CRLFs left + byte[] endings = bout.toByteArray(); + Asserts.assertTrue(endings.length > 4, "Too empty"); + Asserts.assertTrue(endings.length % 2 == 0, + "Length is " + endings.length); + for (int i = 0; i < endings.length; i += 2) { + Asserts.assertEquals(endings[i], (byte)'\r', + "Byte at " + i + " is not CR"); + Asserts.assertEquals(endings[i + 1], (byte)'\n', + "Byte at " + (i + 1) + " is not LF"); + } + } +}