ctron / pem-keystore

A PKCS #1 PEM KeyStore for Java
Eclipse Public License 1.0
40 stars 10 forks source link

Problem with EC keys #11

Open kolomparrudi opened 2 years ago

kolomparrudi commented 2 years ago

Hi,

I'm trying to use pem-keystore for LDAP SASL External authentication:

Created a custom SSLSocketFamily:

 public class PemSSLSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory factory;

    public PemSSLSocketFactory() {
        super();
    }

    protected PemSSLSocketFactory(SSLSocketFactory factory) {
        this.factory = factory;
    }

    public static PemSSLSocketFactory getDefault() {
        SSLContext ctx = null;
        KeyStore keyStoreForSASL;
        KeyStore keyStoreForVerify;
        TrustManagerFactory tmf;
        KeyManagerFactory kmf;

        try (FileInputStream stream = new FileInputStream("/tmp/tls.properties")) {
            keyStoreForSASL = KeyStore.getInstance("PEMCFG", new PemKeyStoreProvider());
            keyStoreForSASL.load(stream, new char[0]);
            kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(keyStoreForSASL, new char[0]);
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException e) {
            throw new RuntimeException( e );
        }
        try (FileInputStream stream = new FileInputStream("/run/secrets/tls-int-ca-cert/cacertbundle.pem")) {
            keyStoreForVerify = KeyStore.getInstance("PEMCA", new PemKeyStoreProvider());
            keyStoreForVerify.load(stream, new char[0]);
            tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(keyStoreForVerify);
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException  e) {
            throw new RuntimeException( e );
        }
        KeyManager[] keyManagers = kmf.getKeyManagers();
        try {
            ctx = SSLContext.getInstance( "TLS" );
            ctx.init( keyManagers, tmf.getTrustManagers(), new SecureRandom() );
        } catch ( KeyManagementException e ) {
            throw new RuntimeException( e );
        } catch ( NoSuchAlgorithmException e ) {
            throw new RuntimeException( e );
        }
        return new PemSSLSocketFactory( ctx.getSocketFactory() );
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return factory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return factory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return factory.createSocket( s, host, port, autoClose );
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return factory.createSocket( host, port );
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return factory.createSocket( host, port, localHost, localPort );
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return factory.createSocket( host, port );
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return factory.createSocket( address, port, localAddress, localPort );
    }
}

Here is the LDAP connect part:

        Hashtable<String, Object> env = new Hashtable<String, Object>(11);
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://myserver:389");
        try {
            // Create initial context
            LdapContext ctx = new InitialLdapContext(env, null);
            // Start TLS
            StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
            tls.negotiate(PemSSLSocketFactory.getDefault());

            // Perform client authentication using TLS credentials. This uses default client keystore configured, check client-tls.cli
            ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "EXTERNAL");

            System.out.println(ctx.lookup("ou=people,dc=myquery"));`

/tmp/tls.properties content:

alias=ldapclient1
source.key=/run/secrets/ldap-admin-certs/cliprivkey.pem
source.cert=/run/secrets/ldap-admin-certs/clicert.pem

If I execute it with -Djavax.net.debug=all I got these errors:


javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.310 UTC|SunX509KeyManagerImpl.java:401|matching alias: ldapclient1
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.310 UTC|X509Authentication.java:242|ldapclient1 private or public key is not of EC algorithm
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.310 UTC|CertificateRequest.java:813|Unavailable authentication scheme: ecdsa_secp256r1_sha256
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.310 UTC|SunX509KeyManagerImpl.java:401|matching alias: ldapclient1
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.320 UTC|X509Authentication.java:242|ldapclient1 private or public key is not of EC algorithm
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.320 UTC|CertificateRequest.java:813|Unavailable authentication scheme: ecdsa_secp384r1_sha384
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.320 UTC|SunX509KeyManagerImpl.java:401|matching alias: ldapclient1
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.320 UTC|X509Authentication.java:242|ldapclient1 private or public key is not of EC algorithm
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.320 UTC|CertificateRequest.java:813|Unavailable authentication scheme: ecdsa_secp521r1_sha512
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.320 UTC|CertificateRequest.java:773|Unable to produce CertificateVerify for signature scheme: ed25519
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.321 UTC|CertificateRequest.java:773|Unable to produce CertificateVerify for signature scheme: ed448
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.321 UTC|X509Authentication.java:215|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.321 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pss_pss_sha256
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.321 UTC|X509Authentication.java:215|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.321 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pss_pss_sha384
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.322 UTC|X509Authentication.java:215|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.322 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pss_pss_sha512
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.322 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.322 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pss_rsae_sha256
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.322 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.322 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pss_rsae_sha384
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.322 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.323 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pss_rsae_sha512
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.323 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.323 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pkcs1_sha256
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.323 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.323 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pkcs1_sha384
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.323 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.323 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pkcs1_sha512
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.324 UTC|SunX509KeyManagerImpl.java:401|matching alias: ldapclient1
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.324 UTC|X509Authentication.java:242|ldapclient1 private or public key is not of EC algorithm
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.324 UTC|CertificateRequest.java:813|Unavailable authentication scheme: ecdsa_sha224
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.324 UTC|SunX509KeyManagerImpl.java:401|matching alias: ldapclient1
javax.net.ssl|FINE|01|main|2022-05-20 11:13:31.324 UTC|X509Authentication.java:242|ldapclient1 private or public key is not of EC algorithm
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.324 UTC|CertificateRequest.java:813|Unavailable authentication scheme: ecdsa_sha1
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.324 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.324 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_sha224
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.325 UTC|X509Authentication.java:215|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.325 UTC|CertificateRequest.java:813|Unavailable authentication scheme: rsa_pkcs1_sha1
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.325 UTC|X509Authentication.java:215|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.325 UTC|CertificateRequest.java:813|Unavailable authentication scheme: dsa_sha224
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.325 UTC|X509Authentication.java:215|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.325 UTC|CertificateRequest.java:813|Unavailable authentication scheme: dsa_sha1
javax.net.ssl|ALL|01|main|2022-05-20 11:13:31.325 UTC|X509Authentication.java:215|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.325 UTC|CertificateRequest.java:813|Unavailable authentication scheme: dsa_sha256
javax.net.ssl|WARNING|01|main|2022-05-20 11:13:31.326 UTC|CertificateRequest.java:823|No available authentication scheme
...
javax.net.ssl|FINE|01|main|2022-05-20 12:07:27.344 UTC|CertificateMessage.java:328|Produced client Certificate handshake message (
"Certificates": <empty list>
)

If I convert the certificate and key to jks and using the default SSLContextFactory than it is working.

The certificate is signed with ecdsa-with-SHA256.

Do you have any idea, what could be the problem?

Thanks

ctron commented 2 years ago

Hm, not really. It looks good, except for the fact that it doesn't work.

I am not sure though I ever tested this with ECs. So that might be an issue.

I would recommend (and really appreciate it) it you could create a test for EC to the existing tests. That would most likely fail, but give us a reproducer and a future test once this is fixed.

kolomparrudi commented 2 years ago

Hi,

You are right, the problem is with the EC keys. When I print the keyStoreForSASL.getKey(...).getAlgorithm() I got ECDSA If I load it from JKS keystore I got EC.

Found similar issue and they found the problem in Bouncy Castle: https://stackoverflow.com/questions/27743045/ec-private-key-recovery-from-pem-format-with-bouncycastle

Thanks