8256895: Add support for RFC 8954: Online Certificate Status Protocol (OCSP) Nonce Extension

Reviewed-by: jnimeh, mullan
This commit is contained in:
Hai-May Chao 2021-01-20 22:23:50 +00:00 committed by Sean Mullan
parent 4f11ff325f
commit 8b95d9549e
5 changed files with 77 additions and 24 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -76,14 +76,16 @@ public final class OCSPNonceExtension extends Extension {
* *
* @throws IOException if any errors happen during encoding of the * @throws IOException if any errors happen during encoding of the
* extension. * extension.
* @throws IllegalArgumentException if length is not a positive integer. * @throws IllegalArgumentException if length is not in the range of 1 to 32.
*/ */
public OCSPNonceExtension(boolean isCritical, int length) public OCSPNonceExtension(boolean isCritical, int length)
throws IOException { throws IOException {
this.extensionId = PKIXExtensions.OCSPNonce_Id; this.extensionId = PKIXExtensions.OCSPNonce_Id;
this.critical = isCritical; this.critical = isCritical;
if (length > 0) { // RFC 8954, section 2.1: the length of the nonce MUST be at least 1 octet
// and can be up to 32 octets.
if (length > 0 && length <= 32) {
SecureRandom rng = new SecureRandom(); SecureRandom rng = new SecureRandom();
this.nonceData = new byte[length]; this.nonceData = new byte[length];
rng.nextBytes(nonceData); rng.nextBytes(nonceData);
@ -91,7 +93,7 @@ public final class OCSPNonceExtension extends Extension {
nonceData).toByteArray(); nonceData).toByteArray();
} else { } else {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Length must be a positive integer"); "Length of nonce must be at least 1 byte and can be up to 32 bytes");
} }
} }
@ -121,12 +123,13 @@ public final class OCSPNonceExtension extends Extension {
* @param isCritical a boolean flag indicating whether the criticality bit * @param isCritical a boolean flag indicating whether the criticality bit
* is set for this extension * is set for this extension
* @param incomingNonce The nonce data to be set for the extension. This * @param incomingNonce The nonce data to be set for the extension. This
* must be a non-null array of at least one byte long. * must be a non-null array of at least one byte long and can be up to
* 32 bytes.
* *
* @throws IOException if any errors happen during encoding of the * @throws IOException if any errors happen during encoding of the
* extension. * extension.
* @throws IllegalArgumentException if the incomingNonce length is not a * @throws IllegalArgumentException if the incomingNonce length is not
* positive integer. * in the range of 1 to 32.
* @throws NullPointerException if the incomingNonce is null. * @throws NullPointerException if the incomingNonce is null.
*/ */
public OCSPNonceExtension(boolean isCritical, byte[] incomingNonce) public OCSPNonceExtension(boolean isCritical, byte[] incomingNonce)
@ -135,13 +138,15 @@ public final class OCSPNonceExtension extends Extension {
this.critical = isCritical; this.critical = isCritical;
Objects.requireNonNull(incomingNonce, "Nonce data must be non-null"); Objects.requireNonNull(incomingNonce, "Nonce data must be non-null");
if (incomingNonce.length > 0) { // RFC 8954, section 2.1: the length of the nonce MUST be at least 1 octet
// and can be up to 32 octets.
if (incomingNonce.length > 0 && incomingNonce.length <= 32) {
this.nonceData = incomingNonce.clone(); this.nonceData = incomingNonce.clone();
this.extensionValue = new DerValue(DerValue.tag_OctetString, this.extensionValue = new DerValue(DerValue.tag_OctetString,
nonceData).toByteArray(); nonceData).toByteArray();
} else { } else {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Nonce data must be at least 1 byte in length"); "Nonce data must be at least 1 byte and can be up to 32 bytes in length");
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -83,7 +83,10 @@ class RevocationChecker extends PKIXRevocationChecker {
String ocspSubject; String ocspSubject;
String ocspIssuer; String ocspIssuer;
String ocspSerial; String ocspSerial;
boolean ocspNonce;
} }
private RevocationProperties rp;
private static final int DEFAULT_NONCE_BYTES = 16;
RevocationChecker() { RevocationChecker() {
legacy = false; legacy = false;
@ -99,7 +102,7 @@ class RevocationChecker extends PKIXRevocationChecker {
void init(TrustAnchor anchor, ValidatorParams params) void init(TrustAnchor anchor, ValidatorParams params)
throws CertPathValidatorException throws CertPathValidatorException
{ {
RevocationProperties rp = getRevocationProperties(); rp = getRevocationProperties();
URI uri = getOcspResponder(); URI uri = getOcspResponder();
responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri; responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri;
X509Certificate cert = getOcspResponderCert(); X509Certificate cert = getOcspResponderCert();
@ -198,6 +201,8 @@ class RevocationChecker extends PKIXRevocationChecker {
= Security.getProperty("ocsp.responderCertSerialNumber"); = Security.getProperty("ocsp.responderCertSerialNumber");
rp.crlDPEnabled rp.crlDPEnabled
= Boolean.getBoolean("com.sun.security.enableCRLDP"); = Boolean.getBoolean("com.sun.security.enableCRLDP");
rp.ocspNonce
= Boolean.getBoolean("jdk.security.certpath.ocspNonce");
return rp; return rp;
} }
} }
@ -712,6 +717,13 @@ class RevocationChecker extends PKIXRevocationChecker {
certId = new CertId(issuerInfo.getName(), issuerInfo.getPublicKey(), certId = new CertId(issuerInfo.getName(), issuerInfo.getPublicKey(),
currCert.getSerialNumberObject()); currCert.getSerialNumberObject());
byte[] nonce = null;
for (Extension ext : ocspExtensions) {
if (ext.getId().equals(KnownOIDs.OCSPNonceExt.value())) {
nonce = ext.getValue();
}
}
// check if there is a cached OCSP response available // check if there is a cached OCSP response available
byte[] responseBytes = ocspResponses.get(cert); byte[] responseBytes = ocspResponses.get(cert);
if (responseBytes != null) { if (responseBytes != null) {
@ -721,12 +733,6 @@ class RevocationChecker extends PKIXRevocationChecker {
response = new OCSPResponse(responseBytes); response = new OCSPResponse(responseBytes);
// verify the response // verify the response
byte[] nonce = null;
for (Extension ext : ocspExtensions) {
if (ext.getId().equals(KnownOIDs.OCSPNonceExt.value())) {
nonce = ext.getValue();
}
}
response.verify(Collections.singletonList(certId), issuerInfo, response.verify(Collections.singletonList(certId), issuerInfo,
responderCert, params.date(), nonce, params.variant()); responderCert, params.date(), nonce, params.variant());
@ -740,9 +746,43 @@ class RevocationChecker extends PKIXRevocationChecker {
null, -1); null, -1);
} }
List<Extension> tmpExtensions = null;
if (rp.ocspNonce) {
if (nonce == null) {
try {
// create the 16-byte nonce by default
Extension nonceExt = new OCSPNonceExtension(DEFAULT_NONCE_BYTES);
if (ocspExtensions.size() > 0) {
tmpExtensions = new ArrayList<Extension>(ocspExtensions);
tmpExtensions.add(nonceExt);
} else {
tmpExtensions = List.of(nonceExt);
}
if (debug != null) {
debug.println("Default nonce has been created in the OCSP extensions");
}
} catch (IOException e) {
throw new CertPathValidatorException("Failed to create the default nonce " +
"in OCSP extensions", e);
}
} else {
throw new CertPathValidatorException("Application provided nonce cannot be " +
"used if the value of the jdk.security.certpath.ocspNonce system " +
"property is true");
}
} else {
if (nonce != null) {
if (debug != null) {
debug.println("Using application provided nonce");
}
}
}
response = OCSP.check(Collections.singletonList(certId), response = OCSP.check(Collections.singletonList(certId),
responderURI, issuerInfo, responderCert, null, responderURI, issuerInfo, responderCert, null,
ocspExtensions, params.variant()); rp.ocspNonce ? tmpExtensions : ocspExtensions, params.variant());
} }
} catch (IOException e) { } catch (IOException e) {
throw new CertPathValidatorException( throw new CertPathValidatorException(

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8232019 * @bug 8232019 8256895
* @summary Interoperability tests with LuxTrust Global Root 2 CA * @summary Interoperability tests with LuxTrust Global Root 2 CA
* @build ValidatePathWithParams * @build ValidatePathWithParams
* @run main/othervm -Djava.security.debug=certpath LuxTrustCA OCSP * @run main/othervm -Djava.security.debug=certpath LuxTrustCA OCSP
@ -173,6 +173,7 @@ public class LuxTrustCA {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.setProperty("jdk.security.certpath.ocspNonce", "true");
ValidatePathWithParams pathValidator = new ValidatePathWithParams(null); ValidatePathWithParams pathValidator = new ValidatePathWithParams(null);
if (args.length >= 1 && "CRL".equalsIgnoreCase(args[0])) { if (args.length >= 1 && "CRL".equalsIgnoreCase(args[0])) {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8243320 * @bug 8243320 8256895
* @summary Interoperability tests with SSL.com's RSA, EV RSA, and ECC CA * @summary Interoperability tests with SSL.com's RSA, EV RSA, and ECC CA
* @build ValidatePathWithParams * @build ValidatePathWithParams
* @run main/othervm -Djava.security.debug=certpath SSLCA OCSP * @run main/othervm -Djava.security.debug=certpath SSLCA OCSP
@ -47,6 +47,7 @@ public class SSLCA {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.setProperty("jdk.security.certpath.ocspNonce", "true");
ValidatePathWithParams pathValidator = new ValidatePathWithParams(null); ValidatePathWithParams pathValidator = new ValidatePathWithParams(null);
boolean ocspEnabled = false; boolean ocspEnabled = false;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8046321 * @bug 8046321 8256895
* @summary Unit tests for OCSPNonceExtension objects * @summary Unit tests for OCSPNonceExtension objects
* @modules java.base/sun.security.provider.certpath * @modules java.base/sun.security.provider.certpath
* java.base/sun.security.util * java.base/sun.security.util
@ -192,6 +192,12 @@ public class OCSPNonceExtensionTests {
throw new RuntimeException("Accepted a zero length nonce"); throw new RuntimeException("Accepted a zero length nonce");
} catch (IllegalArgumentException iae) { } } catch (IllegalArgumentException iae) { }
// Length of the nonce exceeds the maximum 32 bytes
try {
Extension bigLenNonce = new OCSPNonceExtension(33);
throw new RuntimeException("Accepted a larger than 32 bytes of nonce");
} catch (IllegalArgumentException iae) { }
// Valid input to constructor // Valid input to constructor
Extension nonceByLen = new OCSPNonceExtension(32); Extension nonceByLen = new OCSPNonceExtension(32);