Conjur Java API Usage Example

The Conjur Java API is a powerful tool for integrating Conjur into your Java projects. Here’s a small example I put together to demonstrate it locally.

1. Set Up Conjur Server

First, you need to set up a local Conjur OSS server. You can either set the server up manually by following to Conjur Quick Start Tutorial, or you can use the repo I made for quickly setting up Conjur instances for projects like this HERE and run ./setup.sh.

Next, give the variable we defined a secret:

docker-compose exec client conjur authn login -u admin -p <admin_API_KEY>
docker-compose exec client conjur variable values add test/secret secretValue

<admin_API_KEY>: Stored in admin_data

Note: This repo is a really rough script I use with a lot of room for improvement. Feel free to use it as just a guide if you want. The main reason I use this repo is to grab the cert from the Conjur server automatically for testing locally

2. Add Cert to Java Keystore

Next, we need to add our cert to our Java keystore.

First, find your JRE_HOME directory and set it as an environment variable if it’s not already set.

export JRE_HOME=<path_to_JRE_home>

Following the docs we convert the cert from .pem to .der.

openssl x509 -outform der -in conjure-myConjurAccount.pem -out conjur-myConjurAccount.der

Finally, we add the cert to our Java Keystore:

sudo -E keytool -importcert \
    -alias conjur-myConjurAccount \
    -keystore "$JRE_HOME/lib/security/cacerts" \
    -storepass changeit \
    -file ./conjur-myConjurAccount.der

3. Set Env Variables

export CONJUR_ACCOUNT: myConjurAccount
export CONJUR_AUTHN_LOGIN: host/test/myApp
export CONJUR_AUTHN_API_KEY: <myApp_API_KEY>
export CONJUR_APPLIANCE_URL: https://127.0.0.1:8443
export CONJUR_SECOND_USER: admin
export CONJUR_SECOND_API_KEY: <admin_API_KEY>

<myApp_API_KEY>: Stored in user_data
<admin_API_KEY>: Stored in admin_data

4. Setup Java App

On my machine, Eclipse cannot access the env variables unless it’s launched from the same Terminal that set them. This will probably be different on your machine. To launch Eclipse I use:

open /Applications/Eclipse.app

Finally, we can set up our Java app. Make sure to follow the Setup instructions to incorporate the Conjur Java API into your project. Both the Maven and JAR File setup will work for this example.

Here is the quick Java app I wrote for this example. The app:

  1. retrieves the secret as myApp
  2. logins into a user with write privileges(admin)
  3. changes the secret value
  4. retrieves the new secret value as myApp

main.java

import com.cyberark.conjur.api.Conjur;

public class main {
	static String variableID = "test/secret",
			originalSecret = "secretValue",
			newSecret = "ChangedByJavaAPI";
	
	public static void main(String[] args) {	
		String conjurAccount = System.getenv("CONJUR_ACCOUNT");
		String userLogin = System.getenv("CONJUR_AUTHN_LOGIN");
		String userAPIKey = System.getenv("CONJUR_AUTHN_API_KEY");
		String applianceURL = System.getenv("CONJUR_APPLIANCE_URL");
		String secondUser = System.getenv("CONJUR_SECOND_USER");
		String secondUserAPIKey = System.getenv("CONJUR_SECOND_API_KEY");
		
		// Login to user
		System.out.println("Logging into user: "+ userLogin);
		Conjur conjur = new Conjur();
		printLine();
		
		// Get secret as user
		System.out.println("Retrieving secret as: " + userLogin);
		String secret = conjur.variables().retrieveSecret(variableID);
		System.out.println(variableID + ": " + secret);
		printLine();
		
		// Log into account with write privileges
		System.out.println("Logging into user: " + secondUser);
		Conjur adminUser = new Conjur(secondUser, secondUserAPIKey);
		//System.out.println(adminUser.variables().toString());
		
		// Change variable value
		System.out.println("Setting '" + variableID + "' to: " + newSecret);
		adminUser.variables().addSecret(variableID, newSecret);
		printLine();
		
		// Retrieve new value
		System.out.println("Retrieving secret again as: " + userLogin);
		secret = conjur.variables().retrieveSecret(variableID);
		System.out.println(variableID + ": " + secret);
		
		reset(adminUser);
	}
	
	private static void reset(Conjur conjur) {
		conjur.variables().addSecret(variableID, originalSecret);
		
	}
	
	private static void printLine() {
		System.out.println("-----------------------");
	}

}

Note: The import may be different depending on whether you used Maven or the JAR File. This example uses Maven. For the JAR File it will be:

import net.conjur.api.Conjur;

Add this file to your Java project you created.

4. Run

You should now be able to run this and watch the Conjur Java API do it’s thang!

If you have any questions feel free to ask, thanks for reading!

3 Likes

hi,
I see there are conjur-api-java and conjur-sdk-java. Is there a brief guide when to use the former or the latter (api vs sdk)? From latest Conjur Open Source Suite only the conjur-api-java is listed. So conjur-sdk-java shouldn’t be used or used only in special cases?
Thank you
(sorry if I asked the question in the wrong place, feel free to move it)

Hi @stefano,

Apologies, the documentation for the Conjur Open Source Suite could probably use some updating re. using conjur-api-java vs. conjur-sdk-java.

We recommend using conjur-sdk-java over conjur-api-java in most cases. The only case where conjur-api-java might be more appropriate is if/when you want to store/retrieve binary secrets in Conjur (conjur-sdk-java doesn’t handle binary secrets quite right at the moment).

For some background, the conjur-api-java is our original, or legacy Java API package. The conjur-sdk-java is a newer SDK that is auto-generated based on an OpenAPI spec.

Hope this helps,
-Dane

Thank you.

You could at least modify the java library to be used in these two places (especially for new users). So if anyone has any doubts, they will be educated in the right way.

https://docs.conjur.org/Latest/en/Content/ReleaseNotes/ConjurOSS-suite-v1.13.1-s1.htm?tocpath=Release%20Notes|_____1

Hi @stefano,

Thank you for the details. I have question on multiple conjure account configuration. I found below example very useful:

// Configuration for Conjur account “myapp-dev”
ConjurConfiguration configDev = new ConjurConfiguration.Builder()
.withAccount(“myapp-dev”)
.withApplianceUrl(“https://conjur-dev.mycompany.com/api”)
.withCredentials(new Credentials(“myusername”, “mypassword”))
.build();

// Configuration for Conjur account “myapp-prod”
ConjurConfiguration configProd = new ConjurConfiguration.Builder()
.withAccount(“myapp-prod”)
.withApplianceUrl(“https://conjur-prod.mycompany.com/api”)
.withCredentials(new Credentials(“myusername”, “mypassword”))
.build();

but I could not found ConjurConfiguration package in api client. Can you please suggest how to configure multiple accounts from java client.

Regards,
Pydi

Hi, @chpsrinu

You’re right - the ConjurConfiguration class doesn’t belong to either the legacy conjur-api-java or the more recent conjur-sdk-java packages. Where did you find the example you’re referencing there?

Meanwhile, let’s use conjur-sdk-java to target two different Conjur endpoints and accounts:

ApiClient devClient = new ApiClient();
devClient.setBasePath("https://conjur-dev.mycompany.com/api")
devClient.setAccount("myapp-dev");
devClient.setUsername("myusername");
// NEVER HARD-CODE YOUR CREDENTIALS
devClient.setPassword(System.getenv("PASSWORD");

ApiClient prodClient = new ApiClient();
prodClient.setBasePath("https://conjur-prod.mycompany.com/api")
prodClient.setAccount("myapp-prod");
prodClient.setUsername("myusername");
// NEVER HARD-CODE YOUR CREDENTIALS
prodClient.setPassword(System.getenv("PASSWORD");

These ApiClient objects are passed used by specific API client classes, which have methods to target a subset of Conjur API endpoints. For example:

SecretsApi devApi = new SecretsApi(devClient);
SecretsApi prodApi = new SecretsApi(prodClient);

The account usually needs to be specified as an argument on an API function as well, like this:

String devSecret = api.getSecret(devClient.getAccount(), "variable", "somevariable");
String prodSecret = api.getSecret(prodClient.getAccount(), "variable", "somevariable");

Thank you @john.odon highly appreciated your suggestion.

API code is working fine with one account but sdk with different account giving below error:
Exception in thread “main” java.lang.NullPointerException: AccessToken must be initialized properly before use.
at com.cyberark.conjur.sdk.AccessToken.getHeaderValue(AccessToken.java:45)
at com.cyberark.conjur.sdk.auth.ApiKeyAuth.getApiKey(ApiKeyAuth.java:47)
at com.cyberark.conjur.sdk.auth.ApiKeyAuth.applyToParams(ApiKeyAuth.java:78)
at com.cyberark.conjur.sdk.ApiClient.updateParamsForAuth(ApiClient.java:1271)
at com.cyberark.conjur.sdk.ApiClient.buildRequest(ApiClient.java:1123)
at com.cyberark.conjur.sdk.ApiClient.buildCall(ApiClient.java:1101)
at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecretCall(SecretsApi.java:322)
at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecretValidateBeforeCall(SecretsApi.java:344)
at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecretWithHttpInfo(SecretsApi.java:422)
at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecret(SecretsApi.java:396)
at org.example.AppAPI.main(AppAPI.java:17)

Do I need to set token? token expire in few mins. How to fetch latest token from java client?

Also, Can you please help me understand if I use token to fetch secrets still I need to trust certificates? Because when we deploy my node to production environment user do not have access to java keystore for trust. I am trying to see if we can fetch secrets without trust if i use token.

@chpsrinu -

As long as you’re providing a valid password or API key to the client.setPassword() method, the API client will authenticate on your behalf, and attach a valid access token to your requests [1] - you could do this yourself, but don’t need to.

Yes, you’ll definitely need valid certificates attached to the Java client for Conjur (or a proxy in front of Conjur) to accept the request as valid. You can use the client.setCertFile(String filename) or client.setSslCaCert(InputStream sslCaCert) methods to configure the Conjur client with a certificate.

Regarding your getSecret failure: Given that one client is working as expected but the other isn’t, I expect there’s some authentication issue at play here. You might want to check your Conjur logs for any authentication failures.

Hello @john.odon Thank you for prompt response.

I tried to provide same password for both sdk and api. Looking like some bug in sdk 4.0.0 version:

working code with env CONJUR_ACCOUNT and CONJUR_APPLIANCE_URL configured:

Map<String, String> env = System.getenv();
        env.forEach((k,v) -> System.out.println(k + ":" + v));
        Conjur conjur = new Conjur("Dave@BotApp", "2nhme5m1d2aw3rf9zrd120k75f164xb2d8nvz8w14qg9vj2pmcrea");
        System.out.println(conjur.variables().retrieveSecret("BotApp/secretVar"));

SDK code while is not working:

ApiClient devClient = new ApiClient();
        devClient.setBasePath("https://127.0.0.1:8443/api");
        devClient.setAccount("myConjurAccount");
        devClient.setUsername("Dave@BotApp");
        devClient.setPassword("2nhme5m1d2aw3rf9zrd120k75f164xb2d8nvz8w14qg9vj2pmcrea");
        devClient.setApiKey("2nhme5m1d2aw3rf9zrd120k75f164xb2d8nvz8w14qg9vj2pmcrea");

        SecretsApi devApi = new SecretsApi(devClient);
        devApi.getSecret(devClient.getAccount(), "variable", "BotApp/secretVar");

error:

Exception in thread "main" java.lang.NullPointerException: AccessToken must be initialized properly before use.
	at com.cyberark.conjur.sdk.AccessToken.getHeaderValue(AccessToken.java:45)
	at com.cyberark.conjur.sdk.auth.ApiKeyAuth.getApiKey(ApiKeyAuth.java:47)
	at com.cyberark.conjur.sdk.auth.ApiKeyAuth.applyToParams(ApiKeyAuth.java:78)
	at com.cyberark.conjur.sdk.ApiClient.updateParamsForAuth(ApiClient.java:1271)
	at com.cyberark.conjur.sdk.ApiClient.buildRequest(ApiClient.java:1123)
	at com.cyberark.conjur.sdk.ApiClient.buildCall(ApiClient.java:1101)
	at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecretCall(SecretsApi.java:322)
	at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecretValidateBeforeCall(SecretsApi.java:344)
	at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecretWithHttpInfo(SecretsApi.java:422)
	at com.cyberark.conjur.sdk.endpoint.SecretsApi.getSecret(SecretsApi.java:396)
	at org.example.AppAPI.main(AppAPI.java:21)

In ApiKeyAuth.class, getApiKey() apiKey is null so it is trying to get from token and token is null. I verified apikey is set correctly in ApiClient.class.

Apologies, @chpsrinu - this claim that I made in my last response was incorrect…

As long as you’ve provided a valid password or API key to the client.setPassword() method, the API client will authenticate on your behalf, and attach a valid access token to your requests

This behavior can be enabled by setting the CONJUR_AUTO_UPDATE_TOKEN environment variable to true, and using the other available environment variables to configure authentication – otherwise, authentication is manual. Here’s my refresh-less solution:

ApiClient client = Configuration.getDefaultApiClient();
// ApiClient setup here...

// Authenticate client and attach token
AuthenticationApi authn = new AuthenticationApi(client);
String encodedToken = authn.getAccessToken(account, login, apiKey, "base64", null);
AccessToken conjurToken = AccessToken.fromEncodedToken(encodedToken);
((ApiKeyAuth)client.getAuthentication("conjurAuth")).setAccessToken(conjurToken);

// Fetch secret
SecretsApi devApi = new SecretsApi(client);
devApi.getSecret(client.getAccount(), "variable", "BotApp/secretVar");

Thank you for the details it worked with above suggestion.