前幾日和同事聊天,說到經常忘記密碼,但記在一些地方,又不放心。我們就想,要是實現一個加密算法,每次記錄加密結果,需要時再解密,那問題不就迎刃而解了嘛。
後來根據需求,設計了一個基於異或的對稱加密算法。算法非常簡單:
1.先定義一個由一系列不規則字符組成的鹽值(SALT),然後生成一個密鑰,密鑰是一個整數數組,裏面的值隨機生成,對應鹽值字符串的下標。
2.加密時,根據密鑰與鹽值生成一個用來加密的字節數組。
3.通過這個字節數組,與被加密數據的字節數組進行異或操作
4.返回加密結果,由於異或的特性,解密過程同加密過程。
後來經過一些修改,將加密的字節數組轉換爲對應的16進制字符串(4位二進制表示一位16進制,根據這個規律生成)。這樣就能將任何數據加密爲一串字符串,解密時,先將這串字符串轉換爲對應的字節數組,再進行解密還原。
老規矩,上王者(呸,代碼 最近有些沉迷遊戲):
package com.myself.tryit;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA
* Date: 2017/12/8
* Time: 上午11:04
*
* @author 陳樟傑
*/
public class EncryptDecrypt {
/**
* 鹽值
*/
private static final String SALT = "dfsad@#%$@TDGDF%$#%@#%WFRGFDHJKcvxznmfdsgdfgs2432534fgdf46t";
/**
* 內部密鑰
*/
private static final int[] KEY = {
23, 22, 24, 4, 51, 26, 37, 27, 24, 6, 26, 38, 29, 35, 18, 21, 14, 3, 12, 4, 41, 39, 18, 44, 54, 21, 33, 35, 31, 22, 34, 53, 51, 44, 8, 12, 3, 0, 28, 1, 48, 9, 51, 57, 20, 44, 27, 3, 16, 48
};
/**
* 16進制映射表
*/
private static final Map MMP = new HashMap(12);
static {
short i = 10;
for (; i < 16; i++) {
MMP.put(i, (char) ('A' + i - 10));
MMP.put((char) ('A' + i - 10), i);
}
}
/**
* 生成密鑰
*
* @param len 密鑰長度
* @return
*/
public static int[] generateKey(int len) {
int ceiling = SALT.length();
int[] key = new int[len];
for (int i = 0; i < len; i++) {
key[i] = (int) (Math.random() * ceiling);
}
return key;
}
/**
* 加密
*
* @param original
* @return
*/
public static byte[] encrypt(byte[] original) {
byte[] keyByte = new byte[KEY.length];
/**
* 獲取加密字節數組
*/
for (int i = 0; i < KEY.length; i++) {
keyByte[i] = (byte) SALT.charAt(KEY[i]);
}
/**
* 加密
*/
int k = 0;
byte[] encryptByte = new byte[original.length];
for (int i = 0; i < original.length; i++) {
encryptByte[i] = (byte) (original[i] ^ keyByte[k++ % keyByte.length]);
}
return encryptByte;
}
/**
* 解密
*
* @param original
* @return
*/
public static byte[] decrypt(byte[] original) {
return encrypt(original);
}
/**
* 對字符串加密
* 由於解碼時 byte數組不一定符合規範 所以解碼算法可能會改變字節數組的值 所以只能返回字節數組 而不能返回字符串
*
* @param original
* @return
*/
public static byte[] encryptString(String original) {
return encrypt(original.getBytes());
}
/**
* 對字符串解密
*
* @param original
* @return
*/
public static String decryptToString(byte[] original) {
return new String(encrypt(original));
}
/**
* 加密成16進制字符串
*
* @param original
* @return
*/
public static String encryptToHex(byte[] original) {
//先加密
byte[] bytes = encrypt(original);
/**
* 將加密的字節數組轉換成16進制字符串
*/
StringBuffer stringBuffer = new StringBuffer("");
for (int i = 0; i < bytes.length; i++) {
short a = (short) (bytes[i] & 15);
short b = (short) ((bytes[i] & (15 << 4)) >>> 4);
if (b < 10) {
stringBuffer.append(b);
} else {
stringBuffer.append(MMP.get(b));
}
if (a < 10) {
stringBuffer.append(a);
} else {
stringBuffer.append(MMP.get(a));
}
}
return stringBuffer.toString();
}
/**
* 從16進制字符串解密
*
* @param original
* @return
*/
public static byte[] decryptFromHex(String original) {
/**
* 將16進制字符串轉換成字節數組
*/
byte[] bytes = new byte[original.length() / 2];
int len = 0;
for (int i = 0; i < original.length(); i += 2) {
short a, b;
if (original.charAt(i) >= '0' && original.charAt(i) <= '9') {
a = (short) (original.charAt(i) - '0');
a <<= 4;
} else {
a = (short) MMP.get(original.charAt(i));
a <<= 4;
}
if (original.charAt(i + 1) >= '0' && original.charAt(i + 1) <= '9') {
b = (short) (original.charAt(i + 1) - '0');
} else {
b = (short) MMP.get(original.charAt(i + 1));
}
bytes[len++] = (byte) (a + b);
}
//解密字節數組
return decrypt(bytes);
}
/**
* 將字符串加密成16進制字符串
*
* @param original
* @return
*/
public static String encryptStringToHex(String original) {
return encryptToHex(original.getBytes());
}
/**
* 從16進制字符串解密成原字符串
*
* @param original
* @return
*/
public static String decryptStringFromHex(String original) {
return new String(decryptFromHex(original));
}
/**
* 加密
*/
private void doEncrypt() {
System.err.println("-------------加密-------------");
Scanner scanner = new Scanner(System.in);
String original = scanner.next();
System.out.println(encryptStringToHex(original));
}
/**
* 解密
*/
private void doDecrypt() {
System.err.println("-------------解密-------------");
Scanner scanner = new Scanner(System.in);
String original = scanner.next();
System.out.println(decryptStringFromHex(original));
}
public static void main(String[] args) {
//加密
new EncryptDecrypt().doEncrypt();
//解密
// new EncryptDecrypt().doDecrypt();
}
@Test
public void test() {
int[] keys = generateKey(SALT.length());
for (int k : keys) {
System.out.print(k+", ");
}
System.out.println();
String original = "窮且益堅,不墜青雲之志。";
String encrypt = encryptStringToHex(original);
System.out.println(encrypt);
System.out.println(decryptStringFromHex(encrypt));
encrypt = encryptToHex(original.getBytes());
System.out.println(encrypt);
System.out.println(new String(decryptFromHex(encrypt)));
original = "人生得意須盡歡,莫使金樽空對月。";
byte[] bytes = encryptString(original);
System.out.println(new String(bytes));
System.out.println(decryptToString(bytes));
bytes = encrypt(original.getBytes());
System.out.println(new String(bytes));
System.out.println(new String(decrypt(bytes)));
}
}
本人並不是專業研究安全領域的人,只是有個想法,然後順便實現了一下。所以我也不清楚這個算法的安全性有多高,但至少用着方便,我也能安心記密碼了。(表示連銀行卡的密碼都經常忘,好在現在支付寶都支持指紋了。)