RsaKemHybridEncrypt implements HybridEncrypt { @Override public byte[] encrypt(final byte[] plaintext, final byte[] contextInfo) throws GeneralSecurityException { BigInteger mod = recipientPublicKey.getModulus(); byte[] sharedSecret = RsaKem.generateSecret(mod); Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding"); rsaCipher.init(Cipher.ENCRYPT_MODE, recipientPublicKey); byte[] token = rsaCipher.doFinal(sharedSecret); byte[] demKey = Hkdf.computeHkdf( hkdfHmacAlgo, sharedSecret, hkdfSalt, contextInfo, aeadFactory.getKeySizeInBytes()); Aead aead = aeadFactory.createAead(demKey); byte[] ciphertext = aead.encrypt(plaintext, RsaKem.EMPTY_AAD); return ByteBuffer.allocate(token.length + ciphertext.length).put(token).put(ciphertext).array(); } RsaKemHybridEncrypt(
      final RSAPublicKey recipientPublicKey,
      String hkdfHmacAlgo,
      final byte[] hkdfSalt,
      AeadFactory aeadFactory); @Override byte[] encrypt(final byte[] plaintext, final byte[] contextInfo);  }
@Test public void encrypt_decrypt_success() throws GeneralSecurityException { if (TestUtil.isTsan()) { return; } KeyPair keyPair = RsaKem.generateRsaKeyPair(2048); String hmacAlgo = "HMACSHA256"; byte[] salt = Random.randBytes(20); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); HybridEncrypt hybridEncrypt = new RsaKemHybridEncrypt(rsaPublicKey, hmacAlgo, salt, new AesGcmFactory(16)); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); HybridDecrypt hybridDecrypt = new RsaKemHybridDecrypt(rsaPrivateKey, hmacAlgo, salt, new AesGcmFactory(16)); byte[] plaintext = Random.randBytes(20); byte[] context = Random.randBytes(20); Set<String> ciphertexts = new TreeSet<>(); for (int j = 0; j < 8; j++) { byte[] ciphertext = hybridEncrypt.encrypt(plaintext, context); if (ciphertexts.contains(new String(ciphertext, UTF_8))) { throw new GeneralSecurityException("Encryption is not randomized"); } ciphertexts.add(new String(ciphertext, UTF_8)); byte[] decrypted = hybridDecrypt.decrypt(ciphertext, context); assertArrayEquals(plaintext, decrypted); } assertThat(ciphertexts).hasSize(8); }