Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
java: [11, 17, 21]
name: "Java ${{ matrix.java }} build"
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM --platform=$BUILDPLATFORM debian:bullseye-slim AS project-build
# Install build dependencies
RUN \
apt-get update && \
apt-get install -y --no-install-recommends openjdk-17-jdk maven unzip chromium git && \
apt-get install -y --no-install-recommends openjdk-21-jdk maven unzip chromium git && \
# Workaround Chromium binary path for arm64 (see https://github.com/puppeteer/puppeteer/blob/v4.0.0/src/Launcher.ts#L110)
ln -s /usr/bin/chromium /usr/bin/chromium-browser

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,21 @@
* $Id: AMCRLStore.java,v 1.7 2009/01/28 05:35:12 ww203982 Exp $
*
* Portions Copyrighted 2013-2016 ForgeRock AS.
* Portions Copyrighted 2025 Wren Security
*/
package com.sun.identity.security.cert;

import static org.forgerock.openam.utils.Time.*;

import com.forgerock.opendj.ldap.controls.TransactionIdControl;
import com.iplanet.security.x509.CertUtils;
import com.iplanet.security.x509.IssuingDistributionPointExtension;
import com.sun.identity.common.HttpURLConnectionManager;
import com.sun.identity.shared.encode.URLEncDec;
import sun.security.x509.CRLDistributionPointsExtension;
import sun.security.x509.DistributionPoint;
import sun.security.x509.DistributionPointName;
import sun.security.x509.GeneralNames;
import sun.security.x509.IssuingDistributionPointExtension;
import sun.security.x509.PKIXExtensions;
import sun.security.x509.X509CertImpl;

Expand Down Expand Up @@ -346,7 +348,7 @@ private IssuingDistributionPointExtension getCRLIDPExt(X509CRL crl) {
crl.getExtensionValue(
PKIXExtensions.IssuingDistributionPoint_Id.toString());
if (ext != null) {
idpExt = new IssuingDistributionPointExtension(ext);
idpExt = new IssuingDistributionPointExtension(true, ext);
}
} catch (Exception e) {
debug.error("Error finding CRL distribution Point configured: ", e);
Expand All @@ -371,10 +373,10 @@ private IssuingDistributionPointExtension getCRLIDPExt(X509CRL crl) {

List dps = null;
try {
dps = (List) dpExt.get(CRLDistributionPointsExtension.POINTS);
} catch (IOException ioex) {
dps = SunSecurityProviderCompat.getDistributionPoints(dpExt);
} catch (Exception ex) {
if (debug.warningEnabled()) {
debug.warning("AMCRLStore.getUpdateCRLFromCrlDP: ", ioex);
debug.warning("AMCRLStore.getUpdateCRLFromCrlDP: ", ex);
}
}

Expand Down Expand Up @@ -415,8 +417,17 @@ private IssuingDistributionPointExtension getCRLIDPExt(X509CRL crl) {
* @param idpExt
*/
private synchronized X509CRL getUpdateCRLFromCrlIDP(IssuingDistributionPointExtension idpExt) {
GeneralNames gName = null;
try {
DistributionPointName dpName = SunSecurityProviderCompat.getDistributionPoint(idpExt);
if (dpName != null) {
gName = dpName.getFullName();
}
} catch (Exception e) {
debug.error("Error getting distribution point name", e);
return null;
}

GeneralNames gName = idpExt.getFullName();
if (gName == null) {
return null;
}
Expand Down Expand Up @@ -573,7 +584,7 @@ private byte[] getCRLByLdapURI(String uri) {

SearchResultEntry entry = results.readEntry();

/*
/*
* Retrieve the certificate revocation list if available.
*/
Attribute crlAttribute = entry.getAttribute(CERTIFICATE_REVOCATION_LIST);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.1.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.1.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright Wren Security 2025
*/
package com.sun.identity.security.cert;

import java.lang.reflect.Method;
import java.util.List;

import sun.security.x509.CRLDistributionPointsExtension;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.DistributionPoint;
import sun.security.x509.DistributionPointName;
import sun.security.x509.GeneralNames;
import sun.security.x509.IssuingDistributionPointExtension;
import sun.security.x509.SubjectAlternativeNameExtension;
import sun.security.x509.X509CertInfo;

/**
* Utility methods for maintaining compatibility with older supported JDK versions.
*
* <p>
* This class exists only because we are using JDK internals that are not guaranteed being stable across releases.
* Ideally the whole X.509 code that relies on <code>sun.security.x509</code> should be replaced. Using reflection
* gets rid of build time type checking and makes the codebase less future-proof.
*
* <p>
* Issues solved by this class:
* <ul>
* <li> binary incompatibility introduced by https://bugs.openjdk.org/browse/JDK-8296072
*/
@SuppressWarnings({ "restriction", "unchecked" })
public final class SunSecurityProviderCompat {

public static List<DistributionPoint> getDistributionPoints(CRLDistributionPointsExtension extension) throws Exception {
try {
Method typesafeGetter = CRLDistributionPointsExtension.class.getMethod("getDistributionPoints");
return (List<DistributionPoint>) typesafeGetter.invoke(extension);
} catch (NoSuchMethodException e) { /* fall through */ }
try {
Method legacyGetter = CRLDistributionPointsExtension.class.getMethod("get", String.class);
return (List<DistributionPoint>) legacyGetter.invoke(extension, "points");
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Unsupported SUN provider implementation", e);
}
}

public static DistributionPointName getDistributionPoint(IssuingDistributionPointExtension extension) throws Exception {
try {
Method typesafeGetter = IssuingDistributionPointExtension.class.getMethod("getDistributionPoint");
return (DistributionPointName) typesafeGetter.invoke(extension);
} catch (NoSuchMethodException e) { /* fall through */ }
try {
Method legacyGetter = IssuingDistributionPointExtension.class.getMethod("get", String.class);
return (DistributionPointName) legacyGetter.invoke(extension, "point");
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Unsupported SUN provider implementation", e);
}
}

public static CertificateExtensions getExtensions(X509CertInfo certInfo) throws Exception {
try {
Method typesafeGetter = X509CertInfo.class.getMethod("getExtensions");
return (CertificateExtensions) typesafeGetter.invoke(certInfo);
} catch (NoSuchMethodException e) { /* fall through */ }
try {
Method legacyGetter = X509CertInfo.class.getMethod("get", String.class);
return (CertificateExtensions) legacyGetter.invoke(certInfo, X509CertInfo.EXTENSIONS);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Unsupported SUN provider implementation", e);
}
}

public static SubjectAlternativeNameExtension getSanExtension(CertificateExtensions extensions) throws Exception {
try {
Method typesafeGetter = CertificateExtensions.class.getMethod("getExtension", String.class);
return (SubjectAlternativeNameExtension) typesafeGetter.invoke(extensions, SubjectAlternativeNameExtension.NAME);
} catch (NoSuchMethodException e) { /* fall through */ }
try {
Method getter = CertificateExtensions.class.getMethod("get", String.class);
return (SubjectAlternativeNameExtension) getter.invoke(extensions, SubjectAlternativeNameExtension.NAME);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Unsupported SUN provider implementation", e);
}
}

public static GeneralNames getSubjectAlternativeNames(SubjectAlternativeNameExtension extension) throws Exception {
try {
Method typesafeGetter = SubjectAlternativeNameExtension.class.getMethod("getNames");
return (GeneralNames) typesafeGetter.invoke(extension);
} catch (NoSuchMethodException e) { /* fall through */ }
try {
Method legacyGetter = SubjectAlternativeNameExtension.class.getMethod("get", String.class);
return (GeneralNames) legacyGetter.invoke(extension, "subject_name");
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Unsupported SUN provider implementation", e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.sun.identity.security.cert;

import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;

import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.util.List;
import org.testng.annotations.Test;
import sun.security.x509.CRLDistributionPointsExtension;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.DistributionPoint;
import sun.security.x509.DistributionPointName;
import sun.security.x509.GeneralNames;
import sun.security.x509.IssuingDistributionPointExtension;
import sun.security.x509.SubjectAlternativeNameExtension;
import sun.security.x509.X509CRLImpl;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

/**
* {@link SunSecurityProviderCompat} test cases.
*/
@SuppressWarnings("restriction")
public class SunSecurityProviderCompatTest {

@Test
public void testSubjectAlternativeNameExtraction() throws Exception {
X509CertImpl cert = readCertificate("/compat/user.cert.pem");
assertNotNull(cert);

X509CertInfo certInfo = new X509CertInfo(cert.getTBSCertificate());
assertNotNull(certInfo);

CertificateExtensions extensions = SunSecurityProviderCompat.getExtensions(certInfo);
assertNotNull(extensions);

SubjectAlternativeNameExtension sanExtension = SunSecurityProviderCompat.getSanExtension(extensions);
assertNotNull(sanExtension);

GeneralNames sanValues = SunSecurityProviderCompat.getSubjectAlternativeNames(sanExtension);
assertNotNull(sanValues);
assertFalse(sanValues.names().isEmpty());
}

@Test
public void testDistributionPointExtraction() throws Exception {
X509CertImpl cert = readCertificate("/compat/user.cert.pem");
assertNotNull(cert);

CRLDistributionPointsExtension crlDistributionPointsExtension = cert.getCRLDistributionPointsExtension();
assertNotNull(crlDistributionPointsExtension);

List<DistributionPoint> distributionPoints = SunSecurityProviderCompat
.getDistributionPoints(crlDistributionPointsExtension);
assertFalse(distributionPoints.isEmpty());
}

@Test
public void testRevocationListIssuerExtraction() throws Exception {
X509CRLImpl crl = readCertificateRevocationList("/compat/sample.crl.pem");
assertNotNull(crl);

IssuingDistributionPointExtension issuingDistributionPointExtension = crl.getIssuingDistributionPointExtension();
assertNotNull(issuingDistributionPointExtension);

DistributionPointName distributionPoint = SunSecurityProviderCompat.getDistributionPoint(issuingDistributionPointExtension);
assertNotNull(distributionPoint);
}

private X509CertImpl readCertificate(String path) throws Exception {
try (InputStream input = SunSecurityProviderCompatTest.class.getResourceAsStream(path)) {
return new X509CertImpl(input);
}
}

private X509CRLImpl readCertificateRevocationList(String path) throws Exception {
try (InputStream input = SunSecurityProviderCompatTest.class.getResourceAsStream(path)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509CRLImpl) certificateFactory.generateCRL(input);
}
}

}
8 changes: 8 additions & 0 deletions openam-certs/src/test/resources/compat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Only used when issuing new certificate for updating the database
sample-ca/newcerts

# Not interested in storing old replaced state
sample-ca/*.old

# Not interested in storing temporary CSR files
*.csr.pem
44 changes: 44 additions & 0 deletions openam-certs/src/test/resources/compat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Sample X509 files

The following commands were used to generate files in this folder:

```sh
# Generate CA certificate
openssl genrsa -out sample-ca/ca.key.pem 2048
openssl req -x509 -new -key sample-ca/ca.key.pem -out sample-ca/ca.cert.pem -days 36500 -subj "/CN=Sample CA"

# Generate user certificate
openssl genrsa -out user.key.pem 2048
openssl req -new -key user.key.pem -out user.csr.pem -subj '/CN=john.smith' -addext "subjectAltName=otherName:1.3.6.1.4.1.311.20.2.3;UTF8:john.smith@example.com"
openssl ca -batch -config sample-ca/openssl.cnf -notext -in user.csr.pem -out user.cert.pem

# Generate revoked certificate
openssl genrsa -out revoked.key.pem 2048
openssl req -new -key revoked.key.pem -out revoked.csr.pem -subj '/CN=jane.smith' -addext "subjectAltName=otherName:1.3.6.1.4.1.311.20.2.3;UTF8:jane.smith@example.com"
openssl ca -batch -config sample-ca/openssl.cnf -notext -in revoked.csr.pem -out revoked.cert.pem
openssl ca -config sample-ca/openssl.cnf -revoke revoked.cert.pem

# Generate revocation list
openssl ca -config sample-ca/openssl.cnf -gencrl -out sample.crl.pem
```

Removing generated content:

```sh
# Remove CA data
rm -f sample-ca/newcerts/*
echo 0 > sample-ca/crlnumber
echo -n "" > sample-ca/index.txt

# Remove user certificate
rm user.key.pem user.key.csr user.cert.pem

# Remove revoked certificate
rm revoked.key.pem revoked.key.csr revoked.cert.pem

# Remove revocation list
rm sample.crl.pem

# Remove CA certificate
rm sample-ca/ca.cert.pem sample-ca/ca.key.pem
```
21 changes: 21 additions & 0 deletions openam-certs/src/test/resources/compat/revoked.cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDijCCAnKgAwIBAgIUKlalqDZrty8Ez0n91YAHxeUWsK0wDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJU2FtcGxlIENBMCAXDTI1MDcyNTEwMzUwMVoYDzIxMjUw
NzAxMTAzNTAxWjAVMRMwEQYDVQQDDApqYW5lLnNtaXRoMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA0bwfXWJ9SQ8QpgNNbYkKLL6dJ9TzQwwjBZyOJxzt
GI72F2FGQXupGDLMfH8cbAzp87zTlrEYAa35sSVJqktWZu7NlwBvRACFYckpPVrM
QsLdpKrLvlnW9FGrlSpZoe73fyGmXVJLtHmiV7z9zNnyyncMTKVsv4ssoSj6CWmI
wzT1JQc8b0/DpoNmPZxjnMMsec6UDXXMOZao5qGRHlTDtvJpDYYsBq7rrTmeU+or
jLtGtGN69g7kTSuA4wRg5JGIZguIPCano+gYYdnLS4uU+aFD9S8nB3JgliWuu7/D
uAJ+H/VpZ0KJC69fJ+u7mkd32bZMxtTAtg45ey7eMW21iQIDAQABo4HQMIHNMB8G
A1UdIwQYMBaAFCNhj+GfqAkE7geJWmfguP+d79hGMAkGA1UdEwQCMAAwKwYDVR0f
BCQwIjAgoB6gHIYaaHR0cDovL2V4YW1wbGUuY29tL2NybC5wZW0wEwYDVR0lBAww
CgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMDEGA1UdEQQqMCigJgYKKwYBBAGCNxQC
A6AYDBZqYW5lLnNtaXRoQGV4YW1wbGUuY29tMB0GA1UdDgQWBBR1Ki82NwPhD/Fz
w3YyeocwwIOvfDANBgkqhkiG9w0BAQsFAAOCAQEAjgEj2lifJuKFR1YOEOcb0FLE
5geOS9zKfdOC7seJRTX7DLMNPf/ACKtiilu8ULyVGo6ckI6SrAi4ZNAKgPkI3Dij
eOT9nDH65JD0AkTzAA8bumqoU7+ccBDvywKhNPBy2P/R6rh+dPkQ2GdZjv98Ewnj
FUeiydepRn2K+BgAjfOWmXnGUgwi7gOGRrjdxaT+urnvVGfpUDPqtWGmF7iTAt/p
YsYwG7XKA3EWoKDb11C2ifva8uY192RrrJA+SomXkkEAD9CXZWwMdbD0COyAVYek
HO3xlX4VzRpkGSc/jbl0Zw62bnFR8kSPwEK7dJ45FvPHBX9nXcTeEnMJUzhGyg==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions openam-certs/src/test/resources/compat/revoked.key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRvB9dYn1JDxCm
A01tiQosvp0n1PNDDCMFnI4nHO0YjvYXYUZBe6kYMsx8fxxsDOnzvNOWsRgBrfmx
JUmqS1Zm7s2XAG9EAIVhySk9WsxCwt2kqsu+Wdb0UauVKlmh7vd/IaZdUku0eaJX
vP3M2fLKdwxMpWy/iyyhKPoJaYjDNPUlBzxvT8Omg2Y9nGOcwyx5zpQNdcw5lqjm
oZEeVMO28mkNhiwGruutOZ5T6iuMu0a0Y3r2DuRNK4DjBGDkkYhmC4g8Jqej6Bhh
2ctLi5T5oUP1LycHcmCWJa67v8O4An4f9WlnQokLr18n67uaR3fZtkzG1MC2Djl7
Lt4xbbWJAgMBAAECggEAD/yieWhN+3kqADWFO+GzxFhdO1GIn2Y37zARR9r1Gaeg
C4UvKUOnToBkxOKdhIDxflI44KYsGcisnTul41Stx9fRFP8D/C97+0mtmo8mvboD
0g7wy9gmQeZNyWd6gJTqCadDep6Qxbd1z/FIeUptrtAnQFplsS4HH1uzv27rbVGj
OrnVRVOghn/3rl/9LQgQb8Pno2ZOwUq8viPiuaWrFfrBynV1WwpvvqCJdJablFus
fqZRXJ2c6P/ZNeckmWZ4M50dGeLSfjkOKw9XsRpzDWMh5c9GUM2BqGJA9VJ1ca9i
4Pl7gFs6rf2KDw5aFJ13eJ8dxcB5dBskPe7H/ZJr7wKBgQDwW6mbsVclimVy5Ccl
aSRhe/7sI/Jiby87nCKkVh6sm2Dyj+Tcsiw1FJowP87est7idQfEeoWKGx3lUbB9
f8l+ITNtwDJSng2NO/Qz/+2AXzEu75CHOhMh+QzYuF7xFaCkyHnKnctu8fAGD6UH
rhoxrLkipcbIM86r8yWJwD1x2wKBgQDfYkfs4rhJIyNZB6KELVsfwuLgzi0h6bCu
dOvHUcl2UL5+jQrcXAdf9htdUIxXLcoAdGhYKuBd7kRT6ahrKwYEXxDmFcYJlhVa
oz6KltBe3gWhGy93CC+jkvr+Q1z9xrLkNonrTkH7xvlHfKVzLL6ajEbPiGE1pCsW
e5uqxhUNawKBgBXTTPhtRwuKoKGpJADapkoP11sb/IOBsxlHmUGw7EIiLdB4zoBX
0XnUcBfXg3JnbaPEmrr1oTCkO7e6DjunIeXJIAFkRW2JGpPrkMY0BB33BuFLMaWF
2XzpP4hiXYSowRiVd7G1WGavo2r5erPS1GAUXg9OXFmLksW8Y3k5spqnAoGBAIgz
bXVi+0gks426GP4MhY4FDr5RF7Wgvghw473BAVwxeSTCLIgVWK3K6f5oeVlCYvMK
BwETC4BaIbEkO3s0XVPW/v+68Oexac280QpBUEU3jCkh4TvrctiCaqUTP6TAPRzm
oAsnyRWRyTYsKtjhxEmJFDe/iL3jHh50OYLTicyHAoGAYNjsURdYfUeGB8EETs/0
hWN2hr9690Mj7vP/CDe36kOwTPG53KpFOFlO63EgSHe2FNYjJB2pt+aBNvb8P6dn
K7dMQ09tgyG3RWUyL6DIsp8TpaWJgoHLcP2QivRiEF4m1MSMSaAGH8+sy9slJ9mx
WqanIB5RIkf+x77cJau7Iak=
-----END PRIVATE KEY-----
Loading