Skip to content

Commit 05301f5

Browse files
author
Hai-May Chao
committed
8257497: Update keytool to create AKID from the SKID of the issuing certificate as specified by RFC 5280
Reviewed-by: coffeys, mullan, weijun
1 parent cb84539 commit 05301f5

File tree

3 files changed

+146
-14
lines changed

3 files changed

+146
-14
lines changed

src/java.base/share/classes/sun/security/tools/keytool/Main.java

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2021, 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
@@ -1476,12 +1476,39 @@ private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStr
14761476
reqex = (CertificateExtensions)attr.getAttributeValue();
14771477
}
14781478
}
1479+
1480+
PublicKey subjectPubKey = req.getSubjectPublicKeyInfo();
1481+
PublicKey issuerPubKey = signerCert.getPublicKey();
1482+
1483+
KeyIdentifier signerSubjectKeyId;
1484+
if (Arrays.equals(subjectPubKey.getEncoded(), issuerPubKey.getEncoded())) {
1485+
// No AKID for self-signed cert
1486+
signerSubjectKeyId = null;
1487+
} else {
1488+
X509CertImpl certImpl;
1489+
if (signerCert instanceof X509CertImpl) {
1490+
certImpl = (X509CertImpl) signerCert;
1491+
} else {
1492+
certImpl = new X509CertImpl(signerCert.getEncoded());
1493+
}
1494+
1495+
// To enforce compliance with RFC 5280 section 4.2.1.1: "Where a key
1496+
// identifier has been previously established, the CA SHOULD use the
1497+
// previously established identifier."
1498+
// Use issuer's SKID to establish the AKID in createV3Extensions() method.
1499+
signerSubjectKeyId = certImpl.getSubjectKeyId();
1500+
1501+
if (signerSubjectKeyId == null) {
1502+
signerSubjectKeyId = new KeyIdentifier(issuerPubKey);
1503+
}
1504+
}
1505+
14791506
CertificateExtensions ext = createV3Extensions(
14801507
reqex,
14811508
null,
14821509
v3ext,
1483-
req.getSubjectPublicKeyInfo(),
1484-
signerCert.getPublicKey());
1510+
subjectPubKey,
1511+
signerSubjectKeyId);
14851512
info.set(X509CertInfo.EXTENSIONS, ext);
14861513
X509CertImpl cert = new X509CertImpl(info);
14871514
cert.sign(privateKey, sigAlgName);
@@ -4224,14 +4251,15 @@ private static void setExt(CertificateExtensions result, Extension ex)
42244251
* @param extstrs -ext values, Read keytool doc
42254252
* @param pkey the public key for the certificate
42264253
* @param akey the public key for the authority (issuer)
4254+
* @param aSubjectKeyId the subject key identifier for the authority (issuer)
42274255
* @return the created CertificateExtensions
42284256
*/
42294257
private CertificateExtensions createV3Extensions(
42304258
CertificateExtensions requestedEx,
42314259
CertificateExtensions existingEx,
42324260
List <String> extstrs,
42334261
PublicKey pkey,
4234-
PublicKey akey) throws Exception {
4262+
KeyIdentifier aSubjectKeyId) throws Exception {
42354263

42364264
// By design, inside a CertificateExtensions object, all known
42374265
// extensions uses name (say, "BasicConstraints") as key and
@@ -4256,6 +4284,14 @@ private CertificateExtensions createV3Extensions(
42564284
}
42574285
}
42584286
try {
4287+
// always non-critical
4288+
setExt(result, new SubjectKeyIdentifierExtension(
4289+
new KeyIdentifier(pkey).getIdentifier()));
4290+
if (aSubjectKeyId != null) {
4291+
setExt(result, new AuthorityKeyIdentifierExtension(aSubjectKeyId,
4292+
null, null));
4293+
}
4294+
42594295
// name{:critical}{=value}
42604296
// Honoring requested extensions
42614297
if (requestedEx != null) {
@@ -4578,13 +4614,6 @@ private CertificateExtensions createV3Extensions(
45784614
"Unknown.extension.type.") + extstr);
45794615
}
45804616
}
4581-
// always non-critical
4582-
setExt(result, new SubjectKeyIdentifierExtension(
4583-
new KeyIdentifier(pkey).getIdentifier()));
4584-
if (akey != null && !pkey.equals(akey)) {
4585-
setExt(result, new AuthorityKeyIdentifierExtension(
4586-
new KeyIdentifier(akey), null, null));
4587-
}
45884617
} catch(IOException e) {
45894618
throw new RuntimeException(e);
45904619
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8257497
27+
* @summary Check if issuer's SKID is used to establish the AKID for the subject cert
28+
* @library /test/lib
29+
* @modules java.base/sun.security.util
30+
*/
31+
32+
import jdk.test.lib.SecurityTools;
33+
import jdk.test.lib.process.OutputAnalyzer;
34+
35+
import java.io.*;
36+
import java.security.KeyStore;
37+
import java.security.cert.X509Certificate;
38+
import java.util.Arrays;
39+
import sun.security.util.DerValue;
40+
import sun.security.util.KnownOIDs;
41+
import static sun.security.util.KnownOIDs.*;
42+
43+
public class CheckCertAKID {
44+
45+
static OutputAnalyzer kt(String cmd, String ks) throws Exception {
46+
return SecurityTools.keytool("-storepass changeit " + cmd +
47+
" -keystore " + ks);
48+
}
49+
50+
public static void main(String[] args) throws Exception {
51+
52+
System.out.println("Generating a root cert with SubjectKeyIdentifier extension");
53+
kt("-genkeypair -keyalg rsa -alias ca -dname CN=CA -ext bc:c " +
54+
"-ext 2.5.29.14=04:14:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16:17:18:19",
55+
"ks");
56+
57+
kt("-exportcert -alias ca -rfc -file root.cert", "ks");
58+
59+
SecurityTools.keytool("-keystore ks -storepass changeit " +
60+
"-printcert -file root.cert")
61+
.shouldNotContain("AuthorityKeyIdentifier")
62+
.shouldContain("SubjectKeyIdentifier")
63+
.shouldContain("0000: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15")
64+
.shouldContain("0010: 16 17 18 19")
65+
.shouldHaveExitValue(0);
66+
67+
System.out.println("Generating an end entity cert using issuer CA's SKID as its AKID");
68+
kt("-genkeypair -keyalg rsa -alias e1 -dname CN=E1", "ks");
69+
kt("-certreq -alias e1 -file tmp.req", "ks");
70+
kt("-gencert -alias ca -ext san=dns:e1 -infile tmp.req -outfile tmp.cert ",
71+
"ks");
72+
kt("-importcert -alias e1 -file tmp.cert", "ks");
73+
74+
byte[] expectedId = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
75+
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19};
76+
77+
KeyStore kstore = KeyStore.getInstance(new File("ks"),
78+
"changeit".toCharArray());
79+
X509Certificate cert = (X509Certificate)kstore.getCertificate("e1");
80+
byte[] authorityKeyIdExt = cert.getExtensionValue(
81+
KnownOIDs.AuthorityKeyID.value());
82+
83+
byte[] authorityKeyId = null;
84+
if (authorityKeyIdExt == null) {
85+
System.out.println("Failed to get AKID extension from the cert");
86+
System.exit(1);
87+
} else {
88+
try {
89+
authorityKeyId = new DerValue(authorityKeyIdExt).getOctetString();
90+
} catch (IOException e) {
91+
System.out.println("Failed to get AKID encoded OctetString in the cert");
92+
System.exit(1);
93+
}
94+
}
95+
96+
authorityKeyId = Arrays.copyOfRange(authorityKeyId, 4, authorityKeyId.length);
97+
if (!Arrays.equals(authorityKeyId, expectedId)) {
98+
System.out.println("Failed due to AKID mismatch");
99+
System.exit(1);
100+
}
101+
}
102+
}

test/jdk/sun/security/tools/keytool/ExtOptionCamelCase.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2021, 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
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 8231950
26+
* @bug 8231950 8257497
2727
* @summary keytool -ext camel-case shorthand not working
2828
* @modules java.base/sun.security.tools.keytool
2929
* java.base/sun.security.tools.keytool:open
@@ -38,6 +38,7 @@
3838
import sun.security.x509.BasicConstraintsExtension;
3939
import sun.security.x509.CertificateExtensions;
4040
import sun.security.x509.Extension;
41+
import sun.security.x509.KeyIdentifier;
4142
import sun.security.x509.KeyUsageExtension;
4243

4344
import java.io.ByteArrayOutputStream;
@@ -154,7 +155,7 @@ static void prepare() throws Exception {
154155
CertificateExtensions.class,
155156
List.class,
156157
PublicKey.class,
157-
PublicKey.class);
158+
KeyIdentifier.class);
158159
createV3Extensions.setAccessible(true);
159160
ctor = Main.class.getDeclaredConstructor();
160161
ctor.setAccessible(true);

0 commit comments

Comments
 (0)