Skip to content

8325766: Review seclibs tests for cert expiry #23700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

mpdonova
Copy link
Contributor

@mpdonova mpdonova commented Feb 19, 2025

This PR updates the CertificateBuilder with a new method that creates a new instance with common fields (subject name, public key, serial number, validity, and key uses) filled-in. One test, IPIdentities.java, is updated to show how the method can be used to create various certificates. I attached screenshots that compare the old hard-coded certificates (left) with the new generated certificates.

trusted-cert
server-cert
client-cert


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8325766: Review seclibs tests for cert expiry (Bug - P4)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/23700/head:pull/23700
$ git checkout pull/23700

Update a local copy of the PR:
$ git checkout pull/23700
$ git pull https://git.openjdk.org/jdk.git pull/23700/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 23700

View PR using the GUI difftool:
$ git pr show -t 23700

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/23700.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 19, 2025

👋 Welcome back mdonovan! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Feb 19, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk openjdk bot added the rfr Pull request is ready for review label Feb 19, 2025
@openjdk
Copy link

openjdk bot commented Feb 19, 2025

@mpdonova The following labels will be automatically applied to this pull request:

  • net
  • security

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@mlbridge
Copy link

mlbridge bot commented Feb 19, 2025

Webrevs

throws CertificateException, IOException {
SecureRandom random = new SecureRandom();

boolean [] keyUsage = new boolean[]{false, false, false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be easier to just use var keyUsage = new boolean[KeyUsage.values().length]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the array initialization.

@wangweij
Copy link
Contributor

The similarity between the certificate pairs is impressive! Just curious - why the change in issuer and owner names?

@mpdonova
Copy link
Contributor Author

The similarity between the certificate pairs is impressive! Just curious - why the change in issuer and owner names?

Looks like it's something between keytool and openssl x509. When i print the certificates with openssl, the issuer and owner names are in the opposite order from keytool.

@mpdonova
Copy link
Contributor Author

The similarity between the certificate pairs is impressive! Just curious - why the change in issuer and owner names?

After looking into this some more, I found that X500Name(String dname) is expecting the string to be in the same order as RFC 1779, 2253, or 4514. If you give the constructor a "backwards" string, it will store and print it backwards.

OpenSSL prints them backwards and I was using OpenSSL to print the hard-coded certificates and then just copying and pasting the strings.

I changed the hard-coded DN strings to follow the RFC order.

KeyUsage.DIGITAL_SIGNATURE, KeyUsage.NONREPUDIATION, KeyUsage.KEY_ENCIPHERMENT)
.addBasicConstraintsExt(false, false, -1)
.addExtension(CertificateBuilder.createIPSubjectAltNameExt(true, "127.0.0.1"))
.build(trustedCert, caKeys.getPrivate(), "MD5WithRSA");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MD5 algorithm is not allowed in TLSv1.3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll address this in JDK-8353738

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thanks!

Copy link
Member

@artur-oracle artur-oracle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bridgekeeper
Copy link

bridgekeeper bot commented May 16, 2025

@mpdonova This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply issue a /touch or /keepalive command to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration!

Comment on lines +108 to +109
public static CertificateBuilder newCertificateBuilder(String subjectName,
PublicKey publicKey, PublicKey caKey, KeyUsage... keyUsages)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion is to make this a regular constructor and have an additional method that sets the certificate lifetime as a Duration parameter, ex:

new CertificateBuilder(subject, pubKey, caPubKey, keyUsage).withValidity(Duration.ofHours(1));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the way it currently is follows the builder pattern better. All of the 'set' methods return this which means if I change this method to a constructor, I'd have to duplicate the "set" code from all those methods.

I like the idea of using Duration but the API for it doesn't really lend itself to this use case. When the certificate is finally created, we write Date objects to the DerOutputStream so I'd have to choose a start time and then calculate the end time based on the duration. It's not hard, but it doesn't seem worth it when the common use case of this class is to generate short-lived certificates for a test. The default one-hour validity will cover the vast majority of tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the way it currently is follows the builder pattern better. All of the 'set' methods return this which means if I change this method to a constructor, I'd have to duplicate the "set" code from all those methods.

I'm not sure what you mean - can you give an example? I don't see any advantages of using a static method here, but I agree it works either way.

I like the idea of using Duration but the API for it doesn't really lend itself to this use case. When the certificate is finally created, we write Date objects to the DerOutputStream so I'd have to choose a start time and then calculate the end time based on the duration. It's not hard, but it doesn't seem worth it when the common use case of this class is to generate short-lived certificates for a test. The default one-hour validity will cover the vast majority of tests.

I view this test utility class as being flexible enough for different cases. This is a useful method in that the other parameters are common fields that all certs usually have but it also means I can't use this method to to create a certificate with a longer, or shorter validity period. Alternatively, how about adding another method that hard-codes it as one hour:

CertificateBuilder oneHourValidity()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a constructor, the code would look like this

public CertificateBuilder(String subjectName,
                              PublicKey publicKey, PublicKey caKey, KeyUsage... keyUsages)
            throws CertificateException, IOException {
        factory = CertificateFactory.getInstance("X.509");
        SecureRandom random = new SecureRandom();

        boolean [] keyUsage = new boolean[KeyUsage.values().length];
        for (KeyUsage ku : keyUsages) {
            keyUsage[ku.ordinal()] = true;
        }

        this.subjectName = new X500Principal(subjectName);
        this.publicKey = Objects.requireNonNull(publicKey, "Caught null public key");
        notBefore = (Date)Date.from(Instant.now().minus(1, ChronoUnit.HOURS));
        notAfter = Date.from(Instant.now().plus(1, ChronoUnit.HOURS));
        serialNumber = BigInteger.valueOf(random.nextLong(1000000)+1);
        byte[] keyIdBytes = new KeyIdentifier(publicKey).getIdentifier();
        Extension e = new SubjectKeyIdentifierExtension(keyIdBytes);
        extensions.put(e.getId(), e);

        KeyIdentifier kid = new KeyIdentifier(caKey);
        e = new AuthorityKeyIdentifierExtension(kid, null, null);
        extensions.put(e.getId(), e);

        if (keyUsages.length != 0) {
            e = new KeyUsageExtension(keyUsage);
            extensions.put(e.getId(), e);
        }
    }

it also means I can't use this method to to create a certificate with a longer, or shorter validity period

There are methods to set the not-before and not-after fields. Any field set in this static method can be changed:

newCertificateBuilder(...).notAfter(Date.from(Instant.now().plus(10, TimeUnit.YEARS)));

I don't like using Date here and would be happy to overload it to take Instant as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a constructor, the code would look like this

Ah, I see. Well technically you could call the set methods from the ctor but you would get this-escape compiler warnings, which you probably want to avoid.

it also means I can't use this method to to create a certificate with a longer, or shorter validity period

There are methods to set the not-before and not-after fields. Any field set in this static method can be changed:

newCertificateBuilder(...).notAfter(Date.from(Instant.now().plus(10, TimeUnit.YEARS)));

I don't like using Date here and would be happy to overload it to take Instant as well.

Yes, but I don't think the static method which is generically named newCertificateBuilder should be hard-coding a one hour validity period, that detail should be either in a different method (my preference) or this method should be named more clearly like oneHourCertificateBuilder. I realize this is just a test method, but this is a nicely designed API that has been very useful so I would prefer if we keep the flexibility.


private static void setupCertificates() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be removed so implementation uses a default value.

Certificate trusedCert = cf.generateCertificate(is);
is.close();
private static SSLContext getSSLContext(X509Certificate trustedCert,
X509Certificate keyCert, KeyPair key, char[] passphrase) throws Exception {

// create a key store
KeyStore ks = KeyStore.getInstance("JKS");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest changing a key store type to PKCS12, should be a better choice in the long run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

5 participants