Skip to content

Commit e1d1fa9

Browse files
seanjmullanbchristi-git
authored andcommitted
8360937: Enhance certificate handling
Reviewed-by: ahgross, rhalade, jnibedita, ascarpino, naoto
1 parent c448505 commit e1d1fa9

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

src/java.base/share/classes/sun/security/util/DerValue.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,22 @@ public String getUniversalString() throws IOException {
859859
return readStringInternal(tag_UniversalString, new UTF_32BE());
860860
}
861861

862+
/**
863+
* Checks that the BMPString does not contain any surrogate characters,
864+
* which are outside the Basic Multilingual Plane.
865+
*
866+
* @throws IOException if illegal characters are detected
867+
*/
868+
public void validateBMPString() throws IOException {
869+
String bmpString = getBMPString();
870+
for (int i = 0; i < bmpString.length(); i++) {
871+
if (Character.isSurrogate(bmpString.charAt(i))) {
872+
throw new IOException(
873+
"Illegal character in BMPString, index: " + i);
874+
}
875+
}
876+
}
877+
862878
/**
863879
* Reads the ASN.1 NULL value
864880
*/

src/java.base/share/classes/sun/security/x509/AVA.java

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,10 +28,13 @@
2828
import java.io.ByteArrayOutputStream;
2929
import java.io.IOException;
3030
import java.io.Reader;
31+
import java.nio.charset.Charset;
3132
import java.text.Normalizer;
3233
import java.util.*;
3334

35+
import static java.nio.charset.StandardCharsets.ISO_8859_1;
3436
import static java.nio.charset.StandardCharsets.UTF_8;
37+
import static java.nio.charset.StandardCharsets.UTF_16BE;
3538

3639
import sun.security.util.*;
3740
import sun.security.pkcs.PKCS9Attribute;
@@ -589,6 +592,10 @@ private static boolean trailingSpace(Reader in) throws IOException {
589592
throw new IOException("AVA, extra bytes = "
590593
+ derval.data.available());
591594
}
595+
596+
if (value.tag == DerValue.tag_BMPString) {
597+
value.validateBMPString();
598+
}
592599
}
593600

594601
AVA(DerInputStream in) throws IOException {
@@ -713,7 +720,8 @@ public String toRFC2253String(Map<String, String> oidMap) {
713720
* NOTE: this implementation only emits DirectoryStrings of the
714721
* types returned by isDerString().
715722
*/
716-
String valStr = new String(value.getDataBytes(), UTF_8);
723+
String valStr =
724+
new String(value.getDataBytes(), getCharset(value, false));
717725

718726
/*
719727
* 2.4 (cont): If the UTF-8 string does not have any of the
@@ -832,7 +840,8 @@ public String toRFC2253CanonicalString() {
832840
* NOTE: this implementation only emits DirectoryStrings of the
833841
* types returned by isDerString().
834842
*/
835-
String valStr = new String(value.getDataBytes(), UTF_8);
843+
String valStr =
844+
new String(value.getDataBytes(), getCharset(value, true));
836845

837846
/*
838847
* 2.4 (cont): If the UTF-8 string does not have any of the
@@ -927,6 +936,39 @@ private static boolean isDerString(DerValue value, boolean canonical) {
927936
}
928937
}
929938

939+
/*
940+
* Returns the charset that should be used to decode each DN string type.
941+
*
942+
* This method ensures that multi-byte (UTF8String and BMPString) types
943+
* are decoded using the correct charset and the String forms represent
944+
* the correct characters. For 8-bit ASCII-based types (PrintableString
945+
* and IA5String), we return ISO_8859_1 rather than ASCII, so that the
946+
* complete range of characters can be represented, as many certificates
947+
* do not comply with the Internationalized Domain Name ACE format.
948+
*
949+
* NOTE: this method only supports DirectoryStrings of the types returned
950+
* by isDerString().
951+
*/
952+
private static Charset getCharset(DerValue value, boolean canonical) {
953+
if (canonical) {
954+
return switch (value.tag) {
955+
case DerValue.tag_PrintableString -> ISO_8859_1;
956+
case DerValue.tag_UTF8String -> UTF_8;
957+
default -> throw new Error("unexpected tag: " + value.tag);
958+
};
959+
}
960+
961+
return switch (value.tag) {
962+
case DerValue.tag_PrintableString,
963+
DerValue.tag_T61String,
964+
DerValue.tag_IA5String,
965+
DerValue.tag_GeneralString -> ISO_8859_1;
966+
case DerValue.tag_BMPString -> UTF_16BE;
967+
case DerValue.tag_UTF8String -> UTF_8;
968+
default -> throw new Error("unexpected tag: " + value.tag);
969+
};
970+
}
971+
930972
boolean hasRFC2253Keyword() {
931973
return AVAKeyword.hasKeyword(oid, RFC2253);
932974
}

test/lib/jdk/test/lib/security/CertificateBuilder.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import sun.security.x509.SubjectAlternativeNameExtension;
5454
import sun.security.x509.URIName;
5555
import sun.security.x509.KeyIdentifier;
56+
import sun.security.x509.X500Name;
5657

5758

5859
/**
@@ -90,7 +91,7 @@
9091
public class CertificateBuilder {
9192
private final CertificateFactory factory;
9293

93-
private X500Principal subjectName = null;
94+
private X500Name subjectName = null;
9495
private BigInteger serialNumber = null;
9596
private PublicKey publicKey = null;
9697
private Date notBefore = null;
@@ -199,7 +200,7 @@ public CertificateBuilder() throws CertificateException {
199200
* on this certificate.
200201
*/
201202
public CertificateBuilder setSubjectName(X500Principal name) {
202-
subjectName = name;
203+
subjectName = X500Name.asX500Name(name);
203204
return this;
204205
}
205206

@@ -209,7 +210,23 @@ public CertificateBuilder setSubjectName(X500Principal name) {
209210
* @param name The subject name in RFC 2253 format
210211
*/
211212
public CertificateBuilder setSubjectName(String name) {
212-
subjectName = new X500Principal(name);
213+
try {
214+
subjectName = new X500Name(name);
215+
} catch (IOException ioe) {
216+
throw new IllegalArgumentException(ioe);
217+
}
218+
return this;
219+
}
220+
221+
/**
222+
* Set the subject name for the certificate. This method is useful when
223+
* you need more control over the contents of the subject name.
224+
*
225+
* @param name an {@code X500Name} to be used as the subject name
226+
* on this certificate
227+
*/
228+
public CertificateBuilder setSubjectName(X500Name name) {
229+
subjectName = name;
213230
return this;
214231
}
215232

0 commit comments

Comments
 (0)