Skip to content

Commit 6433305

Browse files
Alexey BakhtinRealCLanger
authored andcommitted
8360937: Enhance certificate handling
Reviewed-by: fferrari Backport-of: f2fba5a55176ca82985ca42996cef36be7b7500a
1 parent 23bd6b5 commit 6433305

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
@@ -856,6 +856,22 @@ public String getUniversalString() throws IOException {
856856
return readStringInternal(tag_UniversalString, new UTF_32BE());
857857
}
858858

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

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, 2022, 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.action.GetBooleanAction;
3740
import sun.security.util.*;
@@ -590,6 +593,10 @@ private static boolean trailingSpace(Reader in) throws IOException {
590593
throw new IOException("AVA, extra bytes = "
591594
+ derval.data.available());
592595
}
596+
597+
if (value.tag == DerValue.tag_BMPString) {
598+
value.validateBMPString();
599+
}
593600
}
594601

595602
AVA(DerInputStream in) throws IOException {
@@ -714,7 +721,8 @@ public String toRFC2253String(Map<String, String> oidMap) {
714721
* NOTE: this implementation only emits DirectoryStrings of the
715722
* types returned by isDerString().
716723
*/
717-
String valStr = new String(value.getDataBytes(), UTF_8);
724+
String valStr =
725+
new String(value.getDataBytes(), getCharset(value, false));
718726

719727
/*
720728
* 2.4 (cont): If the UTF-8 string does not have any of the
@@ -839,7 +847,8 @@ public String toRFC2253CanonicalString() {
839847
* NOTE: this implementation only emits DirectoryStrings of the
840848
* types returned by isDerString().
841849
*/
842-
String valStr = new String(value.getDataBytes(), UTF_8);
850+
String valStr =
851+
new String(value.getDataBytes(), getCharset(value, true));
843852

844853
/*
845854
* 2.4 (cont): If the UTF-8 string does not have any of the
@@ -942,6 +951,39 @@ private static boolean isDerString(DerValue value, boolean canonical) {
942951
}
943952
}
944953

954+
/*
955+
* Returns the charset that should be used to decode each DN string type.
956+
*
957+
* This method ensures that multi-byte (UTF8String and BMPString) types
958+
* are decoded using the correct charset and the String forms represent
959+
* the correct characters. For 8-bit ASCII-based types (PrintableString
960+
* and IA5String), we return ISO_8859_1 rather than ASCII, so that the
961+
* complete range of characters can be represented, as many certificates
962+
* do not comply with the Internationalized Domain Name ACE format.
963+
*
964+
* NOTE: this method only supports DirectoryStrings of the types returned
965+
* by isDerString().
966+
*/
967+
private static Charset getCharset(DerValue value, boolean canonical) {
968+
if (canonical) {
969+
return switch (value.tag) {
970+
case DerValue.tag_PrintableString -> ISO_8859_1;
971+
case DerValue.tag_UTF8String -> UTF_8;
972+
default -> throw new Error("unexpected tag: " + value.tag);
973+
};
974+
}
975+
976+
return switch (value.tag) {
977+
case DerValue.tag_PrintableString,
978+
DerValue.tag_T61String,
979+
DerValue.tag_IA5String,
980+
DerValue.tag_GeneralString -> ISO_8859_1;
981+
case DerValue.tag_BMPString -> UTF_16BE;
982+
case DerValue.tag_UTF8String -> UTF_8;
983+
default -> throw new Error("unexpected tag: " + value.tag);
984+
};
985+
}
986+
945987
boolean hasRFC2253Keyword() {
946988
return AVAKeyword.hasKeyword(oid, RFC2253);
947989
}

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import sun.security.x509.SubjectAlternativeNameExtension;
5353
import sun.security.x509.URIName;
5454
import sun.security.x509.KeyIdentifier;
55+
import sun.security.x509.X500Name;
5556

5657
/**
5758
* Helper class that builds and signs X.509 certificates.
@@ -88,7 +89,7 @@
8889
public class CertificateBuilder {
8990
private final CertificateFactory factory;
9091

91-
private X500Principal subjectName = null;
92+
private X500Name subjectName = null;
9293
private BigInteger serialNumber = null;
9394
private PublicKey publicKey = null;
9495
private Date notBefore = null;
@@ -114,7 +115,7 @@ public CertificateBuilder() throws CertificateException {
114115
* on this certificate.
115116
*/
116117
public CertificateBuilder setSubjectName(X500Principal name) {
117-
subjectName = name;
118+
subjectName = X500Name.asX500Name(name);
118119
return this;
119120
}
120121

@@ -124,7 +125,23 @@ public CertificateBuilder setSubjectName(X500Principal name) {
124125
* @param name The subject name in RFC 2253 format
125126
*/
126127
public CertificateBuilder setSubjectName(String name) {
127-
subjectName = new X500Principal(name);
128+
try {
129+
subjectName = new X500Name(name);
130+
} catch (IOException ioe) {
131+
throw new IllegalArgumentException(ioe);
132+
}
133+
return this;
134+
}
135+
136+
/**
137+
* Set the subject name for the certificate. This method is useful when
138+
* you need more control over the contents of the subject name.
139+
*
140+
* @param name an {@code X500Name} to be used as the subject name
141+
* on this certificate
142+
*/
143+
public CertificateBuilder setSubjectName(X500Name name) {
144+
subjectName = name;
128145
return this;
129146
}
130147

0 commit comments

Comments
 (0)