Skip to content

Commit 0c783b0

Browse files
committed
8257497: Update keytool to create AKID from the SKID of the issuing certificate as specified by RFC 5280
Reviewed-by: clanger Backport-of: 05301f5
1 parent aebdee3 commit 0c783b0

File tree

2 files changed

+142
-11
lines changed

2 files changed

+142
-11
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, 2019, 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
@@ -1493,12 +1493,39 @@ private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStr
14931493
reqex = (CertificateExtensions)attr.getAttributeValue();
14941494
}
14951495
}
1496+
1497+
PublicKey subjectPubKey = req.getSubjectPublicKeyInfo();
1498+
PublicKey issuerPubKey = signerCert.getPublicKey();
1499+
1500+
KeyIdentifier signerSubjectKeyId;
1501+
if (Arrays.equals(subjectPubKey.getEncoded(), issuerPubKey.getEncoded())) {
1502+
// No AKID for self-signed cert
1503+
signerSubjectKeyId = null;
1504+
} else {
1505+
X509CertImpl certImpl;
1506+
if (signerCert instanceof X509CertImpl) {
1507+
certImpl = (X509CertImpl) signerCert;
1508+
} else {
1509+
certImpl = new X509CertImpl(signerCert.getEncoded());
1510+
}
1511+
1512+
// To enforce compliance with RFC 5280 section 4.2.1.1: "Where a key
1513+
// identifier has been previously established, the CA SHOULD use the
1514+
// previously established identifier."
1515+
// Use issuer's SKID to establish the AKID in createV3Extensions() method.
1516+
signerSubjectKeyId = certImpl.getSubjectKeyId();
1517+
1518+
if (signerSubjectKeyId == null) {
1519+
signerSubjectKeyId = new KeyIdentifier(issuerPubKey);
1520+
}
1521+
}
1522+
14961523
CertificateExtensions ext = createV3Extensions(
14971524
reqex,
14981525
null,
14991526
v3ext,
1500-
req.getSubjectPublicKeyInfo(),
1501-
signerCert.getPublicKey());
1527+
subjectPubKey,
1528+
signerSubjectKeyId);
15021529
info.set(X509CertInfo.EXTENSIONS, ext);
15031530
X509CertImpl cert = new X509CertImpl(info);
15041531
cert.sign(privateKey, params, sigAlgName, null);
@@ -4204,14 +4231,15 @@ private static void setExt(CertificateExtensions result, Extension ex)
42044231
* @param extstrs -ext values, Read keytool doc
42054232
* @param pkey the public key for the certificate
42064233
* @param akey the public key for the authority (issuer)
4234+
* @param aSubjectKeyId the subject key identifier for the authority (issuer)
42074235
* @return the created CertificateExtensions
42084236
*/
42094237
private CertificateExtensions createV3Extensions(
42104238
CertificateExtensions requestedEx,
42114239
CertificateExtensions existingEx,
42124240
List <String> extstrs,
42134241
PublicKey pkey,
4214-
PublicKey akey) throws Exception {
4242+
KeyIdentifier aSubjectKeyId) throws Exception {
42154243

42164244
// By design, inside a CertificateExtensions object, all known
42174245
// extensions uses name (say, "BasicConstraints") as key and
@@ -4236,6 +4264,14 @@ private CertificateExtensions createV3Extensions(
42364264
}
42374265
}
42384266
try {
4267+
// always non-critical
4268+
setExt(result, new SubjectKeyIdentifierExtension(
4269+
new KeyIdentifier(pkey).getIdentifier()));
4270+
if (aSubjectKeyId != null) {
4271+
setExt(result, new AuthorityKeyIdentifierExtension(aSubjectKeyId,
4272+
null, null));
4273+
}
4274+
42394275
// name{:critical}{=value}
42404276
// Honoring requested extensions
42414277
if (requestedEx != null) {
@@ -4568,13 +4604,6 @@ private CertificateExtensions createV3Extensions(
45684604
"Unknown.extension.type.") + extstr);
45694605
}
45704606
}
4571-
// always non-critical
4572-
setExt(result, new SubjectKeyIdentifierExtension(
4573-
new KeyIdentifier(pkey).getIdentifier()));
4574-
if (akey != null && !pkey.equals(akey)) {
4575-
setExt(result, new AuthorityKeyIdentifierExtension(
4576-
new KeyIdentifier(akey), null, null));
4577-
}
45784607
} catch(IOException e) {
45794608
throw new RuntimeException(e);
45804609
}
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 jdk.test.lib.KnownOIDs;
41+
import static jdk.test.lib.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+
}

0 commit comments

Comments
 (0)