|
@@ -27,6 +27,7 @@ import password.pwm.PwmApplication;
|
|
|
import password.pwm.PwmConstants;
|
|
|
import password.pwm.bean.SessionLabel;
|
|
|
import password.pwm.config.Configuration;
|
|
|
+import password.pwm.config.option.CertificateMatchingMode;
|
|
|
import password.pwm.error.ErrorInformation;
|
|
|
import password.pwm.error.PwmError;
|
|
|
import password.pwm.error.PwmException;
|
|
@@ -68,23 +69,27 @@ public abstract class X509Utils
|
|
|
{
|
|
|
private static final PwmLogger LOGGER = PwmLogger.forClass( X509Utils.class );
|
|
|
|
|
|
- public static List<X509Certificate> readRemoteCertificates( final URI uri )
|
|
|
+ public static List<X509Certificate> readRemoteCertificates(
|
|
|
+ final URI uri,
|
|
|
+ final Configuration configuration
|
|
|
+ )
|
|
|
throws PwmOperationalException
|
|
|
{
|
|
|
final String host = uri.getHost();
|
|
|
final int port = PwmURL.portForUriSchema( uri );
|
|
|
|
|
|
- return readRemoteCertificates( host, port );
|
|
|
+ return readRemoteCertificates( host, port, configuration );
|
|
|
}
|
|
|
|
|
|
public static List<X509Certificate> readRemoteHttpCertificates(
|
|
|
final PwmApplication pwmApplication,
|
|
|
final SessionLabel sessionLabel,
|
|
|
- final URI uri
|
|
|
+ final URI uri,
|
|
|
+ final Configuration configuration
|
|
|
)
|
|
|
throws PwmUnrecoverableException
|
|
|
{
|
|
|
- final CertReaderTrustManager certReaderTrustManager = new CertReaderTrustManager();
|
|
|
+ final CertReaderTrustManager certReaderTrustManager = new CertReaderTrustManager( readCertificateFlagsFromConfig( configuration ) );
|
|
|
final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
|
|
|
.trustManager( certReaderTrustManager )
|
|
|
.build();
|
|
@@ -105,7 +110,7 @@ public abstract class X509Utils
|
|
|
|
|
|
if ( certReaderTrustManager.getCertificates() != null )
|
|
|
{
|
|
|
- return Arrays.asList( certReaderTrustManager.getCertificates() );
|
|
|
+ return certReaderTrustManager.getCertificates();
|
|
|
}
|
|
|
|
|
|
{
|
|
@@ -122,11 +127,15 @@ public abstract class X509Utils
|
|
|
throw new PwmUnrecoverableException( requestError );
|
|
|
}
|
|
|
|
|
|
- public static List<X509Certificate> readRemoteCertificates( final String host, final int port )
|
|
|
+ public static List<X509Certificate> readRemoteCertificates(
|
|
|
+ final String host,
|
|
|
+ final int port,
|
|
|
+ final Configuration configuration
|
|
|
+ )
|
|
|
throws PwmOperationalException
|
|
|
{
|
|
|
LOGGER.debug( () -> "ServerCertReader: beginning certificate read procedure to import certificates from host=" + host + ", port=" + port );
|
|
|
- final CertReaderTrustManager certReaderTm = new CertReaderTrustManager();
|
|
|
+ final CertReaderTrustManager certReaderTm = new CertReaderTrustManager( readCertificateFlagsFromConfig( configuration ) );
|
|
|
try
|
|
|
{
|
|
|
// use custom trust manager to read the certificates
|
|
@@ -164,8 +173,8 @@ public abstract class X509Utils
|
|
|
);
|
|
|
throw new PwmOperationalException( errorInformation );
|
|
|
}
|
|
|
- final X509Certificate[] certs = certReaderTm.getCertificates();
|
|
|
- if ( certs == null )
|
|
|
+ final List<X509Certificate> certs = certReaderTm.getCertificates();
|
|
|
+ if ( JavaHelper.isEmpty( certs ) )
|
|
|
{
|
|
|
LOGGER.debug( () -> "ServerCertReader: unable to read certificates: null returned from CertReaderTrustManager.getCertificates()" );
|
|
|
}
|
|
@@ -177,7 +186,15 @@ public abstract class X509Utils
|
|
|
}
|
|
|
}
|
|
|
LOGGER.debug( () -> "ServerCertReader: process completed" );
|
|
|
- return certs == null ? Collections.emptyList() : Arrays.asList( certs );
|
|
|
+ return certs == null ? Collections.emptyList() : certs;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static ReadCertificateFlag[] readCertificateFlagsFromConfig( final Configuration configuration )
|
|
|
+ {
|
|
|
+ final CertificateMatchingMode mode = configuration.readCertificateMatchingMode();
|
|
|
+ return mode == CertificateMatchingMode.CA_ONLY
|
|
|
+ ? Collections.singletonList( X509Utils.ReadCertificateFlag.ReadOnlyRootCA ).toArray( new X509Utils.ReadCertificateFlag[0] )
|
|
|
+ : new X509Utils.ReadCertificateFlag[0];
|
|
|
}
|
|
|
|
|
|
public static boolean testIfLdapServerCertsInDefaultKeystore( final URI serverURI )
|
|
@@ -211,8 +228,15 @@ public abstract class X509Utils
|
|
|
|
|
|
private static class CertReaderTrustManager implements X509TrustManager
|
|
|
{
|
|
|
+ private final ReadCertificateFlag[] readCertificateFlags;
|
|
|
+
|
|
|
private X509Certificate[] certificates;
|
|
|
|
|
|
+ CertReaderTrustManager( final ReadCertificateFlag[] readCertificateFlags )
|
|
|
+ {
|
|
|
+ this.readCertificateFlags = readCertificateFlags;
|
|
|
+ }
|
|
|
+
|
|
|
public void checkClientTrusted( final X509Certificate[] chain, final String authType )
|
|
|
throws CertificateException
|
|
|
{
|
|
@@ -233,9 +257,13 @@ public abstract class X509Utils
|
|
|
+ JsonUtil.serialize( new ArrayList<>( certDebugInfo ) ) );
|
|
|
}
|
|
|
|
|
|
- public X509Certificate[] getCertificates( )
|
|
|
+ public List<X509Certificate> getCertificates( )
|
|
|
{
|
|
|
- return certificates;
|
|
|
+ if ( JavaHelper.enumArrayContainsValue( readCertificateFlags, ReadCertificateFlag.ReadOnlyRootCA ) )
|
|
|
+ {
|
|
|
+ return Collections.unmodifiableList( identifyRootCACertificate( Arrays.asList( certificates ) ) );
|
|
|
+ }
|
|
|
+ return Collections.unmodifiableList( Arrays.asList( certificates ) );
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -278,13 +306,15 @@ public abstract class X509Utils
|
|
|
|
|
|
public static class CertMatchingTrustManager implements X509TrustManager
|
|
|
{
|
|
|
- final List<X509Certificate> certificates;
|
|
|
+ final List<X509Certificate> trustedCertificates;
|
|
|
final boolean validateTimestamps;
|
|
|
+ final CertificateMatchingMode certificateMatchingMode;
|
|
|
|
|
|
- public CertMatchingTrustManager( final Configuration config, final List<X509Certificate> certificates )
|
|
|
+ public CertMatchingTrustManager( final Configuration config, final List<X509Certificate> trustedCertificates )
|
|
|
{
|
|
|
- this.certificates = new ArrayList<>( certificates );
|
|
|
+ this.trustedCertificates = new ArrayList<>( trustedCertificates );
|
|
|
validateTimestamps = config != null && Boolean.parseBoolean( config.readAppProperty( AppProperty.SECURITY_CERTIFICATES_VALIDATE_TIMESTAMPS ) );
|
|
|
+ certificateMatchingMode = config.readCertificateMatchingMode();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -301,10 +331,46 @@ public abstract class X509Utils
|
|
|
throw new CertificateException( errorMsg );
|
|
|
}
|
|
|
|
|
|
- for ( final X509Certificate loopCert : x509Certificates )
|
|
|
+ switch ( certificateMatchingMode )
|
|
|
+ {
|
|
|
+ case CERTIFICATE_CHAIN:
|
|
|
+ {
|
|
|
+ doValidation( trustedCertificates, Arrays.asList( x509Certificates ), validateTimestamps );
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case CA_ONLY:
|
|
|
+ {
|
|
|
+ final List<X509Certificate> trustedRootCA = X509Utils.identifyRootCACertificate( trustedCertificates );
|
|
|
+ if ( trustedRootCA.isEmpty() )
|
|
|
+ {
|
|
|
+ final String errorMsg = "no root CA certificates in configuration trust store for this operation";
|
|
|
+ throw new CertificateException( errorMsg );
|
|
|
+ }
|
|
|
+ doValidation(
|
|
|
+ trustedRootCA,
|
|
|
+ X509Utils.identifyRootCACertificate( Arrays.asList( x509Certificates ) ),
|
|
|
+ validateTimestamps
|
|
|
+ );
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ JavaHelper.unhandledSwitchStatement( certificateMatchingMode );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void doValidation(
|
|
|
+ final List<X509Certificate> trustedCertificates,
|
|
|
+ final List<X509Certificate> certificates,
|
|
|
+ final boolean validateTimestamps
|
|
|
+ )
|
|
|
+ throws CertificateException
|
|
|
+ {
|
|
|
+ for ( final X509Certificate loopCert : certificates )
|
|
|
{
|
|
|
boolean certTrusted = false;
|
|
|
- for ( final X509Certificate storedCert : certificates )
|
|
|
+ for ( final X509Certificate storedCert : trustedCertificates )
|
|
|
{
|
|
|
if ( loopCert.equals( storedCert ) )
|
|
|
{
|
|
@@ -372,6 +438,11 @@ public abstract class X509Utils
|
|
|
detail,
|
|
|
}
|
|
|
|
|
|
+ public enum ReadCertificateFlag
|
|
|
+ {
|
|
|
+ ReadOnlyRootCA
|
|
|
+ }
|
|
|
+
|
|
|
public enum DebugInfoFlag
|
|
|
{
|
|
|
IncludeCertificateDetail
|
|
@@ -443,4 +514,21 @@ public abstract class X509Utils
|
|
|
{
|
|
|
return StringUtil.base64Encode( certificate.getEncoded() );
|
|
|
}
|
|
|
+
|
|
|
+ private static List<X509Certificate> identifyRootCACertificate( final List<X509Certificate> certificates )
|
|
|
+ {
|
|
|
+ final int keyCertSignBitPosition = 5;
|
|
|
+ for ( final X509Certificate certificate : certificates )
|
|
|
+ {
|
|
|
+ final boolean[] keyUsages = certificate.getKeyUsage();
|
|
|
+ if ( keyUsages.length > keyCertSignBitPosition - 1 )
|
|
|
+ {
|
|
|
+ if ( keyUsages[keyCertSignBitPosition] )
|
|
|
+ {
|
|
|
+ return Collections.singletonList( certificate );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
}
|