在日常开发中的安全测试或安全功能研发时经常遇到加解密,涉及到对称加密和非对称加密。
在对称加密中常用DES(data encrption standard)算法和AES算法
DES:DES 加密算法是一种分组密码,以 64 位为分组对数据加密,它的密钥长度是 56 位,加密解密用同一算法。DES 加密算法是对密钥进行保密,而公开算法,包括加密和解密算法。这样,只有掌握了和发送方相同密钥的人才能解读由 DES 加密算法加密的密文数据。因此,破译 DES 加密算法实际上就是搜索密钥的编码。对于 56 位长度的密钥来说,如果用穷举法来进行搜索的话,其运算次数为 2 的 56 次方。
public class DES {
private final static String DES = "DES";
public static void main(String[] args) throws Exception {
String data = "DES对称加密算法";
String key = "asdfghjkl123456789"; //秘钥设置的长度必须大于等于 8
System.err.println(encrypt(data, key));
System.err.println(decrypt(encrypt(data, key), key));
}
/**
* Description 根据键值进行加密
*/
public static String encrypt(String data, String key) throws Exception {
byte[] bt = encrypt(data.getBytes(), key.getBytes());
String strs = new BASE64Encoder().encode(bt);
return strs;
}
/**
* Description 根据键值进行解密
*/
public static String decrypt(String data, String key) throws IOException, Exception
{
if (data == null) {
return null;
}
BASE64Decoder decoder = new BASE64Decoder();
byte[] buf = decoder.decodeBuffer(data);
byte[] bt = decrypt(buf,key.getBytes());
return new String(bt);
}
/**
* Description 根据键值进行加密
*/
private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
// 生成一个可信任的随机数源
SecureRandom sr = new SecureRandom();
// 从原始密钥数据创建DESKeySpec对象
DESKeySpec dks = new DESKeySpec(key);
// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance(DES);
// 用密钥初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
return cipher.doFinal(data);
}
/**
* Description 根据键值进行解密
*/
private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
// 生成一个可信任的随机数源
SecureRandom sr = new SecureRandom();
// 从原始密钥数据创建DESKeySpec对象
DESKeySpec dks = new DESKeySpec(key);
// 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance(DES);
// 用密钥初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
return cipher.doFinal(data);
}
}
//打印
JozfxOVCs+RpROTmdXQtaE5mu97tjoNQ
DES对称加密算法
AES:AES 加密算法是密码学中的高级加密标准,该加密算法采用对称分组密码体制,密钥长度的最少支持为 128、192、256,分组长度 128 位,算法应易于各种硬件和软件实现。这种加密算法是美国联邦政府采用的区块加密标准,AES 标准用来替代原先的 DES,已经被多方分析且广为全世界所使用。
public class AES {
private final static String AES = "AES";
private final static String ALGORITHM = "AES/ECB/PKCS5Padding";
public static void main(String[] args) throws Exception {
// 此处使用AES-128-ECB加密模式,key需要为16位。
String cKey = "1234567890123456";
// 需要加密的字串
String cSrc = "DES对称加密算法";
System.out.println("原文是:" + cSrc);
// 加密
String enString = Encrypt(cSrc, cKey);
System.out.println("加密后的字串是:" + enString);
// 解密
String DeString = Decrypt(enString, cKey);
System.out.println("解密后的字串是:" + DeString);
}
// 加密
public static String Encrypt(String sSrc, String sKey) throws Exception {
if (sKey == null) {
System.out.print("Key为空null");
return null;
}
// 判断Key是否为16位
if (sKey.length() != 16) {
System.out.print("Key长度不是16位");
return null;
}
byte[] raw = sKey.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
Cipher cipher = Cipher.getInstance(ALGORITHM);//"算法/模式/补码方式"
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
return new Base64().encodeToString(encrypted);//此处使用BASE64做转码功能,同时能起到2次加密的作用。
}
// 解密
public static String Decrypt(String sSrc, String sKey) throws Exception {
try {
// 判断Key是否正确
if (sKey == null) {
System.out.print("Key为空null");
return null;
}
// 判断Key是否为16位
if (sKey.length() != 16) {
System.out.print("Key长度不是16位");
return null;
}
byte[] raw = sKey.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
try {
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original,"utf-8");
return originalString;
} catch (Exception e) {
System.out.println(e.toString());
return null;
}
} catch (Exception ex) {
System.out.println(ex.toString());
return null;
}
}
}
//打印
原文是:DES对称加密算法
加密后的字串是:dT8DU7s8uAWviSBWHA7F5I2aeUSqIXpF6I7CxbmTzY4=
解密后的字串是:DES对称加密算法
在非对称加密中常用RSA算法
RSA:生成密钥,介绍3中方法
- 命令行:可以使用openssl进行生成公钥和私钥。
-- 生成公钥和私钥
openssl genrsa -out key.pem 1024
-out 指定生成文件,此文件包含公钥和私钥两部分,所以即可以加密,也可以解密
1024 生成密钥的长度
- 使用网站:生成密钥的网站,百度一下
- 使用代码:可以指定生成密钥的长度,最低是 512。
public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
final int keySize = 2048;
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGenerator.initialize(keySize);
return keyPairGenerator.genKeyPair();
}
public class RSA {
private static final Base64.Decoder decoder = Base64.getDecoder();
private static final Base64.Encoder encoder = Base64.getEncoder();
public static void main(String[] args) throws Exception {
KeyPair keyPair = buildKeyPair();
String message = "RSA非对称加密算法";
byte[] encryptData = encrypt(keyPair.getPublic(), message);
System.out.println(String.format("加密后的数据:%s",base64Encode(encryptData)));
System.out.println(String.format("解密后的数据:%s",new String(decrypt(keyPair.getPrivate(),encryptData),"utf-8")));
String context = "加签的字符串";
String sign = signWithRSA(context, keyPair.getPrivate());
System.out.println(String.format("生成的签名:%s",sign));
Boolean checkSignWithRSA = checkSignWithRSA(context, keyPair.getPublic(), sign);
System.out.println(String.format("校验的结果:%s",checkSignWithRSA.toString()));
}
/**
* 生成密钥对
*/
public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
final int keySize = 2048;
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(keySize);
return keyPairGenerator.genKeyPair();
}
/**
* 加密
*/
public static byte[] encrypt(PublicKey publicKey, String message) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(message.getBytes("utf-8"));
}
/**
* 解密
*/
public static byte[] decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encrypted);
}
/**
* 使用RSA签名
*/
private static String signWithRSA(String content, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initSign(privateKey);
signature.update(content.getBytes("utf-8"));
byte[] signed = signature.sign();
return base64Encode(signed);
}
/**
* 使用RSA验签
*/
private static boolean checkSignWithRSA(String content, PublicKey publicKey,String sign) throws Exception {
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initVerify(publicKey);
signature.update(content.getBytes("utf-8"));
return signature.verify(base64Decode(sign));
}
private static String base64Encode(byte[] signed) {
//编码
String encodedText = encoder.encodeToString(signed);
return encodedText;
}
private static byte[] base64Decode(String sign) throws UnsupportedEncodingException {
//解码
byte[] decode = decoder.decode(sign);
return decode;
}
}
//打印
加密后的数据:j56URGDJjS2Meo3P698aj3JqZKKpOJIOghTawsdi1+ASQb9FgsC2QDPF/w78aghIo0SEY6Q7H7p2Slys/wH1VW5zFfy5/IdK6CK44nvnHMs+NmodCJrSeimoZnuFC92WRqryWgSC2l25pGaHrEVcR5jR7V6Mk8677TyUpDjyHPIPwAHBUVn8mydxKR0BtgP2J5pvFZJip7T+5sPavGixKLHHgRC+B660OHwPOGUXfvaNDjN6l5crSQlfNMBep9dzKLh/22RQHUVf8HkKTwR38LQM+sNvR23RE8q+Q2W/+mEx5WZLkXBBlcRQ4Ww/UvkmmADIxuurblUBtkP0mENEzw==
解密后的数据:RSA非对称加密算法
生成的签名:WezHTvgJExCvAL1gJbEsfAOVntzFac9FDmtX3qa1BO33QwHWOKGbsdH2Drk2MIVWS1db7grqwifi4w60tzH0Q7SU1iCJ1TOcQR4EI3PbIsY9VkLD+nRRyJKyTRstN8S+saJDV5NIzA9wb7x170DsIaUphLslU1XQR1Y0c7HY4WklzMfQ25kLIc7ocJP29tzknEnkcElqQVwxiGNIZehWmmHJZ2JMcbdTtLZEMtW5FC3pEhrvUtrEwENWhc4g8Dvix9LrSVoxPmRslMeL8ldHj5Ec4UsXR4mZldBKpdc5PO8oo6HbIFP2eX14ACpPFCQQnAjtQZ+9TOP/KuDzXduClQ==
校验的结果:true
加签和验签
公钥是用来解密信息的,确保让别人知道这条信息是真的由我发布的,是完整正确的。接收者由此可知这条信息确实来自于拥有私钥的某人,这被称作数字签名,公钥的形式就是数字证书。所以这种我们称之为加签和验签。举例,银行 A 发布了一个银行客户端的补丁供所有用户更新,那为了确保人家下载的是正确完整的客户端,银行A会为这个程序打上一个数字签名(就是用银行A的私钥对这个程序加密然后发布),你需要在你的电脑里装上银行 A 的数字证书(就是银行对外发布的公钥),然后下载好这个程序,数字证书会去解密这个程序的数字签名,解密成功,补丁得以使用。同时你能知道这个补丁确实是来自这个银行 A,是由他发布的,而不是其他人发布的。
摘要算法常用MD5算法
数据摘要算法是密码学算法中非常重要的一个分支,它通过对所有数据提取指纹信息以实现数据签名、数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密。数据摘要算法也被称为哈希(Hash)算法或散列算法。
public class MD5 {
public static void main(String[] args) {
String message = "123456789";
try {
String md5Str = getMD5Str(message);
System.out.println(md5Str);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getMD5Str(String str) throws Exception {
try {
// 生成一个MD5加密计算摘要
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(str.getBytes());
// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
throw new Exception("MD5加密出现错误,"+e.toString());
}
}
}
// 打印
25f9e794323b453885f5181f1b624d0b
或
public class MD5Util {
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
public static void main(String[] args) {
String sign = "3ae14696f82a547cfce841651b67342a1";
String message = "摘要";
String signature = encode(message);
System.out.println(signature);
boolean flag = signature.equals(sign);
Assert.isTrue(flag, "签名错误");
}
public static String encode(String origin) {
return encode(origin, "UTF-8");
}
public static String encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} else {
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
}
} catch (Exception exception) {
}
return resultString;
}
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
}
// 打印
3ae14696f82a547cfce841651b67342a