8284194: Allow empty subject fields in keytool

Reviewed-by: jnimeh, hchao
This commit is contained in:
Weijun Wang 2022-05-15 22:30:06 +00:00
parent dc9462137c
commit f4f1dddfef
4 changed files with 125 additions and 37 deletions

View File

@ -37,12 +37,10 @@ import java.security.cert.CertStoreException;
import java.security.cert.CRL;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.TrustAnchor;
import java.security.cert.URICertStoreParameters;
import java.security.spec.ECParameterSpec;
import java.text.Collator;
import java.text.MessageFormat;
import java.util.*;
@ -66,7 +64,6 @@ import sun.security.provider.certpath.CertPathConstraintsParameters;
import sun.security.util.ConstraintsParameters;
import sun.security.util.ECKeySizeParameterSpec;
import sun.security.util.KeyUtil;
import sun.security.util.NamedCurve;
import sun.security.util.ObjectIdentifier;
import sun.security.pkcs10.PKCS10;
import sun.security.pkcs10.PKCS10Attribute;
@ -3731,11 +3728,13 @@ public final class Main {
String userInput = null;
int maxRetry = 20;
boolean needRepeat;
do {
if (maxRetry-- < 0) {
throw new RuntimeException(rb.getString(
"Too.many.retries.program.terminated"));
}
System.err.println(rb.getString("enter.dname.components"));
commonName = inputString(in,
rb.getString("What.is.your.first.and.last.name."),
commonName);
@ -3756,20 +3755,32 @@ public final class Main {
rb.getString
("What.is.the.two.letter.country.code.for.this.unit."),
country);
name = new X500Name(commonName, organizationalUnit, organization,
city, state, country);
MessageFormat form = new MessageFormat
(rb.getString("Is.name.correct."));
Object[] source = {name};
userInput = inputString
(in, form.format(source), rb.getString("no"));
} while (collator.compare(userInput, rb.getString("yes")) != 0 &&
collator.compare(userInput, rb.getString("y")) != 0);
name = new X500Name(
dotToNull(commonName), dotToNull(organizationalUnit),
dotToNull(organization), dotToNull(city),
dotToNull(state), dotToNull(country));
if (name.isEmpty()) {
System.err.println(rb.getString("no.field.in.dname"));
needRepeat = true;
} else {
MessageFormat form = new MessageFormat
(rb.getString("Is.name.correct."));
Object[] source = {name};
userInput = inputString
(in, form.format(source), rb.getString("no"));
needRepeat = collator.compare(userInput, rb.getString("yes")) != 0 &&
collator.compare(userInput, rb.getString("y")) != 0;
}
} while (needRepeat);
System.err.println();
return name;
}
private static String dotToNull(String input) {
return ".".equals(input) ? null : input;
}
private String inputString(BufferedReader in, String prompt,
String defaultValue)
throws IOException
@ -3777,7 +3788,7 @@ public final class Main {
System.err.println(prompt);
MessageFormat form = new MessageFormat
(rb.getString(".defaultValue."));
Object[] source = {defaultValue};
Object[] source = { ".".equals(defaultValue) ? "" : defaultValue };
System.err.print(form.format(source));
System.err.flush();

View File

@ -374,6 +374,8 @@ public class Resources extends java.util.ListResourceBundle {
{"Enter.alias.name.", "Enter alias name: "},
{".RETURN.if.same.as.for.otherAlias.",
"\t(RETURN if same as for <{0}>)"},
{"enter.dname.components",
"Enter the distinguished name. Provide a single dot (.) to leave a sub-component empty or press ENTER to use the default value in braces."},
{"What.is.your.first.and.last.name.",
"What is your first and last name?"},
{"What.is.the.name.of.your.organizational.unit.",
@ -386,6 +388,8 @@ public class Resources extends java.util.ListResourceBundle {
"What is the name of your State or Province?"},
{"What.is.the.two.letter.country.code.for.this.unit.",
"What is the two-letter country code for this unit?"},
{"no.field.in.dname",
"At least one field must be provided. Enter again."},
{"Is.name.correct.", "Is {0} correct?"},
{"no", "no"},
{"yes", "yes"},

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2022, 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
@ -241,29 +241,45 @@ public class X500Name implements GeneralNameInterface, Principal {
String organizationName, String localityName,
String stateName, String country)
throws IOException {
names = new RDN[6];
/*
* NOTE: it's only on output that little-endian
* ordering is used.
*/
names[5] = new RDN(1);
names[5].assertion[0] = new AVA(commonName_oid,
new DerValue(commonName));
names[4] = new RDN(1);
names[4].assertion[0] = new AVA(orgUnitName_oid,
new DerValue(organizationUnit));
names[3] = new RDN(1);
names[3].assertion[0] = new AVA(orgName_oid,
new DerValue(organizationName));
names[2] = new RDN(1);
names[2].assertion[0] = new AVA(localityName_oid,
new DerValue(localityName));
names[1] = new RDN(1);
names[1].assertion[0] = new AVA(stateName_oid,
new DerValue(stateName));
names[0] = new RDN(1);
names[0].assertion[0] = new AVA(countryName_oid,
new DerValue(country));
RDN name;
List<RDN> list = new ArrayList<>(6);
if (country != null) {
name = new RDN(1);
name.assertion[0] = new AVA(countryName_oid,
new DerValue(country));
list.add(name);
}
if (stateName != null) {
name = new RDN(1);
name.assertion[0] = new AVA(stateName_oid,
new DerValue(stateName));
list.add(name);
}
if (localityName != null) {
name = new RDN(1);
name.assertion[0] = new AVA(localityName_oid,
new DerValue(localityName));
list.add(name);
}
if (organizationName != null) {
name = new RDN(1);
name.assertion[0] = new AVA(orgName_oid,
new DerValue(organizationName));
list.add(name);
}
if (organizationUnit != null) {
name = new RDN(1);
name.assertion[0] = new AVA(orgUnitName_oid,
new DerValue(organizationUnit));
list.add(name);
}
if (commonName != null) {
name = new RDN(1);
name.assertion[0] = new AVA(commonName_oid,
new DerValue(commonName));
list.add(name);
}
names = list.toArray(new RDN[0]);
}
/**

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2022, 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 8284194
* @summary Allow empty subject fields in keytool
* @library /test/lib
*/
import jdk.test.lib.Asserts;
import jdk.test.lib.SecurityTools;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
public class EmptyField {
public static void main(String[] args) throws Exception {
// All "." in first round, "Me" as name in 2nd round.
SecurityTools.setResponse(
".\n.\n.\n.\n.\n.\n" // all empty, must retry
+ "Me\n\n\n\n\n\nno\n" // one non-empty, ask yes/no
+ "\n\n\n\n\n\nyes\n"); // remember input
SecurityTools.keytool("-genkeypair -keystore ks -storepass changeit -alias b -keyalg EC")
.shouldContain("[Unknown]") // old default
.shouldContain("At least one field must be provided. Enter again.")
.shouldContain("[]") // new value in 2nd round
.shouldContain("[Me]") // new value in 3nd round
.shouldContain("Is CN=Me correct?")
.shouldHaveExitValue(0);
var ks = KeyStore.getInstance(new File("ks"), "changeit".toCharArray());
var cert = (X509Certificate) ks.getCertificate("b");
Asserts.assertEQ(cert.getSubjectX500Principal().toString(), "CN=Me");
}
}