@Override
  public String encrypt(String data) {
    try {
      Cipher cipher;
      switch (transformation) {
        case AES_CBC_NOPADDING:
          cipher = Cipher.getInstance("AES/CBC/NoPadding");
          cipher.init(Cipher.ENCRYPT_MODE, key, generateIv(cipher));
          break;
        case RSA_ECB_PKCS1:
          cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
          cipher.init(Cipher.ENCRYPT_MODE, key);
          break;
        default:
          throw new AssertionError("How could this happen...");
      }
      // we use a salt the size of the first block
      // so that we don't need to know IV for AES/CBC
      byte[] salt = new byte[cipher.getBlockSize()];
      SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
      random.nextBytes(salt);
      byte[] encryptedSalt = cipher.update(salt);
      byte[] encryptedData = cipher.doFinal(data.getBytes(UTF_8));
      byte[] encrypted = new byte[encryptedSalt.length + encryptedData.length];
      System.arraycopy(encryptedSalt, 0, encrypted, 0, encryptedSalt.length);
      System.arraycopy(encryptedData, 0, encrypted, encryptedSalt.length, encryptedData.length);
      return BaseEncoding.base64Url().encode(encrypted);
    } catch (BadPaddingException
        | IllegalBlockSizeException
        | InvalidAlgorithmParameterException
        | InvalidKeyException
        | NoSuchAlgorithmException
        | NoSuchPaddingException e) {
      monitor.severe(() -> format("Exception encrypting data, length: %s", data.length()), e);
      throw new RuntimeException("Couldn't encrypt data", e);
    }
  }
Provides AES and RSA-based encryption implementations. See {@link EncrypterFactory} to create.

The first block encrypted is a random salt so that we can ignore its value in the decrypter. This
makes it so we don't have to record the IV for AES.
libraries/security/src/main/java/org/datatransferproject/security/EncrypterImpl.java