1---
2layout: "guides"
3page_title: "Java Application Demo - Guides"
4sidebar_title: "Java Application Demo"
5sidebar_current: "guides-encryption-spring-demo"
6description: |-
7  This guide discusses the concepts necessary to help users
8  understand Vault's AppRole authentication pattern and how to use it to
9  securely introduce a Vault authentication token to a target server,
10  application, container, etc. in a Java environment.
11---
12
13# Java Sample App using Spring Cloud Vault
14
15Once you have learned the fundamentals of Vault, the next step is to start
16integrating your system with Vault to secure your organization's secrets.
17
18The purpose of this guide is to go through the working implementation demo
19introduced in the [Manage secrets, access, and encryption in the public cloud
20with
21Vault](https://www.hashicorp.com/resources/solutions-engineering-webinar-series-episode-2-vault)
22webinar.
23
24[![YouTube](/img/vault-java-demo-1.png)](https://youtu.be/NxL2-XuZ3kc)
25
26The Java application in this demo leverages the [_Spring Cloud
27Vault_](https://cloud.spring.io/spring-cloud-vault/) library which provides
28lightweight client-side support for connecting to Vault in a distributed
29environment.
30
31
32## Reference Material
33
34- [Encryption as a Service](/guides/encryption/transit.html)
35- [Manage secrets, access, and encryption in
36the public cloud with
37Vault](https://www.hashicorp.com/resources/solutions-engineering-webinar-series-episode-2-vault)
38- [Spring Cloud Vault](https://cloud.spring.io/spring-cloud-vault/)
39- [Transit Secrets Engine](/docs/secrets/transit/index.html)
40- [Secrets as a Service: Dynamic Secrets](/guides/secret-mgmt/dynamic-secrets.html)
41
42
43## Estimated Time to Complete
44
4515 minutes
46
47
48## Challenge
49
50Incidents of data breaches which expose sensitive information make headlines more
51often than we like to hear. It becomes more and more important to protect data
52by encrypting it whether the data is in-transit or at-rest. However, creating
53a highly secure and sophisticated solution by yourself requires time and resources
54which are in demand when an organization is facing a constant threat.
55
56
57## Solution
58
59Vault centralizes management of cryptographic services used to protect your
60data.  Your system can communicate with Vault easily through the Vault API to
61encrypt and decrypt your data, and the encryption keys never have to leave the
62Vault.
63
64![Encryption as a Service](/img/vault-eaas.png)
65
66
67## Prerequisites
68
69To perform the tasks described in this guide:
70
71- Install [HashiCorp Vagrant](https://www.vagrantup.com/intro/getting-started/install.html)
72- Clone or download the demo assets from the [hashicorp/vault-guides](https://github.com/hashicorp/vault-guides/tree/master/secrets/spring-cloud-vault)
73GitHub repository
74
75
76## Steps
77
78-> For the purposes of this guide, you are going to provision a Linux machine
79locally using Vagrant.  However, the GitHub repository provides supporting files
80to provision the environment demonstrated in the webinar.
81
82After downloading the [demo assets](#prerequisites) from the GitHub repository,
83you should find the following folders:
84
85| Folder           | Description                                               |
86|------------------|-----------------------------------------------------------|
87| `aws`            | Supporting files to deploy the demo app to AWS            |
88| `kubernetes`     | Supporting files to deploy the demo app to Kubernetes     |
89| `nomad`          | Supporting files to deploy the demo app to Nomad          |
90| `scripts`        | Scripts to setup PostgreSQL and Vault                     |
91| `src/main`       | Sample app source code                                    |
92| `vagrant-local`  | Vagrant file to deploy the demo locally                   |
93
94
95<br>
96
97In this guide, you will perform the following:
98
991. [Review the demo application implementation](#step1)
1001. [Deploy and review the demo environment](#step2)
1011. [Run the demo application](#step3)
1021. [Reload the Static Secrets](#step4)
103
104![Encryption as a Service](/img/vault-java-demo-10.png)
105
106### <a name="step1"></a>Step 1: Review the demo application implementation
107
108The source code can be found under the `src/main` directory.
109
110```plaintext
111├── java
112│   └── com
113│       └── hashicorp
114│           └── vault
115│               └── spring
116│                   └── demo
117│                       ├── BeanUtil.java
118│                       ├── Order.java
119│                       ├── OrderAPIController.java
120│                       ├── OrderRepository.java
121│                       ├── Secret.java
122│                       ├── SecretController.java
123│                       ├── TransitConverter.java
124│                       └── VaultDemoOrderServiceApplication.java
125└── resources
126    └── application.yaml
127```
128
129The demo Java application leverages the Spring Cloud Vault library to
130communicate with Vault.
131
132In the `TransitConverter` class, the `convertToDatabaseColumn` method invokes a
133Vault operation to encrypt the `order`. Similarly, the
134`convertToEntityAttribute` method decrypts the `order` data.
135
136```plaintext
137...
138	@Override
139	public String convertToDatabaseColumn(String customer) {
140		VaultOperations vaultOps = BeanUtil.getBean(VaultOperations.class);
141		Plaintext plaintext = Plaintext.of(customer);
142		String cipherText = vaultOps.opsForTransit().encrypt("order", plaintext).getCiphertext();
143		return cipherText;
144	}
145
146	@Override
147	public String convertToEntityAttribute(String customer) {
148		VaultOperations vaultOps = BeanUtil.getBean(VaultOperations.class);
149		Ciphertext ciphertext = Ciphertext.of(customer);
150        String plaintext = vaultOps.opsForTransit().decrypt("order", ciphertext).asString();
151		return plaintext;
152...
153```
154
155The `VaultDemoOrderServiceApplication` class defines the `main` method.
156
157```plaintext
158public class VaultDemoOrderServiceApplication  {
159
160	private static final Logger logger = LoggerFactory.getLogger(VaultDemoOrderServiceApplication.class);
161
162	@Autowired
163    	private SessionManager sessionManager;
164
165	@Value("${spring.datasource.username}")
166	private String dbUser;
167
168	@Value("${spring.datasource.password}")
169	private String dbPass;
170
171	public static void main(String[] args) {
172		SpringApplication.run(VaultDemoOrderServiceApplication.class, args);
173	}
174
175	@PostConstruct
176	public void initIt() throws Exception {
177		logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken());
178		logger.info("Got DB User: " + dbUser);
179	}
180}
181```
182
183The `OrderAPIController` class defines the API endpoint (`api/orders`).
184
185
186### <a name="step2"></a>Step 2: Deploy and review the demo environment
187
188Now let's run the demo app and examine how it behaves.
189
190~> To keep it simple and lightweight, you are going to run a Linux virtual
191machine locally using Vagrant.
192
193#### Task 1: Run Vagrant
194
195In the **`vault-guides/secrets/spring-cloud-vault/vagrant-local`** folder,
196a `Vagrantfile` is provided which spins up a Linux machine where the demo
197components are installed and configured.
198
199```shell
200# Change the working directory to vagrant-local
201$ cd /vault-guides/secrets/spring-cloud-vault/vagrant-local
202
203# Create and configure a Linux machine. This takes about 3 minutes
204$ vagrant up
205...
206demo: Success! Data written to: database/roles/order
207demo: Success! Enabled the transit secrets engine at: transit/
208demo: Success! Data written to: transit/keys/order
209demo: Success! Data written to: secret/spring-vault-demo
210
211# Verify that the virtual machine was successfully created and running
212$ vagrant status
213Current machine states:
214demo                      running (virtualbox)
215...
216
217# Connect to the demo machine
218$ vagrant ssh demo
219```
220
221
222There are 3 Docker containers running on the machine: `spring`, `vault`, and `postgres`.
223
224```plaintext
225[vagrant@demo ~]$ docker ps
226CONTAINER ID     IMAGE            COMMAND                  CREATED           STATUS           NAMES
227684d8fb23ae5     spring           "java -Djava.secur..."   7 minutes ago     Up 7 minutes     spring
228dc6a3454b323     vault:0.10.0     "docker-entrypoint..."   7 minutes ago     Up 7 minutes     vault
2294093a45c209f     postgres         "docker-entrypoint..."   7 minutes ago     Up 7 minutes     postgres
230```
231
232#### Task 2: Examine the Vault environment
233
234During the demo machine provisioning, the `/scripts/vault.sh` script was
235executed to perform the following:
236
237- Created a policy named, **`order`**
238- Enabled the `transit` secret engine and created an encryption key named, **`order`**
239- Enabled the `database` secret engine and created a role named, **`order`**
240
241View the `vault` log:
242
243```plaintext
244[vagrant@demo ~]$  docker logs vault
245...
246==> Vault shutdown triggered
247==> Vault server configuration:
248             Api Address: http://0.0.0.0:8200
249                     Cgo: disabled
250         Cluster Address: https://0.0.0.0:8201
251              Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "0.0.0.0:8201", tls: "disabled")
252               Log Level: info
253                   Mlock: supported: true, enabled: false
254                 Storage: inmem
255                 Version: Vault v0.10.0
256             Version Sha: 5dd7f25f5c4b541f2da62d70075b6f82771a650d
257WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
258and starts unsealed with a single unseal key. The root token is already
259authenticated to the CLI, so you can immediately begin using Vault.
260You may need to set the following environment variable:
261    $ export VAULT_ADDR='http://0.0.0.0:8200'
262The unseal key and root token are displayed below in case you want to
263seal/unseal the Vault or re-authenticate.
264Unseal Key: 2QIPWPDykRG/xWWl0quSHiXq8u+pFg3yEq0sgJPhMbA=
265Root Token: root
266...
267```
268
269Notice that the log indicates that the Vault server is running in the `dev`
270mode, and the root token is `root`.
271
272You can visit the Vault UI at http://localhost:8200/ui.  Enter **`root`** and
273click **Sign In**.
274
275
276Select the **`transit/`** secrets engine, and you should find an encryption key
277named, "`order`".
278
279![Vault UI](/img/vault-java-demo-2.png)
280
281Under the **Policies**, verify that the `order` policy exists.
282
283![Vault UI](/img/vault-java-demo-3.png)
284
285This `order` policy is for the application.  It permits `read` on the
286`database/creds/order` path so that the demo app can get a dynamically generated
287database credential from Vault. Therefore, the PostgreSQL credentials are not
288hard-coded anywhere.
289
290```plaintext
291path "database/creds/order"
292{
293  capabilities = ["read"]
294}
295```
296
297An `update` permission allows the app to request data encryption and decryption
298using the `order` encryption key in Vault.
299
300```plaintext
301...
302path "transit/decrypt/order" {
303  capabilities = ["update"]
304}
305
306path "transit/encrypt/order" {
307  capabilities = ["update"]
308}
309...
310```
311
312
313
314#### Task 3: Examine the Spring container
315
316Remember that the `VaultDemoOrderServiceApplication` class logs messages during
317the successful execution of `initIt()`:
318
319```plaintext
320...
321  @PostConstruct
322  	public void initIt() throws Exception {
323  		logger.info("Got Vault Token: " + sessionManager.getSessionToken().getToken());
324  		logger.info("Got DB User: " + dbUser);
325...
326```
327
328Verify that the log indicates that the demo app obtained a database
329credentials from Vault successfully:
330
331```plaintext
332[vagrant@demo ~]$  docker logs spring | grep Got
333...VaultDemoOrderServiceApplication : Got Vault Token: root
334...VaultDemoOrderServiceApplication : Got DB User: v-token-order-rywqz61432yyx2x27w8r-1524067226
335```
336
337Create a new shell session in the `spring` container.
338
339```plaintext
340[vagrant@demo ~]$  docker exec -it spring sh
341/ #
342```
343
344Find the `bootstrap.yaml` file:
345
346```plaintext
347/ #  ls -al
348total 36720
349drwxr-xr-x    1 root     root            51 Apr 18 16:00 .
350drwxr-xr-x    1 root     root            51 Apr 18 16:00 ..
351-rwxr-xr-x    1 root     root             0 Apr 18 16:00 .dockerenv
352-rwxr--r--    1 root     root      37587245 Apr 18 15:59 app.jar
353drwxr-xr-x    2 root     root          4096 Jan  9 19:37 bin
354-rw-r--r--    1 1000     1000           426 Apr 17 17:58 bootstrap.yaml
355...
356
357/ # cat bootstrap.yaml
358spring.application.name: spring-vault-demo
359spring.cloud.vault:
360    authentication: TOKEN
361    token: ${VAULT_TOKEN}
362    host: localhost
363    port: 8200
364    scheme: http
365    fail-fast: true
366    config.lifecycle.enabled: true
367    generic:
368      enabled: true
369      backend: secret
370    database:
371        enabled: true
372        role: order
373        backend: database
374spring.datasource:
375  url: jdbc:postgresql://localhost:5432/postgres
376```
377
378The client token was injected into the `spring` container as an environment
379variable (`VAULT_TOKEN`) by Vagrant.
380
381Enter `exit` to close the shell session in the `spring` container.
382
383
384#### Task 4: Examine the PostgreSQL database
385
386Connect to the PostgreSQL database running in the `postgres` container:
387
388```plaintext
389[vagrant@demo ~]$ docker exec -it postgres psql -U postgres -d postgres
390psql (10.3 (Debian 10.3-1.pgdg90+1))
391Type "help" for help.
392
393
394postgres=# \d orders
395                                          Table "public.orders"
396    Column     |            Type             | Collation | Nullable |              Default
397---------------+-----------------------------+-----------+----------+------------------------------------
398 id            | bigint                      |           | not null | nextval('orders_id_seq'::regclass)
399 customer_name | character varying(60)       |           | not null |
400 product_name  | character varying(20)       |           | not null |
401 order_date    | timestamp without time zone |           | not null |
402Indexes:
403    "orders_pkey" PRIMARY KEY, btree (id)
404```
405
406Let's list the existing database roles.
407
408```plaintext
409postgres-# \du
410                                                     List of roles
411                   Role name                   |                         Attributes                         | Member of
412-----------------------------------------------+------------------------------------------------------------+-----------
413 postgres                                      | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
414 v-token-order-rywqz61432yyx2x27w8r-1524067226 | Password valid until 2018-04-18 20:56:31+00                | {}
415```
416
417Notice that there is a role name starting with `v-token-order-` which was
418dynamically created by the database secret engine.
419
420~> **NOTE:** To learn more
421about the database secret engine, read the [Secrets as a Service: Dynamic
422Secrets](/guides/secret-mgmt/dynamic-secrets.html) guide.
423
424Enter `\q` to exit out of the `psql` session, or you can open another terminal
425and SSH into the demo virtual machine.
426
427
428
429### <a name="step3"></a>Step 3: Run the demo application
430
431If everything looked fine in [Step 2](#step2), you are ready to write some data.
432
433![Vault UI](/img/vault-java-demo-9.png)
434
435You have [verified in the `spring` log](#task-3-examine-the-sprig-container)
436that the demo app successfully retrieved a database credential from the Vault
437server during its initialization.
438
439The next step is to send a new order request via the demo app's _orders_ API
440(http://localhost:8080/api/orders).
441
442```shell
443# Create a new order data
444[vagrant@demo ~]$ tee payload.json<<EOF
445{
446  "customerName": "John",
447  "productName": "Nomad"
448}
449EOF
450
451# Send a request using cURL
452[vagrant@demo ~]$ curl --request POST --header "Content-Type: application/json" \
453                       --data @payload.json http://localhost:8080/api/orders | jq
454{
455  "id": 2,
456  "customerName": "John",
457  "productName": "Nomad",
458  "orderDate": "2018-04-18T22:07:42.916+0000"
459}
460```
461
462**NOTE:** Alternatively, you can use tool such as
463[Postman](https://www.getpostman.com/apps) instead of cURL to invoke the API if
464you prefer.
465
466![Postman](/img/vault-java-demo-4.png)
467
468
469The order data you sent gets encrypted by Vault. The database only sees the
470ciphertext. Let's verify that the order information stored in the database
471is encrypted.
472
473```plaintext
474[vagrant@demo ~]$ docker exec -it postgres psql -U postgres -d postgres
475
476postgres=# select * from orders;
477 id |                     customer_name                     | product_name |       order_date
478----+-------------------------------------------------------+--------------+-------------------------
479  1 | vault:v1:Qj0lx5DSZvwcHeMOX/5UX/ErHTaDPA3mVlSSpaXd1tbM | VE           | 2018-04-18 21:56:37.924
480  2 | vault:v1:UwL3HnyqTUac5ElS5WYAuNg3NdIMFtd6vvwukL+FaKun | Nomad        | 2018-04-18 22:07:42.916
481(2 rows)
482
483postgres=# \q
484```
485
486>In this demo, Vault encrypts the customer names; therefore, the values in the
487**`customer_name`** column do not display the names in a human readable
488manner (e.g. "James" and "John").
489
490Now, retrieve the order data via the orders API:
491
492```plaintext
493[vagrant@demo ~]$ curl --header "Content-Type: application/json" \
494                       http://localhost:8080/api/orders | jq
495[
496 {
497   "id": 1,
498   "customerName": "James",
499   "productName": "VE",
500   "orderDate": "2018-04-18T21:56:37.924+0000"
501 },
502 {
503   "id": 2,
504   "customerName": "John",
505   "productName": "Nomad",
506   "orderDate": "2018-04-18T22:07:42.916+0000"
507 }
508]
509```
510
511The customer names should be readable. Remember that the **`order`** policy
512permits the demo app to encrypt and decrypt data using the `order` encryption
513key in Vault.
514
515#### Web UI
516
517Vault UI makes it easy to decrypt the data.
518
519In the **Secrets** tab, select **`transit/` > `orders`**, and select **Key
520actions**.
521
522![Web UI](/img/vault-java-demo-5.png)
523
524Select **Decrypt** from the transit actions.  Now, copy the ciphertext from the
525**`orders`** table and paste it in.
526
527![Web UI](/img/vault-java-demo-6.png)
528
529Click **Decrypt**.
530
531![Web UI](/img/vault-java-demo-7.png)
532
533Finally, click **Decode from base64** to reveal the customer name.
534
535![Web UI](/img/vault-java-demo-8.png)
536
537
538### <a name="step4"></a>Step 4: Reloading the Static Secrets
539
540Now, let's test another API endpoint, **`api/secret`** provided by the demo app.
541A plain old Java object, `Secret` defines a get method for `key` and `value`.
542The `SecretController.java` defines an API endpoint, **`api/secret`**.
543
544```plaintext
545package com.hashicorp.vault.spring.demo;
546...
547
548@RefreshScope
549@RestController
550public class SecretController {
551
552	@Value("${secret:n/a}")
553	String secret;
554
555	@RequestMapping("/api/secret")
556	public Secret secret() {
557		return new Secret("secret", secret);
558	}
559}
560```
561
562Remember from [Step 2](#task-2-examine-the-vault-environment) that the
563**`order`** policy granted permissions on the `secret/spring-vault-demo` path.
564
565```plaintext
566path "secret/spring-vault-demo" {
567  capabilities = ["create", "read", "update", "delete", "list"]
568}
569...
570```
571<br>
572
573The demo app retrieved the secret from `secret/spring-vault-demo` and has a
574local copy.  If someone (or perhaps another app) updates the secret, it makes the
575secret held by the demo app to be obsolete.
576
577![Static Secret](/img/vault-java-demo-11.png)
578
579Spring offers [Spring Boot
580Actuator](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready)
581which can be used to facilitate the reloading of the static secret.
582
583#### Task 1: Read the secret
584
585The initial key-value was set by Vagrant during the provisioning. (See the
586`Vagrantfile` at line 48.)
587
588Let's invoke the demo app's secret API (**`api/secret`**):
589
590```plaintext
591$ curl -s http://localhost:8080/api/secret | jq
592{
593  "key": "secret",
594  "value": "hello-vault"
595}
596```
597
598This is the secret that the demo app knows about.
599
600
601#### Task 2: Update the Secrets
602
603Now, update the secret stored in Vault using API:
604
605```shell
606# Update the value via API
607$ curl --header "X-Vault-Token: root" \
608       --request POST \
609       --data '{ "secret": "my-api-key" }' \
610       http://127.0.0.1:8200/v1/secret/spring-vault-demo
611
612# Verify that the secret value was updated
613$ curl --header "X-Vault-Token: root" \
614       http://127.0.0.1:8200/v1/secret/spring-vault-demo | jq
615{
616 "request_id": "514601e4-a790-3dc6-14b0-537d6982a6c6",
617 "lease_id": "",
618 "renewable": false,
619 "lease_duration": 2764800,
620 "data": {
621   "secret": "my-api-key"
622 },
623...
624}
625```
626
627
628#### Task 3: Refresh the secret on demo app
629
630Run the demo app's secret API again:
631
632```plaintext
633$ curl -s http://localhost:8080/api/secret | jq
634{
635  "key": "secret",
636  "value": "hello-vault"
637}
638```
639
640The current value stored in Vault is now `my-api-key`; however, the demo app
641still holds `hello-vault`.
642
643
644Spring provides an
645[actuator](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready)
646which can be leveraged to refresh the secret value.  At line 54 of the
647`vault-guides/secrets/spring-cloud-vault/pom.xml`, you see that the actuator was
648added to the project.
649
650```plaintext
651...
652<dependency>
653  <groupId>org.springframework.boot</groupId>
654  <artifactId>spring-boot-starter-actuator</artifactId>
655</dependency>
656...
657```
658
659Let's refresh the secret using the actuator:
660
661```plaintext
662$ curl -s --request POST http://localhost:8080/actuator/refresh | jq
663[
664  "secret"
665]
666```
667
668Read back the secret from the demo app again:
669
670```plaintext
671$ curl -s http://localhost:8080/api/secret | jq
672{
673  "key": "secret",
674  "value": "my-api-key"
675}
676```
677
678It should display the correct value.
679
680---
681
682When you are done exploring the demo implementation, you can destroy the virtual
683machine:
684
685```plaintext
686$ vagrant destroy
687demo: Are you sure you want to destroy the 'demo' VM? [y/N] y
688==> demo: Forcing shutdown of VM...
689==> demo: Destroying VM and associated drives...
690```
691
692~> In the webinar, the demo environment was running in a public cloud, and Nomad
693and Consul were also installed and configured.  If you wish to build a similar
694environment using Kubernetes, the assets in the `vault-guides/secrets/spring-cloud-vault/kubernetes`
695folder provides you with some guidance.
696
697## Next steps
698
699[AppRole](/docs/auth/approle.html) is an authentication mechanism within Vault
700to allow machines or apps to acquire a token to interact with Vault. Read the
701[AppRole Pull Authentication](/guides/identity/authentication.html) guide
702which introduces the steps to generate tokens for machines or apps by enabling
703AppRole auth method.
704