spring-projects / spring-vault

Provides familiar Spring abstractions for HashiCorp Vault
https://spring.io/projects/spring-vault
Apache License 2.0
283 stars 186 forks source link

Lookking for support Authentication with Client Certificate and Key #796

Closed sethvarun closed 1 year ago

sethvarun commented 1 year ago

Hi, I am getting errors while accessing the vault. I am using the provided Client Certificate(.pem) and Key (.key) files for authentication. Spring vault version (2.3.3) By: java.security.SignatureException: Signature does not match. at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:449) at sun.security.provider.certpath.BasicChecker.verifySignature(BasicChecker.java:166) at sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:147) at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:125) at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:238) at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:146) at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:85) at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292) at sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:375) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:285) at sun.security.validator.Validator.validate(Validator.java:262) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:227) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1622) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037) at sun.security.ssl.Handshaker.process_record(Handshaker.java:965) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:87) at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:109) at org.springframework.vault.core.VaultTemplate.lambda$getSessionInterceptor$1(VaultTemplate.java:255) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) at org.springframework.vault.client.RestTemplateBuilder.lambda$createTemplate$4(RestTemplateBuilder.java:239) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) at org.springframework.vault.client.VaultClients.lambda$createRestTemplate$0(VaultClients.java:122) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77) at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602) at org.springframework.vault.core.VaultKeyValue2Accessor.lambda$list$0(VaultKeyValue2Accessor.java:59) at org.springframework.vault.core.VaultKeyValueAccessor.lambda$doRead$2(VaultKeyValueAccessor.java:166) at org.springframework.vault.core.VaultTemplate.doWithSession(VaultTemplate.java:448) at org.springframework.vault.core.VaultKeyValueAccessor.doRead(VaultKeyValueAccessor.java:163) at org.springframework.vault.core.VaultKeyValue2Accessor.list(VaultKeyValue2Accessor.java:58) at

Code snippet: I am getting below exception when trying to use X.509 client certificate for accessing keys from Vault using spring-vault-code 2.3.3 aused By: java.security.SignatureException: Signature does not match. at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:449) at sun.security.provider.certpath.BasicChecker.verifySignature(BasicChecker.java:166) at sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:147) at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:125) at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:238) at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:146) at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:85) at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292) at sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:375) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:285) at sun.security.validator.Validator.validate(Validator.java:262) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:227) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1622) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037) at sun.security.ssl.Handshaker.process_record(Handshaker.java:965) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:87) at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:109) at org.springframework.vault.core.VaultTemplate.lambda$getSessionInterceptor$1(VaultTemplate.java:255) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) at org.springframework.vault.client.RestTemplateBuilder.lambda$createTemplate$4(RestTemplateBuilder.java:239) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) at org.springframework.vault.client.VaultClients.lambda$createRestTemplate$0(VaultClients.java:122) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77) at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602) at org.springframework.vault.core.VaultKeyValue2Accessor.lambda$list$0(VaultKeyValue2Accessor.java:59) at org.springframework.vault.core.VaultKeyValueAccessor.lambda$doRead$2(VaultKeyValueAccessor.java:166) at org.springframework.vault.core.VaultTemplate.doWithSession(VaultTemplate.java:448) at org.springframework.vault.core.VaultKeyValueAccessor.doRead(VaultKeyValueAccessor.java:163) at org.springframework.vault.core.VaultKeyValue2Accessor.list(VaultKeyValue2Accessor.java:58) at com.oracle.healthinsurance.credentialstore.internal.vault.VaultConnectivityUtility.populateCacheFromVault(VaultConnectivityUtility.java:51) at com.oracle.healthinsurance.credentialstore.internal.vault.VaultCredentialStore.populateCacheFromVault(VaultCredentialStore.java:121) at com.oracle.healthinsurance.credentialstore.internal.vault.VaultCredentialStore.getCredentialKeys(VaultCredentialStore.java:60) at com.oracle.healthinsurance.oauth.internal.CredentialStoreInitializer.onApplicationEvent(CredentialStoreInitializer.java:36) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:344) at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:229) at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:166) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:391) at com.oracle.healthinsurance.appstart.impl.OhiApplication.startup(OhiApplication.java:61)

Code snippet which I am using to connect/access vault @Override public VaultEndpoint vaultEndpoint() {

try {
    return VaultEndpoint.from(new URI(vaultAddress));
} catch (URISyntaxException e) {
    logger.error(e, "Unable to connect VaultEndPoint : {0}", () -> objects(vaultAddress));
    throw new RuntimeException("Error while connecting to VaultEndPoint", e);
}

}

@Override public ClientAuthentication clientAuthentication() {

return new TokenAuthentication(tokenCache.get(VAULT_TOKEN_CACHE_KEY).getToken());

}

@Override public ClientOptions clientOptions(){ return null; }

@Override public SslConfiguration sslConfiguration() {

if (isNotEmpty(trustStoreResource)) {
    // 1. JKS trust store
    return useTrustStore(trustStoreResource, resourceLoader);
} else if (isNotEmpty(keyStoreResource)) {
    // 2. JKS keystore
    return useKeyStore(keyStoreResource, keyStorePassword, resourceLoader);
} else if (isNotEmpty(pemResource)) {
    // 3. PEM
    return usePem(pemResource, resourceLoader);
} else if (isNotEmpty(clientPemResource) && isNotEmpty(clientKeyPemResource)) {
    // 4. client PEM and RSA key
    return useClientPem(clientPemResource, clientKeyPemResource, resourceLoader);
}

logger.trace("Return default SslConfiguration");
return SslConfiguration.unconfigured();

} static SslConfiguration useClientPem(final String pemLocation, final String keyPemLocation, final ResourceLoader resourceLoader) {

logger.debug("Initialize Vault using client PEM certificate");

Resource resource = loadResource(resourceLoader, pemLocation);
Resource clientResource = loadResource(resourceLoader, keyPemLocation);
if (resource.isFile()) {
    if (clientResource.isFile()) {
        logger.trace("Found resource is file type");

        return new SslConfiguration(new SslConfiguration.KeyStoreConfiguration(clientResource, null, PEM_KEYSTORE_TYPE),
                new SslConfiguration.KeyStoreConfiguration(resource, null, PEM_KEYSTORE_TYPE));
        //return SslConfiguration.create(clientResource, (char[]) null, resource, null);
        //sslConfiguration.withTrustStore(SslConfiguration.KeyStoreConfiguration.of(resource).withStoreType(PEM_KEYSTORE_TYPE)).withKeyStore(SslConfiguration.KeyStoreConfiguration.of(clientResource).withStoreType(PEM_KEYSTORE_TYPE));
         //return SslConfiguration
         //       .forTrustStore(SslConfiguration.KeyStoreConfiguration.of(resource).withStoreType(PEM_KEYSTORE_TYPE))
         //        .withKeyStore(SslConfiguration.KeyStoreConfiguration.of(clientResource).withStoreType(PEM_KEYSTORE_TYPE));
        //return sslConfiguration;
    } else {
        logger.trace("Found resource is not a file type");
        return SslConfiguration
                .forTrustStore(SslConfiguration.KeyStoreConfiguration.of(resource).withStoreType(PEM_KEYSTORE_TYPE))
                .withTrustStore(
                        SslConfiguration.KeyStoreConfiguration.of(getPEM(clientResource)).withStoreType(PEM_KEYSTORE_TYPE));
    }
} else {
    if (clientResource.isFile()) {
        logger.trace("Found resource is file type ");
        return SslConfiguration
                .forTrustStore(SslConfiguration.KeyStoreConfiguration.of(getPEM(resource)).withStoreType(PEM_KEYSTORE_TYPE))
                .withTrustStore(SslConfiguration.KeyStoreConfiguration.of(clientResource).withStoreType(PEM_KEYSTORE_TYPE));
    } else {
        logger.trace("Found resource is not a file type ");
        return SslConfiguration
                .forTrustStore(SslConfiguration.KeyStoreConfiguration.of(getPEM(resource)).withStoreType(PEM_KEYSTORE_TYPE))
                .withTrustStore(
                        SslConfiguration.KeyStoreConfiguration.of(getPEM(clientResource)).withStoreType(PEM_KEYSTORE_TYPE));
    }
}

}

For accessing vault: vaultProvider.getVaultOperations().opsForKeyValue(getSystemProperty(OHI_VAULT_KV_SECRETS_ENGINE).getValue(), VaultKeyValueOperationsSupport.KeyValueBackend.KV_2).list(path);

mp911de commented 1 year ago

If the signature and the private key do not match, then this indicates a configuration issue. It seems that you use a key that isn't compatible with the server certificate. In any case, this is an infrastructure issue that we cannot solve for you.

sethvarun commented 1 year ago

Earlier we were using Java-Vault-Driver with the same configuration and it was working fine. Later when we migrated to Spring-Vault-C0ore we found the issue. With further dive-in issue was found that Spring-Vault-Code does not have similar code as Java-Vault-Driver has. below is the code snipet.

try {
                    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                    TrustManager[] trustManagers = null;
                    clientPemUTF8 = inputStreamToUTF8(clientResource.getInputStream());
                    clientKeyPemUTF8 = inputStreamToUTF8(resource.getInputStream());
                    KeyManager[] keyManagers = null;

                    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                    ByteArrayInputStream pem = new ByteArrayInputStream(clientPemUTF8.getBytes(StandardCharsets.UTF_8));

                    X509Certificate clientCertificate;
                    try {
                        clientCertificate = (X509Certificate) certificateFactory.generateCertificate(pem);
                    } catch (Throwable var14) {
                        try {
                            pem.close();
                        } catch (Throwable var12) {
                            var14.addSuppressed(var12);
                        }

                        throw var14;
                    }

                    pem.close();
                    String strippedKey = clientKeyPemUTF8.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");
                    byte[] keyBytes = Base64.getMimeDecoder().decode(strippedKey);
                    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
                    KeyFactory var9 = KeyFactory.getInstance("RSA");
                    PrivateKey privateKey = var9.generatePrivate(pkcs8EncodedKeySpec);
                    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                    keyStore.load((InputStream) null, "password".toCharArray());
                    keyStore.setCertificateEntry("clientCert", clientCertificate);
                    keyStore.setKeyEntry("key", privateKey, "password".toCharArray(), new Certificate[]{clientCertificate});
                    keyManagerFactory.init(keyStore, "password".toCharArray());
                    keyManagers = keyManagerFactory.getKeyManagers();

                    SslConfiguration.KeyConfiguration.of("".toCharArray(), keyStore.getCertificateAlias(clientCertificate));
                    return SslConfiguration.forTrustStore(SslConfiguration.KeyStoreConfiguration.of(clientResource).withStoreType( PEM_KEYSTORE_TYPE)).forKeyStore(new FileSystemResource(System.getProperty("weblogic.security.CustomTrustKeyStoreFileName")),
                            "changeit".toCharArray(),
                            SslConfiguration.KeyConfiguration.of("password".toCharArray(),
                                    keyStore.getCertificateAlias(clientCertificate)));
                } catch (Exception e) {
                    e.printStackTrace();

                    return SslConfiguration.unconfigured();
                }
mp911de commented 1 year ago

The code above isn't Spring Vault code. You might find PemObject and CertificateBundle utils useful to parse your keys. With the KeyStore being configured on your side, there isn't much we can do here. You might want to have a look at ClientHttpConnectorFactory to see how we configure the HTTP client to verify your settings are correct.

sethvarun commented 1 year ago

Thanks for your update. Is there any documentation/example which I can follow for PemObject, CertificateBundle and ClientHttpConnectorFactory.