/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.util.proxy;

import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Properties;

/**
 * @author Eugene Zhuravlev
 *         Date: 08-Jul-16
 */
public class PropertiesEncryptionSupport {
  private final Key myKey;

  public PropertiesEncryptionSupport(Key key) {
    myKey = key;
  }

  public PropertiesEncryptionSupport() {
    this(generateKey());
  }

  public static Key generateKey() {
    final byte[] bytes = new byte[16];
    new SecureRandom().nextBytes(bytes);
    return new SecretKeySpec(bytes, "AES");
  }

  @NotNull
  public Properties load(@NotNull File file) throws Exception {
    final byte[] bytes = decrypt(FileUtil.loadFileBytes(file));
    final Properties props = new Properties();
    props.load(new ByteArrayInputStream(bytes));
    return props;
  }

  public void store(@NotNull Properties props, @NotNull String comments, @NotNull File file) throws Exception {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    props.store(out, comments);
    out.close();
    FileUtil.writeToFile(file, encrypt(out.toByteArray()));
  }

  private byte[] encrypt(byte[] bytes) throws Exception {
    return encrypt(bytes, myKey);
  }

  private byte[] decrypt(byte[] bytes) throws Exception {
    return decrypt(bytes, myKey);
  }

  private static byte[] encrypt(byte[] msgBytes, Key key) throws Exception {
    final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
    ciph.init(Cipher.ENCRYPT_MODE, key);
    final byte[] body = ciph.doFinal(msgBytes);
    final byte[] iv = ciph.getIV();

    final byte[] data = new byte[4 + iv.length + body.length];

    final int length = body.length;
    data[0] = (byte)((length >> 24)& 0xFF);
    data[1] = (byte)((length >> 16)& 0xFF);
    data[2] = (byte)((length >> 8)& 0xFF);
    data[3] = (byte)(length & 0xFF);

    System.arraycopy(iv, 0, data, 4, iv.length);
    System.arraycopy(body, 0, data, 4 + iv.length, body.length);
    return data;
  }

  private static byte[] decrypt(byte[] data, Key key) throws Exception {
    int bodyLength = data[0] & 0xFF;
    bodyLength = (bodyLength << 8) + data[1] & 0xFF;
    bodyLength = (bodyLength << 8) + data[2] & 0xFF;
    bodyLength = (bodyLength << 8) + data[3] & 0xFF;

    final int ivlength = data.length - 4 - bodyLength;

    final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
    ciph.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(data, 4, ivlength));
    return ciph.doFinal(data, 4 + ivlength, bodyLength);
  }

}

@@ -2000,38 +2000,37 @@
 ay()));%0A  %7D%0A%0A  p
-rivate
+ublic
  byte%5B%5D encrypt(
@@ -2104,22 +2104,21 @@
   %7D%0A%0A  p
-rivate
+ublic
  byte%5B%5D 
@@ -2950,16 +2950,22 @@
 n %7B%0A    
+final 
 int body
@@ -2969,24 +2969,26 @@
 odyLength = 
+((
 data%5B0%5D & 0x
@@ -2993,153 +2993,156 @@
 0xFF
-;%0A    bodyLength = (bodyLength %3C%3C 8) + data%5B1%5D & 0xFF;%0A    bodyLength = (bodyLength %3C%3C 8) + data%5B2%5D & 0xFF;%0A    bodyLength = (bodyLength %3C%3C 8) + 
+) %3C%3C 24) %7C%0A                           ((data%5B1%5D & 0xFF) %3C%3C 16) %7C%0A                           ((data%5B2%5D & 0xFF) %3C%3C  8) %7C%0A                           ((
 data
@@ -3151,16 +3151,24 @@
 %5D & 0xFF
+)      )
 ;%0A%0A    f
@@ -3429,10 +3429,736 @@
 );%0A  %7D%0A%0A
+  //public static void main(String%5B%5D args) throws Exception %7B%0A  //  final PropertiesEncryptionSupport support = new PropertiesEncryptionSupport();%0A  //  final Charset charset = Charset.forName(%22utf-8%22);%0A  //  final SecureRandom rnd = new SecureRandom();%0A  //  for (int idx = 0; idx %3C 90; idx++) %7B%0A  //    final String message = new BigInteger(10 * 1024, rnd).toString();%0A  //    final byte%5B%5D encrypted = support.encrypt(message.getBytes(charset));%0A  //    String decrypted = new String(support.decrypt(encrypted), charset);%0A  //    if (message.equals(decrypted)) %7B%0A  //      System.out.println(%22Test %22 + idx + %22 ok%22);%0A  //    %7D%0A  //    else %7B%0A  //      System.out.println(%22Test %22 + idx + %22 FAILED%22);%0A  //    %7D%0A  //  %7D%0A  //%7D%0A%0A%0A
 %7D%0A

fix io (IDEA-158917)