这是「进击的Coder」的第 652 篇技术分享
作者:Python 进阶者
来源:Python 爬虫与数据挖掘
“
阅读本文大概需要 13 分钟。
”PS:本教程只用于学习探讨,不允许任何人使用技术进行违法操作,阅读教程即表示同意
在搞逆向进行抓包的时候,可以经常发现一些莫名其妙的字符串,可能是81dc9bdb52d04dc20036dbd8313ed055
等之类的一长串字符,这些是怎么生成呢?
这些其实就是加密,加密算法主要分为两大类
标准加密算法
非标准加密算法
标准加密算法在任何语言中的实现,结果都是一样的。
是应该是一样的,也可能不一样,如果不一样,说明更改了标准算法的某些变量,但是这种情况比较少。
非标准算法那就是自己写的了,这就具有很大的不确定性了,全靠程序员发挥!
注意:
在安卓逆向中,加密算法通常出现在 Java 层和 C++ 中!
在 Java 层标准算法是有固定名字的,即使再混淆,固定名字是不能混淆的所以比较好处理!
在 C++ 层标准加密算法是没有固定名字的,那就只能根据算法特征去识别了!
消息摘要算法(散列函数、哈希函数) MD5、SHA、MAC
对称加密算法 DES、3DES、AES
非对称加密算法 RSA
数字签名算法 MD5withRSA、SHA1withRSA、SHA256withRSA
因为本次主要是安卓逆向,所以就将常用的标准加密算法使用 Android 来复现一下!
AndroidStudio 2020.3.1版本
Jdk 8版本
CryptologyDemo.zip
Hex 和 Base64 不是加密,它是一种编码!!!
Hex 和 Base64 编码是加密算法中最常用的编码,任何加密算法最终都要选择它的表现形式,而 Hex 和 Base64 是最常用的!
api 'com.squareup.okhttp3:okhttp:3.10.0'
记得点击 Sync Now
Hex 编码是一种用 16 个字符 (0-9 a-f) 表示任意二进制数据的方法!
它是一种编码,而非加密!
Hex 主要应用在 MD5 等加密表现形式上。
//从字符串到hex
byte[] bytes = "zhangsan".getBytes(StandardCharsets.UTF_8);
ByteString of = ByteString.of(bytes);
String hex = of.hex();
Log.d(TAG, "hex:" + hex);
Base64 是一种用 64 个字符 (A-Z a-z 0-9 + / =) 表示任意二进制数据的方法。
它是一种编码,而非加密。
相比较之下,Base64 应用就广泛的很多,像图片,,长密文甚至文件,都采用 Base64,因为可承载的数据很多!
//从字符串到base64
byte[] bytes = "zhangsan".getBytes(StandardCharsets.UTF_8);
ByteString of = ByteString.of(bytes);
//方式一
String base64 = of.base64();
Log.d(TAG, "base64_1:" + base64);
//方式二
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
String s = Base64.getEncoder().encodeToString("zhangsan".getBytes(StandardCharsets.UTF_8));
byte[] encode = Base64.getEncoder().encode("zhangsan".getBytes(StandardCharsets.UTF_8));
Log.d(TAG, "base64_2:" + s);
Log.d(TAG, "base64_2:" + new String(encode));
}
//方式三
String s = android.util.Base64.encodeToString("zhangsan".getBytes(StandardCharsets.UTF_8),0);
Log.d(TAG, "base64_3:" + new String(s));
消息摘要算法最主要的特征!
密文是不可逆的!
就是说,我在客户端把密码通过 md5 加密了,服务端也得采用相同的方式加密,进行比较。
不定长度输入,固定长度输出
就是说,不管是 123,还是 123456... 经过加密,加密的结果都是固定的长度!
加密结果唯一!
这就是最常用的 md5 加密,在 update 时压入数据,通过 digest 获得加密结果,md5 一般通过 hex 展示加密结果!
算法 | 摘要长度 | 实现 |
---|---|---|
MD2 | 128 | Java6 |
MD5 | 128 | Java6 |
MD5 | 128 | Bouncy Castle |
//md5
public static String md5(String plainText) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
//1. md5加密的数据可以直接放在digest中
//2. digest是加密之后的数据,但是有不可见字符,不要使用hex或base64来展示
md5.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] digest = md5.digest();
//1. 效果完全同上,update可以压入数据,区别是digest是一次性压入,update可以分批次压入
//byte[] digest = md5.digest(plainText.getBytes(StandardCharsets.UTF_8));
//使用hex和base64来表示加密之后的数据,因为直接加密的有不可见字符
ByteString of = ByteString.of(digest);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
常用的是 sha-1 算法,所以本次演示的是 sha-1 算法。
sha-1 算法,甚至来说消息摘要算法基本上 api 都是通用的。
只需要换一个algorithm
即可,所以就不废话了。
算法 | 摘要长度 | 实现 |
---|---|---|
SHA-1 | 160 | Java6 |
SHA-256 | 256 | Java6 |
SHA-384 | 384 | Java6 |
SHA-512 | 512 | Java6 |
SHA224 | 224 | Bouncy Castle |
//SHA-1
public static String sha_1(String plainText) throws Exception {
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
sha1.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] digest = sha1.digest();
ByteString of = ByteString.of(digest);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
mac 这个名字听着挺牛逼的,其实就是比 md5 和 sha 算法多了个密钥而已,不必大惊小怪。
算法 | 消息摘要 | 实现 |
---|---|---|
HmacMD5 | 128 | Java6 |
HmacSHA1 | 160 | Java6 |
HmacSHA256 | 256 | Java6 |
HmacSHA384 | 384 | Java6 |
HmacSHA512 | 512 | Java6 |
HmacMD2 | 128 | Java6 |
HmacMD4 | 128 | Bouncy Castle |
HmacSHA224 | 224 | Bouncy Castle |
public static String mac(String plainText) throws Exception {
//生成密钥
SecretKeySpec hmacMD5 = new SecretKeySpec("123".getBytes(StandardCharsets.UTF_8), "HmacMD5");
//hmacMD5.getAlgorithm()表示获取算法,此时获取的就是HmacMD5
Mac instance = Mac.getInstance(hmacMD5.getAlgorithm());
//同上
//Mac instance = Mac.getInstance("HmacMD5");
//初始化
instance.init(hmacMD5);
//压入数据
instance.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] doFinal = instance.doFinal();
//同上
//byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
ByteString of = ByteString.of(doFinal);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
虽然 md5 也叫加密算法,但是他是无法解密的,但是对称加密算法是可以进行加密和解密的,这就 nice 很多了。
因为加密和解密使用的密钥相同,所以叫做对称加密算法,那不同的,就是非对称咯!
注意了啊,对称加密算法的密钥是可以随便给的,但是有长度要求的,不是乱给的,但是加密的内容无限制。
各算法密钥长度
RC4 密钥长度 1~256 字节
DES 密钥长度 8 字节
3DES/DESede/TripleDES 密钥长度 24 字节
AES 密钥长度 16,24,32 字节
根据密钥长度不同 AES 又分为 AES-128,AES-192,AES-256
ECB 和 CBC 模式主要区别在于 CBC 模式需要一个 iv 向量!
//DES ECB 加密 Cipher
public static String des_encrypt_ECB(String plainText) throws Exception {
//生成des所需要的key
SecretKeySpec desKey = new SecretKeySpec("12345678".getBytes(StandardCharsets.UTF_8), "DES");
//默认工作模式就是ECB,填充模式PKCS5Padding,
//Cipher instance = Cipher.getInstance("DES");
//也可以写全
Cipher instance = Cipher.getInstance("DES/ECB/PKCS5Padding");
//初始化,指定是加密模式还是解密模式和密钥
instance.init(Cipher.ENCRYPT_MODE, desKey);
//关于Cipher的update似乎有些问题,所以用doFinal的多
//加密内容,返回结果
byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
ByteString of = ByteString.of(doFinal);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
//DES ECB 解密
public static String des_decrypt_ECB(byte[] cipherBytes) throws Exception {
//生成des所需要的key
SecretKeySpec desKey = new SecretKeySpec("12345678".getBytes(StandardCharsets.UTF_8), "DES");
Cipher instance = Cipher.getInstance("DES/ECB/PKCS5Padding");
instance.init(Cipher.DECRYPT_MODE, desKey);
byte[] doFinal = instance.doFinal(cipherBytes);
return new String(doFinal);
}
//ECB
String des_encrypt_ECP = des_encrypt_ECB("zhangsan");
Log.d(TAG, "des加密,ECP模式:" + des_encrypt_ECP);
//加密拿到的des加密,ECP模式base结果为:AtLfLL8jc1n+uVm31GQvyw==
byte[] bytes1 = ByteString.decodeBase64("AtLfLL8jc1n+uVm31GQvyw==").toByteArray();
String s1 = des_decrypt_ECB(bytes1);
Log.d(TAG, "des解密,ECP模式:" + s1);
CBC 模式就比 ECB 多了个 iv 向量而已,其他用法一样。
//DES CBC,需要iv向量
public static String des_encrypt_CBC(String plainText) throws Exception {
SecretKeySpec desKey = new SecretKeySpec("12345678".getBytes(StandardCharsets.UTF_8), "DES");
Cipher instance = Cipher.getInstance("DES/CBC/PKCS5Padding");
//CBC需要iv向量
IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
//初始化时添加上iv向量
instance.init(Cipher.ENCRYPT_MODE, desKey,ivParameterSpec);
byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
ByteString of = ByteString.of(doFinal);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
//DES CBC 解密
public static String des_decrypt_CBC(byte[] cipherBytes) throws Exception {
//生成des所需要的key
SecretKeySpec desKey = new SecretKeySpec("12345678".getBytes(StandardCharsets.UTF_8), "DES");
Cipher instance = Cipher.getInstance("DES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
instance.init(Cipher.DECRYPT_MODE, desKey,ivParameterSpec);
byte[] doFinal = instance.doFinal(cipherBytes);
return new String(doFinal);
}
DESede 也分 CBC 和 ECB,使用方法同上,这里将他们合二为一!
//DESede
public static String DESede_encrypt(String plainText) throws Exception {
SecretKeySpec desKey = new SecretKeySpec("123456781234567812345678".getBytes(), "DESede");
//ECB模式
// Cipher instance = Cipher.getInstance("DESede/ECB/PKCS5Padding");
// instance.init(Cipher.ENCRYPT_MODE, desKey);
// byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
//CBC模式需要iv向量
Cipher instance = Cipher.getInstance("DESede/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
//初始化时添加上iv向量
instance.init(Cipher.ENCRYPT_MODE, desKey, ivParameterSpec);
byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
ByteString of = ByteString.of(doFinal);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
//DESede
public static String DESede_decrypt(byte[] cipherBytes) throws Exception {
//生成des所需要的key
SecretKeySpec desKey = new SecretKeySpec("123456781234567812345678".getBytes(StandardCharsets.UTF_8), "DESede");
//ECB模式
// Cipher instance = Cipher.getInstance("DESede/ECB/PKCS5Padding");
// instance.init(Cipher.DECRYPT_MODE, desKey);
// byte[] doFinal = instance.doFinal(cipherBytes);
//CBC模式
Cipher instance = Cipher.getInstance("DESede/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
instance.init(Cipher.DECRYPT_MODE, desKey,ivParameterSpec);
byte[] doFinal = instance.doFinal(cipherBytes);
return new String(doFinal);
}
//DESede
String deSede_encrypt = DESede_encrypt("zhangsan");
Log.d(TAG, "DESede加密:" + deSede_encrypt);
//AtLfLL8jc1n+uVm31GQvyw==
byte[] bytes3 = ByteString.decodeBase64("3M7YukhZweaysZBNnqYLBw==").toByteArray();
String s3 = DESede_decrypt(bytes3);
Log.d(TAG, "DESede解密:" + s3);
AES 算法是对称加密算法中最常用的算法!
AES 也分 CBC 和 ECB,这里也合二为一的!
public static String AES_encrypt(String plainText) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec("0123456789abcdef".getBytes(), "AES");
//ECB模式
Cipher instance = Cipher.getInstance("AES/ECB/PKCS5Padding");
instance.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
//CBC模式需要iv向量
// Cipher instance = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
// //初始化时添加上iv向量
// instance.init(Cipher.ENCRYPT_MODE, desKey, ivParameterSpec);
// byte[] doFinal = instance.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
ByteString of = ByteString.of(doFinal);
String hex = of.hex();
String base64 = of.base64();
return hex + "||" + base64;
}
public static String AES_decrypt(byte[] cipherBytes) throws Exception {
//生成des所需要的key
SecretKeySpec secretKeySpec = new SecretKeySpec("0123456789abcdef".getBytes(), "AES");
//ECB模式
Cipher instance = Cipher.getInstance("AES/ECB/PKCS5Padding");
instance.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] doFinal = instance.doFinal(cipherBytes);
//CBC模式
// Cipher instance = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes());
// instance.init(Cipher.DECRYPT_MODE, desKey, ivParameterSpec);
// byte[] doFinal = instance.doFinal(cipherBytes);
return new String(doFinal);
}
非堆成加密算法中,最常用最典型的加密算法就是 RSA。原来说过,对称加密算法是因为加密解密用的是同一个密钥,但是非对称就不是了。
它需要一堆,称为公钥和私钥,当然,密钥不是随便写的!
在线密钥生成网站:http://web.chacuo.net/netrsakeypair
公钥加密,私钥解密。
私钥加密,公钥解密。
一般公钥是公开的,私钥保密,私钥包含公钥。
加密安全,但是性能差,加密长度有限制。
RSA 可以用于加密解密,也可以用来数据签名。
Java 中的私钥必须是 pkcs8 格式。
//RSA
//解析公钥key并返回
public static PublicKey generatePublic(String publicKeyBase64) throws Exception {
byte[] bytes = ByteString.decodeBase64(publicKeyBase64).toByteArray();
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(bytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(x509EncodedKeySpec);
}
public static PrivateKey generatePrivate(String privateKeyBase64) throws Exception {
byte[] bytes = ByteString.decodeBase64(privateKeyBase64).toByteArray();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
}
//RSA 这里使用私钥解密
public static String RSAPrivateDecrypt(byte[] cipherBytes) throws Exception {
String BEGIN_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMOVkFb2U8aOxLZr\n" +
"v/R/Vq/8+vB1fp4GnLLmBhH/g343Q5J6/9AVqbflgf9DRgzP/zBUoauRQnvfsUBt\n" +
"6NXKv3t2bkkAkA4ulCqk6+pxW/Zy03LyyADUtkBrDrTfGHqaw6vJSp0qjT56u563\n" +
"V0nOoUboUmj+AIZRrzNEcwAKa7B1AgMBAAECgYB4oflDCe+mGkzOTys4PIpVRe3o\n" +
"/i84fM+NsD6yPyz1XlS5NlAuIg5qNI63yOCd6nR1dN26mn+tM8159dCUfNcY1W3F\n" +
"JaTvBZKD5+6fDUKQ5UfHhlrd4rVxWKK+kuhdYe67/Y6twrMzL/TE+OXmn7jdxuq2\n" +
"Au93oa2kxraM6pGJCQJBAN/P+ckCGRl26UraqzP3XwrVPq+yGQUMb8y627MXwVJJ\n" +
"LsE3c9vuoDkm79rYN8jCXbxSkUbBpxopHYfdSxT/Dt8CQQDftlI8PZXDzJLlJAmm\n" +
"LynoC7OO52sdC+PoqndJ04DDjo1rg6fcWaaIXFmOL/WTn5HJt8pa4r7vi54DChZ7\n" +
"ju8rAkBUBUSVdGctyxk7k6mv4Y7Zh0J4PNjtr0SNTBzMN//IP1cBDCs/hm655ecn\n" +
"dgJDKMx9tVV6hZqQ1JyUc7wLDtFrAkB1s6ZmvXw7jTnIR4KwJeZliSqKyGVJ3gSm\n" +
"WHH0rMv1l93+MEG0JJMC8ZvIvKD3b6Azwng8A0q0HAAh1z/m+FgLAkEA0PahyHnX\n" +
"ZCzB5ic4QvkiKCqZ+SyibYXOGxBGyCXkuirCwqrtaEorrFxgNEssdpHcEmk71+nv\n" +
"gvrL5QkvgcLvMA==";
PrivateKey privateKey = generatePrivate(BEGIN_PRIVATE_KEY);
Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding");
instance.init(Cipher.DECRYPT_MODE,privateKey);
byte[] doFinal = instance.doFinal(cipherBytes);
return new String(doFinal);
}
//RSA 使用公钥加密
public static String RSAPublicEncrypt(String plainText) throws Exception {
String BEGIN_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDlZBW9lPGjsS2a7/0f1av/Prw\n" +
"dX6eBpyy5gYR/4N+N0OSev/QFam35YH/Q0YMz/8wVKGrkUJ737FAbejVyr97dm5J\n" +
"AJAOLpQqpOvqcVv2ctNy8sgA1LZAaw603xh6msOryUqdKo0+eruet1dJzqFG6FJo\n" +
"/gCGUa8zRHMACmuwdQIDAQAB";
PublicKey publicKey = generatePublic(BEGIN_PUBLIC_KEY);
Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding");
instance.init(Cipher.ENCRYPT_MODE,publicKey);
byte[] doFinal = instance.doFinal(plainText.getBytes());
ByteString of = ByteString.of(doFinal);
return of.base64();
}
//RSA
//加密
String rsaPublicEncrypt = RSAPublicEncrypt("zhangsan");
Log.d(TAG, "RSA加密:" + rsaPublicEncrypt);
//解密
byte[] bytes5 = ByteString.decodeBase64(rsaPublicEncrypt).toByteArray();
String rsaPrivateDecrypt = RSAPrivateDecrypt(bytes5);
Log.d(TAG, "RSA解密:" + rsaPrivateDecrypt);
本文讲述的加密算法主要分为三大类,也是最常用的几个加密算法。
消息摘要算法 (MD5,SHA1,MAC)
对称加密算法 (DES,DESede,AES)
非堆成加密算法 (RSA)
经过比较发现,在 Java 中加密算法有几大特点
通过MessageDigest
类生成的算法有 MD5,SHA1
通过Mac
类生成的算法有 MAC
通过Cipher
生成的算法有 DES,DESede,AES,RSA
嗯,似乎你不太懂什么意思,意思就是可以通过类反推算法。
这样就可以完成自吐算法了,什么算法直接都一把梭哈了,后面再讲!
End
崔庆才的新书《Python3网络爬虫开发实战(第二版)》已经正式上市了!书中详细介绍了零基础用 Python 开发爬虫的各方面知识,同时相比第一版新增了 JavaScript 逆向、Android 逆向、异步爬虫、深度学习、Kubernetes 相关内容,同时本书已经获得 Python 之父 Guido 的推荐,目前本书正在七折促销中!
内容介绍:《Python3网络爬虫开发实战(第二版)》内容介绍
扫码购买
点个在看你最好看