Springboot SLL API consume - java

I am trying to consume an API from a 3rd party server. The 3rd party sent me an SSL certificated named certificate.p12 which is the cert file which I use to do the handshake. I have created a custom RestTemplate with SSL as follows:
#Configuration
public class CustomRestTemplate {
private static final String PASSWORD = "fake_password";
private static final String RESOURCE_PATH = "keystore/certificate.p12";
private static final String KEY_TYPE = "PKCS12";
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
char[] password = PASSWORD.toCharArray();
SSLContext sslContext = SSLContextBuilder
.create()
// .loadKeyMaterial(keyStore(RESOURCE_PATH, password), password)
.loadKeyMaterial(keyStore(getClass().getClassLoader().getResource(RESOURCE_PATH).getFile(), password), password)
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build();
HttpClient client = HttpClients
.custom()
.setSSLContext(sslContext)
.build();
return builder
.requestFactory(() -> new HttpComponentsClientHttpRequestFactory(client))
.build();
}
private KeyStore keyStore(String file, char[] password) throws Exception {
KeyStore keyStore = KeyStore.getInstance(KEY_TYPE);
File key = ResourceUtils.getFile(file);
try (InputStream in = new FileInputStream(key)) {
keyStore.load(in, password);
}
return keyStore;
}
}
I then call the endpoint using the following code:
#Component
#Service
public class TransactionService implements TransactionInterface {
#Autowired
private CustomRestTemplate restTemplate = new CustomRestTemplate();
private static final String BASE_URL = "https://41.x.x.x:xxxx/";
#Override
public List<Transaction> getUnsentTransactions(int connectionId) throws Exception {
HttpEntity<?> httpEntity = new HttpEntity<>(null, new HttpHeaders());
ResponseEntity<Transaction[]> resp = restTemplate
.restTemplate(new RestTemplateBuilder())
.exchange(BASE_URL + "path/end_point/" + connectionId, HttpMethod.GET, httpEntity, Transaction[].class);
return Arrays.asList(resp.getBody());
}
}
I get an the following stacktrace when trying to consume the api:
org.springframework.web.client.ResourceAccessException: I/O error on GET request for \"https://41.x.x.x:xxxx/path/endpoint/parameters\": Certificate for <41.x.x.x> doesn't match any of the subject alternative names: [some_name_here, some_name_here];
I do not have much experience with TLS or SSL certificates. I am really stuck at the moment and hoping I can get some help here. The 3rd party provided me with a testing site where I can test the endpoints and after importing the certificate.p12 file into my browser I can reach the endpoints using their testing site but my Springboot application still does not reach the endpoint.
Do I need to copy the cert into a specific folder? This does not seem like the case because I get a FileNotFoundException if I change the path or filename and I get a password incorrect error if I enter the wrong password for the certificate.p12 file. I tried using Postman to test it but Postman returns the same stacktrace as my web application.
Looking at the information above, am I missing something? Is the keystore not being created during runtime? Do I need to bind the certificate to the JVM or my outgoing request?
Any help would be appreciated.
Thanks

It looks like you are trying to connect to a server which doesn't have a valid name in the certificate. For example, if you are connecting to "stackoverflow.com", the certificate needs that domain in the "subject" or the "subject alternative names" field.
Even a testing site should have a valid certificate, but if that's not possible (as it's a third party site and you can't change it yourself), you can disable the verification using this question
Of course, this should only be done for testing.

Related

How to get the attached self sign certificate in the server spring boot application

my client side i have attached the certificate in the restTemplate code below . using this rest template i am calling other API(server side) . how to get the certificate in that server side
#Bean(name="custRest")
#Primary
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
char[] password = "changeit".toCharArray();
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = SSLContextBuilder
.create().loadKeyMaterial(new File("C:\\java\\java-certificate.der"),null,null)
.loadTrustMaterial(ResourceUtils.getFile("C:\\java\\java-certificate.der"), null,
acceptingTrustStrategy)
.build();
HttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
return builder.requestFactory(new HttpComponentsClientHttpRequestFactory(client))
.build();
}
You can find all step for configuring HTTPS for Spring Boot in article:
https://www.baeldung.com/spring-boot-https-self-signed-certificate

How to hit Secure Elastic Search through Java High Level Rest Client

I'm new to Elastic search. Integrated my Spring boot application with Elastic search through Java High Level Rest Client.
I've configured JHLRC bean as below and it worked fine:
#Bean(destroyMethod = "close")
public RestHighLevelClient client() {
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
return client;
}
Started exploring the security for Elasticsearch, after setup certificate and passwords, I've enabled security by providing below properties :
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
I'm able to login in kibana by using a created username and password but getting 401 Unauthorized while hitting any Elastic search API through JHLRC.
Can someone please help me on what further changes I've to make while configuring Java High Level Rest Client to hit secure Elastic search?
It worked after making below changes in JHLRC:
#Bean(destroyMethod = "close")
public RestHighLevelClient client() {
final BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
basicCredentialsProvider
.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "password_generated_by_elastic_search"));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http"))
.setHttpClientConfigCallback(new HttpClientConfigCallback() {
#Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
httpClientBuilder.disableAuthCaching();
return httpClientBuilder.setDefaultCredentialsProvider(basicCredentialsProvider);
}
})
);
return restHighLevelClient;
}
You need to include the Basic credentials which you are giving while accessing the kibana, below code shows you can pass the username and password in JHLRC.
First, create the encoded string from your username and password, you can use the superuser elastic which has all the access by using the below code.
private String getEncodedString(String username, String password) {
return HEADER_PREFIX + Base64.getEncoder().encodeToString(
(username + ":" + password)
.getBytes());
}
Now in your request option, you pass the auth header which will include the base 64 encoded string which you will get from the above method.
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder()
.addHeader(AUTH_HEADER_NAME, getEncodedString(basicCredentials));
Last, you just need to build the object of above requestion options builder and pass it to your client in any request like below:
GetResponse getResponse = restHighLevelClient.get(getRequest, builder.build());

How to send self signed certificates in every request using RestTemplate?

I want to call a REST service with my spring application. To access that service i have a client certificate (self signed and in X.509 format) for authorization. What is the proper way to authenticate against the rest service?
I have two certificate file and one private key that I want send to service in each request
private final String CLIENT_CERT = "C:\\Cert\\cert.cert";
private final String CLIENT_KEY = "C:\\Cert\\client.cert";
private final String LYNX_ROOT_CERT = "C:\\Cert\\root.crt";
This is my request:
private ResponseEntity<String> restTemplateGetForObject(UriComponentsBuilder builder,
Map<String, String> uriParams) {
HttpEntity<?> entity = getEntityWithHeaders(HttpMethod.POST);
ResponseEntity<String> resp = restTemplate.exchange(builder.buildAndExpand(uriParams).toUri(), HttpMethod.POST,
entity, String.class);
return resp;
}
public ResponseEntity<String> updateOrCreateAlarm() {
String BaseUrl = configuration.getUrl();
String port = configuration.getPort();
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(BaseUrl + ":" + port + "/api/v1/alarm");
Map<String, String> uriParams = new HashMap<String, String>();
uriParams.put("alarm_group", "LYNXKEYPRO");
uriParams.put("alarm_channel", "001");
uriParams.put("alarm_state", "ALARM");
uriParams.put("ip_address", "10.6.1.42");
uriParams.put("computer_name", "WS-B2-Lab1");
uriParams.put("version", "2");
uriParams.put("additional_text", "Custom Text to display with alarm");
return restTemplateGetForObject(builder, uriParams);
}
1) I would create a truststore and add this certificate as an entry there.
2) When start your rest client, make sure the truststore is included in the jvm process.
Here are the details:
Self-signed certificate (For Windows). Note, in your case you probably already have the certificate. Just make sure it is added into the truststore. Also in the dev environment, keystore and truststore can be the same. You can google how to do it. I used keystore Explorer.
cd certificate directory
"C:\Program Files\Java\jdk1.8.0_162\bin\keytool.exe" -genkey -alias signFiles -keystore badsslkeystore
badsslkeystore – keystore name
assword – changeit.
-alias – keystore entry, in our case it is signFiles.
http://java-buddy.blogspot.cz/2016/07/java-example-of-ssl-server-and-client.html
For client:
$ java -jar -Djavax.net.ssl.trustStore=keystore -Djavax.net.ssl.trustStorePassword=password "...JavaSSLClient.jar"
Or set them as System properties.
public static void setClientSslConfig() {
System.setProperty("javax.net.ssl.trustStore", TRUSTSTORE);//TRUSTSTORE - truststore location in the file system.
System.setProperty("javax.net.ssl.trustStorePassword", KEYSTORE_PASSWORD);
}

How do I get/inject the current SSLSession in Jersey server?

I have a JAX-RS application (Jersey 2.22) in which I need to get the peer SSL client certificates (the server is enabled for mutual SSL authentication).
What I'm trying to achieve is something in the lines of:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String clientCertificates() {
SSLContext sslContext = SSLContext.getDefault();
SSLSessionContext clientContext = sslContext.getClientSessionContext();
byte[] sessionId = clientContext.getIds().nextElement();
SSLSession clientSession = clientContext.getSession(sessionId);
Certificate[] certificates = clientSession.getPeerCertificates();
return "The client provided " + certificates.length + " certificates.";
}
but this throws a NoSuchElementException at clientContext.getIds().nextElement() even if the client DID provide a valid SSL certificate that the server accepted to establish the session (I'm trying this with Glassfish 4, WildFly 9 and WildFly 10).
Note: The web.xml CLIENT-CERT auth-method is NOT what I need: I need to get hands a reference to the client certificates.
I found this (unchecked) method to get the provided certificates in a ContainerRequestFilter from a ContainerRequestContext, if any:
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
X509Certificate[] certificates = (X509Certificate[]) requestContext.getProperty("javax.servlet.request.X509Certificate");
Principal principal = certificates[0].getSubjectX500Principal();
}
}

Authenticating to sharepoint with kerberos from a java HttpClient

I have a linux\java6 client that will authenticate to sharepoint2010 with NTLM and then send HTTP REST web services using Apache Commons HttpClient.
I can do this with NTLM , but I want to use the same REST API to access sharepoint 2010 that uses kerberos auth.
Any examples how to authenticate and send REST over HTTP with a kerberos sharepoint?
(preferably using HttpClient)
p.s.
I dont have access to sharepoint code, but i do have access to sharepoint admin configurations.
This is roughly how I authenticate with NTLM:
HttpClient httpClient = new HttpClient(new SimpleHttpConnectionManager(true));
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JCIFS_NTLMScheme.class);
String localHostName = Inet4Address.getLocalHost().getHostName();
authscope = new AuthScope(uri.getHost(), AuthScope.ANY_PORT);
httpClient.getState().setCredentials(authscope,new NTCredentials(
getUsername(),getPassword(),localHostName,getDomain()));
// after the initial ntlm auth I can call my REST service with "httpClient.executeMethod"
int status = httpClient.executeMethod(new GetMethod(accessURI + "/sitecollection/info"));
Please confirm that your environment is correctly setup for Kerberos, this can be achieved by running kinit. If this fails you will need to ensure that your krb5.ini (windows) or krb5.conf (linux) are setup to point to your domain controller correctly.
Once you have confirmed that Kerberos is functional you can use the example code from HttpClient as pasted below.
Please note that there are many issues that can cause Kerberos to fail, such as time synchronisation, supported encryption types, trust relationships across domain forests and it's also worth ensuring that your client is on a seperate box to the server.
Here is the example code which is available in the HttpClient download, you will need to ensure your JAAS configuration and krb5.conf or ini are correct!
public class ClientKerberosAuthentication {
public static void main(String[] args) throws Exception {
System.setProperty("java.security.auth.login.config", "login.conf");
System.setProperty("java.security.krb5.conf", "krb5.conf");
System.setProperty("sun.security.krb5.debug", "true");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, new SPNegoSchemeFactory());
Credentials use_jaas_creds = new Credentials() {
public String getPassword() {
return null;
}
public Principal getUserPrincipal() {
return null;
}
};
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(null, -1, null),
use_jaas_creds);
HttpUriRequest request = new HttpGet("http://kerberoshost/");
HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println("----------------------------------------");
if (entity != null) {
System.out.println(EntityUtils.toString(entity));
}
System.out.println("----------------------------------------");
// This ensures the connection gets released back to the manager
EntityUtils.consume(entity);
} finally {
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
httpclient.getConnectionManager().shutdown();
}
}
}

Categories