title: How to encrypt Spring Boot application data in mongodb
body: <p>I have a Spring Boot application that stores payment information in the database. The application has end-points</p>
  <ul>
  <li><code>GET /api/orders</code> - get orders by filter</li>
  <li><code>POST /api/orders</code> - add a new order</li>
  <li><code>PUT /api/orders</code> - update order</li>
  <li><code>DELETE /api/orders</code> - delete order</li>
  </ul>
  <p>These endpoints are not secure itself, and I do not want to secure them on the application level. All traffic from the user goes to HTTPS proxy, which will decrypt it and forward to the application.</p>
  <p>However, I use mongo atlas free version for prototyping
  <a href="https://www.mongodb.com/cloud/atlas/faq" rel="nofollow noreferrer">https://www.mongodb.com/cloud/atlas/faq</a></p>
  <p>All the data I put into the database must be encrypted. Even the document structure (fields names and types) must be encrypted.</p>
  <p><a href="https://stackoverflow.com/questions/30726414/how-to-encrypt-field-in-mongodb">How to encrypt a field</a> does not work for me, because I want to encrypt the whole document.
  I do not want to use unofficial libraries like bellow (thus no one guarantees if the library is secure)</p>
  <pre><code>&lt;dependency&gt;
     &lt;groupId&gt;com.bol&lt;/groupId&gt;
     &lt;artifactId&gt;spring-data-mongodb-encrypt&lt;/artifactId&gt;
     &lt;version&gt;1.3.0&lt;/version&gt;
  &lt;/dependency&gt;
  </code></pre>
  <p>One idea that came to me is to configure the application to use a password (somehow configured or generated at given moment of time).</p>
  <pre><code>/**
  * Provides a password to encrypt a document.
  **/
  // TODO  How to do it better? I still have to improve it.
  @Component
  public class PasswordProviderImpl implements PasswordProvider {
      
      private static final byte[] MASTER_PASSWORD = {1, 11, 37, 166, 11, 77};
  
      @Autowired
      private Environment environment;
  
      // I do not care about the implementation yet
      public char [] getPassword() {
          final byte[] envPassword = environment.getProperty(&quot;appplicationPassword&quot;).toString().toByteArray();
          return envPassword;
      }
  }
  </code></pre>
  <pre><code>/**
  * Encrypts input byte array with provided password, afterwards, cleans input data and password - populates them with zeros - 0.
  **/
  @Component
  public class Encryptor {
      private PasswordProvider passwordProvider;
  
      public byte [] encrypt(final byte unecrypted) {
          final byte [] password = passwordProvider.getPassword();
          final byte [] encrypted = xor(unecrypted, password);
          makeZeros(password);
          makeZeros(unecrypted);
          return encrypted;
      }
      
      private void makeZeros(final byte[] array) { /*Implementation*/}
      private byte[] xor(final byte [] arg1, final byte[] arg2) {/*Implementation*/}
  }
  </code></pre>
  <pre><code>/**
  * Represents a unit of data in my application. Its values and fields and structure - everything should be encrypted.
  **/
  public class Order {
      private ObjectId id;
      private Instant createdDate;
      private Instant updatedDate;
      private Money amount;
      private String additionalDetails;
  
      // gettters, setters, constructor
  }
  </code></pre>
  <p>Converter, found <a href="https://stackoverflow.com/questions/13780692/set-mongodb-converter-programmatically">Set MongoDb converter programmatically</a></p>
  <pre><code>/**
  * It is declared application configuration. It defines how to store {@link Order} in mongodb
  **/
  @Component
  public OrderConverter implements onverter&lt;Order, SecuredOrder&gt; {
      
      private ObjectMapper objectMapper;
      private Encryptor encryptor;
      private Base64Converter base64Converter;
  
      @Override 
      public SecuredOrder convert(Order source) { 
          final String json = objectMapper.writeValueAsString(source);
          final unencrypted = json.toByteArray();
          final byte[] encrypted = encryptor.encrypt(unencrypted);
          final String payload = base64Converter.toBase64(encrypted);
          return SecuredOrder.of(order.getId(), payload);
      }
  }
  </code></pre>
  <pre><code>public class SecuredOrder {
      private ObjectId id; // same as in order id
      private String encryptedPayload; // The converter will make it
  }
  
  @Service
  public OrderService {
      public void saveOrder(Order order) {
          orderRepository.save(order);
      }
  }
  </code></pre>
  <p>If you have done a similar thing, please give me a direction. I would really like to do it properly.</p>
  <p>Also, mongodb provides encryption mechanism, so maybe I should use it?</p>
  <p><a href="https://docs.mongodb.com/manual/core/security-encryption-at-rest/" rel="nofollow noreferrer">https://docs.mongodb.com/manual/core/security-encryption-at-rest/</a></p>
  <p><a href="https://docs.mongodb.com/manual/core/security-encryption-at-rest/#encrypted-storage-engine" rel="nofollow noreferrer">https://docs.mongodb.com/manual/core/security-encryption-at-rest/#encrypted-storage-engine</a></p>