/**
   * Test public key encryption.
   *
   * @throws Exception When test fails.
   */
  @Test
  public void testActivationGenerate() throws Exception {
    String activationOTP = "CKZ2O-OE544";
    String activationIdShort = "IFA6F-3NPAZ";
    byte[] activationNonce = BaseEncoding.base64().decode("grDwkvXrgfUdKBsqg0xYYw==");
    byte[] publicKeyBytes =
        BaseEncoding.base64()
            .decode(
                "BJXfJMCANX+T9FzsG6Hi0KTYPN64i7HxMiWoMYPd17DYfBR+IwzOesTh/jj/B3trL9m3O1oODYil+8ssJzDt/QA=");
    byte[] ephemeralPrivateKeyBytes =
        BaseEncoding.base64().decode("AKeMTtivK/XRiQPhfJYxAw1L62ah4lGTQ4JKqRrr0fnC");
    byte[] masterPublicKey =
        BaseEncoding.base64()
            .decode(
                "BFOqvpLNi15eHDt8fkFxFe034Buw/i8gR3ax4fKiIQynt5K858oBBYhqLVH8FhNmMnlysnRd2UsPJSQxzoPhEn8=");

    CryptoProviderUtil keyConvertor = PowerAuthConfiguration.INSTANCE.getKeyConvertor();
    PrivateKey eph = keyConvertor.convertBytesToPrivateKey(ephemeralPrivateKeyBytes);
    PublicKey mpk = keyConvertor.convertBytesToPublicKey(masterPublicKey);

    PublicKey publicKey =
        PowerAuthConfiguration.INSTANCE.getKeyConvertor().convertBytesToPublicKey(publicKeyBytes);
    PowerAuthClientActivation activation = new PowerAuthClientActivation();

    byte[] cDevicePublicKey =
        activation.encryptDevicePublicKey(
            publicKey, eph, mpk, activationOTP, activationIdShort, activationNonce);
    assertArrayEquals(
        cDevicePublicKey,
        BaseEncoding.base64()
            .decode(
                "tnAyB0C5I9xblLlFCPONUT4GtABvutPkRvvx2oTeGIuUMAmUYTqJluKn/Zge+vbq+VArIVNYVTd+0yuBZGVtkkd1mTcc2eTDhqZSQJS6mMgmKeCqv64c6E4dm4INOkxh"));
  }
 /** Add crypto providers. */
 @Before
 public void setUp() {
   // Add Bouncy Castle Security Provider
   Security.addProvider(new BouncyCastleProvider());
   PowerAuthConfiguration.INSTANCE.setKeyConvertor(
       CryptoProviderUtilFactory.getCryptoProviderUtils());
 }
  /**
   * Decrypt the device public key using activation OTP.
   *
   * @param C_devicePublicKey Encrypted device public key.
   * @param activationIdShort Short activation ID.
   * @param activationOTP Activation OTP value.
   * @param activationNonce Activation nonce, used as an initialization vector for AES encryption.
   * @return A decrypted public key.
   */
  public PublicKey decryptDevicePublicKey(
      byte[] C_devicePublicKey,
      String activationIdShort,
      String activationOTP,
      byte[] activationNonce) {
    try {
      // Derive longer key from short activation ID and activation OTP
      byte[] activationIdShortBytes = activationIdShort.getBytes("UTF-8");
      SecretKey otpBasedSymmetricKey =
          new KeyGenerator().deriveSecretKeyFromPassword(activationOTP, activationIdShortBytes);

      // Decrypt device public key
      AESEncryptionUtils aes = new AESEncryptionUtils();
      byte[] decryptedPublicKeyBytes =
          aes.decrypt(C_devicePublicKey, activationNonce, otpBasedSymmetricKey);
      PublicKey devicePublicKey =
          PowerAuthConfiguration.INSTANCE
              .getKeyConvertor()
              .convertBytesToPublicKey(decryptedPublicKeyBytes);
      return devicePublicKey;
    } catch (IllegalBlockSizeException
        | InvalidKeySpecException
        | BadPaddingException
        | InvalidKeyException
        | UnsupportedEncodingException ex) {
      Logger.getLogger(PowerAuthServerActivation.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
  }
 /**
  * Test that the keys are correctly generated.
  *
  * @throws Exception When test fails.
  */
 @Test
 public void testGenerateKeys() throws Exception {
   CryptoProviderUtil keyConvertor = PowerAuthConfiguration.INSTANCE.getKeyConvertor();
   KeyGenerator keyGenerator = new KeyGenerator();
   KeyPair kp = keyGenerator.generateKeyPair();
   System.out.println(
       "Private Key: "
           + BaseEncoding.base64().encode(keyConvertor.convertPrivateKeyToBytes(kp.getPrivate())));
   System.out.println(
       "Public Key: "
           + BaseEncoding.base64().encode(keyConvertor.convertPublicKeyToBytes(kp.getPublic())));
 }
 /**
  * Compute a fingerprint of the device public key. The fingerprint can be used for visual
  * validation of an exchanged public key.
  *
  * @param devicePublicKey Public key for computing fingerprint.
  * @return Fingerprint of the public key.
  */
 public int computeDevicePublicKeyFingerprint(PublicKey devicePublicKey) {
   try {
     byte[] devicePublicKeyBytes =
         PowerAuthConfiguration.INSTANCE
             .getKeyConvertor()
             .convertPublicKeyToBytes(devicePublicKey);
     MessageDigest digest = MessageDigest.getInstance("SHA-256");
     byte[] hash = digest.digest(devicePublicKeyBytes);
     if (hash.length < 4) { // assert
       throw new IndexOutOfBoundsException();
     }
     int index = hash.length - 4;
     int number =
         (ByteBuffer.wrap(hash).getInt(index) & 0x7FFFFFFF)
             % (int) (Math.pow(10, PowerAuthConfiguration.FINGERPRINT_LENGTH));
     return number;
   } catch (NoSuchAlgorithmException ex) {
     Logger.getLogger(PowerAuthServerActivation.class.getName()).log(Level.SEVERE, null, ex);
   }
   return 0;
 }
  /**
   * Encrypt the server public key using activation OTP and device public key. As a technical
   * component for public key encryption, an ephemeral private key is used (in order to deduce
   * ephemeral symmetric key using ECDH).
   *
   * @param serverPublicKey Server public key to be encrypted.
   * @param devicePublicKey Device public key used for encryption.
   * @param ephemeralPrivateKey Ephemeral private key.
   * @param activationOTP Activation OTP value.
   * @param activationIdShort Short activation ID.
   * @param activationNonce Activation nonce, used as an initialization vector for AES encryption.
   * @return Encrypted server public key.
   * @throws InvalidKeyException In case some of the provided keys is invalid.
   */
  public byte[] encryptServerPublicKey(
      PublicKey serverPublicKey,
      PublicKey devicePublicKey,
      PrivateKey ephemeralPrivateKey,
      String activationOTP,
      String activationIdShort,
      byte[] activationNonce)
      throws InvalidKeyException {
    try {

      // Convert public key to bytes
      byte[] serverPublicKeyBytes =
          PowerAuthConfiguration.INSTANCE
              .getKeyConvertor()
              .convertPublicKeyToBytes(serverPublicKey);

      // Generate symmetric keys
      KeyGenerator keyGenerator = new KeyGenerator();
      SecretKey ephemeralSymmetricKey =
          keyGenerator.computeSharedKey(ephemeralPrivateKey, devicePublicKey);

      byte[] activationIdShortBytes = activationIdShort.getBytes("UTF-8");
      SecretKey otpBasedSymmetricKey =
          keyGenerator.deriveSecretKeyFromPassword(activationOTP, activationIdShortBytes);

      // Encrypt the data
      AESEncryptionUtils aes = new AESEncryptionUtils();
      byte[] encryptedTMP =
          aes.encrypt(serverPublicKeyBytes, activationNonce, otpBasedSymmetricKey);
      byte[] encryptServerPublicKey =
          aes.encrypt(encryptedTMP, activationNonce, ephemeralSymmetricKey);
      return encryptServerPublicKey;

    } catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException ex) {
      Logger.getLogger(PowerAuthServerActivation.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
  }