/*
 * 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 com.intellij.util.EncryptionSupport;
import org.jetbrains.annotations.NotNull;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.security.Key;
import java.util.Properties;

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

  public PropertiesEncryptionSupport(Key key) {
    myEncryptionSupport = new EncryptionSupport(key);
  }

  public PropertiesEncryptionSupport() {
    myEncryptionSupport = new EncryptionSupport();
  }

  @NotNull
  public Properties load(@NotNull File file) throws Exception {
    return load(FileUtil.loadFileBytes(file));
  }

  @NotNull
  public Properties load(@NotNull byte[] data) throws Exception {
    final byte[] bytes = myEncryptionSupport.decrypt(data);
    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, myEncryptionSupport.encrypt(out.toByteArray(), out.size()));
  }

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

  public 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 {
    final int bodyLength = ((data[0] & 0xFF) << 24) |
                           ((data[1] & 0xFF) << 16) |
                           ((data[2] & 0xFF) <<  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);
  }

  //public static void main(String[] args) throws Exception {
  //  final PropertiesEncryptionSupport support = new PropertiesEncryptionSupport();
  //  final Charset charset = Charset.forName("utf-8");
  //  final SecureRandom rnd = new SecureRandom();
  //  for (int idx = 0; idx < 90; idx++) {
  //    final String message = new BigInteger(10 * 1024, rnd).toString();
  //    final byte[] encrypted = support.encrypt(message.getBytes(charset));
  //    String decrypted = new String(support.decrypt(encrypted), charset);
  //    if (message.equals(decrypted)) {
  //      System.out.println("Test " + idx + " ok");
  //    }
  //    else {
  //      System.out.println("Test " + idx + " FAILED");
  //    }
  //  }
  //}


}

@@ -685,87 +685,153 @@
 ort 
-com.intellij.util.EncryptionSupport;%0Aimport org.jetbrains.annotations.NotNull;%0A
+org.jetbrains.annotations.NotNull;%0A%0Aimport javax.crypto.Cipher;%0Aimport javax.crypto.spec.IvParameterSpec;%0Aimport javax.crypto.spec.SecretKeySpec;
 %0Aimp
@@ -949,16 +949,51 @@
 ty.Key;%0A
+import java.security.SecureRandom;%0A
 import j
@@ -1140,45 +1140,17 @@
 nal 
-EncryptionSupport myEncryptionSupport
+Key myKey
 ;%0A%0A 
@@ -1206,54 +1206,17 @@
   my
-EncryptionSupport = new EncryptionSupport(
+Key = 
 key
-)
 ;%0A  
@@ -1267,179 +1267,191 @@
     
-myEncryptionSupport = new EncryptionSupport();%0A  %7D%0A%0A  @NotNull%0A  public Properties load(@NotNull File file) throws Exception %7B%0A    return load(FileUtil.loadFileBytes(file)
+this(generateKey());%0A  %7D%0A%0A  public static Key generateKey() %7B%0A    final byte%5B%5D bytes = new byte%5B16%5D;%0A    new SecureRandom().nextBytes(bytes);%0A    return new SecretKeySpec(bytes, %22AES%22
 );%0A 
@@ -1499,27 +1499,25 @@
 NotNull 
-byte%5B%5D data
+File file
 ) throws
@@ -1558,40 +1558,44 @@
 s = 
-myEn
+de
 crypt
-ionSupport.decrypt(data
+(FileUtil.loadFileBytes(file)
 );%0A 
@@ -1975,28 +1975,8 @@
 le, 
-myEncryptionSupport.
 encr
@@ -2000,20 +2000,8 @@
 ay()
-, out.size()
 ));%0A

fix merge