Skip to content

Commit cbba8b9

Browse files
Alexey BakhtinRealCLanger
authored andcommitted
8360937: Enhance certificate handling
Reviewed-by: fferrari Backport-of: f2fba5a55176ca82985ca42996cef36be7b7500a
1 parent 770db93 commit cbba8b9

File tree

3 files changed

+79
-6
lines changed

3 files changed

+79
-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
@@ -852,6 +852,22 @@ public String getUniversalString() throws IOException {
852852
return readStringInternal(tag_UniversalString, new UTF_32BE());
853853
}
854854

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

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

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1996, 2020, 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.*;
@@ -594,6 +597,10 @@ private static boolean trailingSpace(Reader in) throws IOException {
594597
throw new IOException("AVA, extra bytes = "
595598
+ derval.data.available());
596599
}
600+
601+
if (value.tag == DerValue.tag_BMPString) {
602+
value.validateBMPString();
603+
}
597604
}
598605

599606
AVA(DerInputStream in) throws IOException {
@@ -735,7 +742,7 @@ public String toRFC2253String(Map<String, String> oidMap) {
735742
*/
736743
String valStr = null;
737744
try {
738-
valStr = new String(value.getDataBytes(), UTF_8);
745+
valStr = new String(value.getDataBytes(), getCharset(value, false));
739746
} catch (IOException ie) {
740747
throw new IllegalArgumentException("DER Value conversion");
741748
}
@@ -870,7 +877,7 @@ public String toRFC2253CanonicalString() {
870877
*/
871878
String valStr = null;
872879
try {
873-
valStr = new String(value.getDataBytes(), UTF_8);
880+
valStr = new String(value.getDataBytes(), getCharset(value, true));
874881
} catch (IOException ie) {
875882
throw new IllegalArgumentException("DER Value conversion");
876883
}
@@ -977,6 +984,39 @@ private static boolean isDerString(DerValue value, boolean canonical) {
977984
}
978985
}
979986

987+
/*
988+
* Returns the charset that should be used to decode each DN string type.
989+
*
990+
* This method ensures that multi-byte (UTF8String and BMPString) types
991+
* are decoded using the correct charset and the String forms represent
992+
* the correct characters. For 8-bit ASCII-based types (PrintableString
993+
* and IA5String), we return ISO_8859_1 rather than ASCII, so that the
994+
* complete range of characters can be represented, as many certificates
995+
* do not comply with the Internationalized Domain Name ACE format.
996+
*
997+
* NOTE: this method only supports DirectoryStrings of the types returned
998+
* by isDerString().
999+
*/
1000+
private static Charset getCharset(DerValue value, boolean canonical) {
1001+
if (canonical) {
1002+
return switch (value.tag) {
1003+
case DerValue.tag_PrintableString -> ISO_8859_1;
1004+
case DerValue.tag_UTF8String -> UTF_8;
1005+
default -> throw new Error("unexpected tag: " + value.tag);
1006+
};
1007+
}
1008+
1009+
return switch (value.tag) {
1010+
case DerValue.tag_PrintableString,
1011+
DerValue.tag_T61String,
1012+
DerValue.tag_IA5String,
1013+
DerValue.tag_GeneralString -> ISO_8859_1;
1014+
case DerValue.tag_BMPString -> UTF_16BE;
1015+
case DerValue.tag_UTF8String -> UTF_8;
1016+
default -> throw new Error("unexpected tag: " + value.tag);
1017+
};
1018+
}
1019+
9801020
boolean hasRFC2253Keyword() {
9811021
return AVAKeyword.hasKeyword(oid, RFC2253);
9821022
}

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import sun.security.x509.SubjectAlternativeNameExtension;
5555
import sun.security.x509.URIName;
5656
import sun.security.x509.KeyIdentifier;
57+
import sun.security.x509.X500Name;
5758

5859
/**
5960
* Helper class that builds and signs X.509 certificates.
@@ -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;
@@ -116,7 +117,7 @@ public CertificateBuilder() throws CertificateException {
116117
* on this certificate.
117118
*/
118119
public CertificateBuilder setSubjectName(X500Principal name) {
119-
subjectName = name;
120+
subjectName = X500Name.asX500Name(name);
120121
return this;
121122
}
122123

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

0 commit comments

Comments
 (0)