/* * 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 { if (props.isEmpty()) { FileUtil.delete(file); return; } final ByteArrayOutputStream out = new ByteArrayOutputStream(); props.store(out, comments); out.close(); FileUtil.writeToFile(file, encrypt(out.toByteArray())); } 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"); // } // } //} }