前言

假如要逆向做一些模仿恳求,加密算法是绕不过去的坎。以前逆向一个app更多的是破解一些单机的app一般只涉及到一些重打包之类的,现在看更多的是获取到里边的恳求加密算法然后通过python完成一遍从而能够用机器替代人工去恳求去完成一些有商业价值的工作。所以假如了解加密算法的话看逆向的代码会更加称心如意。

下面的每个算法都会用java和python代码完成一遍。

算法简介

  1. 对称加密算法:在对称加密算法中,加密宽和密运用相同的密钥。常见的对称加密算法有AES、DES、3DES等。
  2. 非对称加密算法:在非对称加密算法中,加密宽和密运用不同的密钥。常见的非对称加密算法有RSA、DSA、ECC等。
  3. 音讯摘要算法:音讯摘要算法能够将任意长度的音讯压缩成固定长度的摘要。常见的音讯摘要算法有MD5、SHA-1、SHA-256等。
  4. 散列函数:散列函数是一种将任意长度的数据映射到固定长度的数据的函数。常见的散列函数有SHA-1、SHA-256等。
  5. 数字签名算法:数字签名算法能够用于验证音讯的完整性和身份认证。常见的数字签名算法有RSA、DSA、ECDSA等。

系列文章

  1. 逆向-加密算法总结(1)
  2. 逆向-加密算法总结(2)
  3. 逆向-加密算法总结(3)

这些加密算法在计算机安全范畴中被广泛运用,用于保护数据的机密性、完整性和可用性。

非对称加密

与对称加密不同,它运用了一对密钥,即公钥和私钥。公钥能够公开给任何人运用,而私钥则只能由密钥持有者运用。

逆向非对称加密时只能是了解好参数,然后拿到公钥去加密数据传给后台,至于成不成功你没办法验证,由于同一个秘钥每次生成都是不一样的数据。假如回来数据便是成功了,没回来就换参数。可是这些参数都是有在代码里的,或许混淆会比较难找。

RSA

RSA是一种基于大素数质因数分解的非对称加密算法,是最常用的公钥加密算法之一.安全性高,目前尚未被攻破。加密速度较慢,特别是关于长文本的加密速度较慢。适合用于数据加密,数字签名等场景

  • java

这里边是整体的运用,实际上逆向的时候客户端只会有一个服务器回来过来的公钥。我们做开发一般这个都是保存在全局或许保存到sp文件里边的。


import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
import javax.crypto.Cipher;
public class RSAUtil {
    /**
     * 生成RSA密钥对
     * @param keyLength 密钥长度,一般为1024、2048等
     * @return 包含公钥和私钥的KeyPair目标
     */
    public static KeyPair generateRSAKeyPair(int keyLength) throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(keyLength);
        return keyPairGen.generateKeyPair();
    }
    /**
     * 对数据进行签名
     * @param data       待签名的数据
     * @param privateKey 私钥
     * @return 签名后的数据,Base64编码的字符串
     */
    public static byte[] sign(byte[] data, PrivateKey privateKey) throws Exception {
        //SHA1withRSA、SHA256withRSA、SHA384withRSA、SHA512withRSA
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }
    /**
     * 验证签名是否正确
     * @param data      待验证的数据
     * @param sign 签名数据,Base64编码的字符串
     * @param publicKey 公钥
     * @return true表示签名正确,false表示签名错误
     */
    public static boolean verify(byte[] data, byte[] sign, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(sign);
    }
    //运用公钥进行加密
    public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }
    //运用私钥进行解密
    public static byte[] decrypt(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
    public static void main(String[] args) throws Exception {
        //生成RSA密钥对
        KeyPair keyPair = generateRSAKeyPair(2048);
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("生成RSA密钥对:");
        System.out.println("publicKey:" + publicKey);
        System.out.println("privateKey:" + privateKey);
        System.out.println("公钥:" + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
        System.out.println("私钥:" + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
        //签名、验证
        String message = "Hello, world!";
        byte[] data = message.getBytes();
        byte[] sign = sign(data, privateKey);
        boolean verifyResult = verify(data, sign, publicKey);
        System.out.println("数字签名验证成果:" + verifyResult);
        //加密、解密
        String plainText = "Hello, world!";
        byte[] plainData = plainText.getBytes();
        byte[] cipherData = encrypt(plainData, publicKey);
        byte[] decryptData = decrypt(cipherData, privateKey);
        String decryptText = new String(decryptData);
        System.out.println("加密前的明文:" + plainText);
        System.out.println("加密后的密文:" + Base64.getEncoder().encodeToString(cipherData));
        System.out.println("解密后的明文:" + decryptText);
    }
}

实际运用我们只需要联系下面几个方法

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
/**
 * 实际运用过程中不需要生成公钥秘钥、这个是后端给的。一般会通过恳求回来过来
 * 所以实际上客户端拿到的是一个公钥的字符串,或许是base64的字符串也或许是16进制的字符串
 * 然后我们运用其实便是通过string转publicKey,过程如下
 * 1. 把String传转成字节传给结构 xx 格局的公钥标准,
 * 2. 获取一个加密算法结构器,把公钥标准后的数据传给结构器。这样就能得到一个publicKey目标
 * 3. 然后用PublicKey对数据进行加密传给服务器
 */
public class RSAUserUtil {
    // 公钥
    private static final String PUBLIC_KEY_PEM ="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA532SXQw9PQp8xzz3zy/i2uDslTvYJLqnABO6gL4mZrbZON5vDtLmt7QU8aX0fAyotIC162/cvG0WS8GOAxK48fGsfU9VE1ZA1MvojuvBKhEdb/UPkm4PBxnTYVdbbyUfsIpgMlphRTOyBKnI9XE14Qe9cl5YgzBQicka19kN0maDXLk+1vLEfN1EUu+Q0TH1UYcUVo/zJ/AU1feYmiExEDfqgR82tmZohUHg+jg/SvzbUp6dczN8K8CkAov8D0jgdL+bcq+6WHAbvQVmDoqmxnBeKWZsgENOtPuycwYXEtlb4yNPGr/RlzL97tdcE+1VkoqXg/bxA+4dBNR2ccYYNwIDAQAB";
    public static PublicKey getPublicKeyFromString(String publicKeyString) throws Exception {
        // 将公钥字符串解码为二进制数据
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
        // DER格局,原始数据不必去头去尾。结构X.509证书格局公钥标准
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        // 指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        // 生成公钥目标
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }
    //运用公钥进行加密
    public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
    // ENCRYPT_MODE:加密模式,用于加密数据。
    // DECRYPT_MODE:解密模式,用于解密数据。
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }
    public static void main(String[] args) throws Exception {
        // 不一定所有开发都会在后端去头去尾,假如没去的话要去一下,否则会解析失利
//        String NoTitlePubKey = PUBLIC_KEY
//                .replace("-----BEGIN RSA PUBLIC KEY-----\n", "")
//                .replace("-----END RSA PUBLIC KEY-----", "")
//                .replaceAll("\n", "")
//                .replaceAll("\s", "");
        PublicKey publicKey = getPublicKeyFromString(PUBLIC_KEY_PEM);
        //加密、解密
        String plainText = "Hello, world!";
        byte[] plainData = plainText.getBytes();
        byte[] cipherData = encrypt(plainData, publicKey);
        System.out.println("加密前的明文:" + plainText);
        System.out.println("加密后的密文:" + Base64.getEncoder().encodeToString(cipherData));
    }
}
  • python
import base64
PUBLIC_KEY_PEM = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA532SXQw9PQp8xzz3zy/i2uDslTvYJLqnABO6gL4mZrbZON5vDtLmt7QU8aX0fAyotIC162/cvG0WS8GOAxK48fGsfU9VE1ZA1MvojuvBKhEdb/UPkm4PBxnTYVdbbyUfsIpgMlphRTOyBKnI9XE14Qe9cl5YgzBQicka19kN0maDXLk+1vLEfN1EUu+Q0TH1UYcUVo/zJ/AU1feYmiExEDfqgR82tmZohUHg+jg/SvzbUp6dczN8K8CkAov8D0jgdL+bcq+6WHAbvQVmDoqmxnBeKWZsgENOtPuycwYXEtlb4yNPGr/RlzL97tdcE+1VkoqXg/bxA+4dBNR2ccYYNwIDAQAB"
def get_public_key_from_string(public_key_string):
    public_key_string = public_key_string.replace('\n', '').replace('\r', '').replace(' ', '').replace('/', '/')
    public_key_bytes = base64.b64decode(public_key_string)
    return RSA.import_key(public_key_bytes)
def rsa_encrypt_with_public_key(message, public_key):
    cipher = PKCS1_OAEP.new(public_key)
    ciphertext = cipher.encrypt(message)
    return ciphertext
if __name__ == '__main__':
    public_key = get_public_key_from_string(PUBLIC_KEY_PEM)
    message = 'Hello, world!'.encode()
    ciphertext = rsa_encrypt_with_public_key(message, public_key)
    print("加密前的明文:", message.decode())
    print("加密后的密文:", base64.b64encode(ciphertext).decode())
注意事项:
  1. 整体流程
    * 1. 把String传转成字节传给结构 xx 格局的公钥标准,
    * 2. 获取一个加密算法结构器,把公钥标准后的数据传给结构器。这样就能得到一个publicKey目标
    * 3. 然后用PublicKey对数据进行加密传给服务器
    
  2. python拿到秘钥时,需要对密钥做一些特殊字符处理,由于编译器不一样的原因会导致有些会变成非法字符
public_key_string = public_key_string.replace('\n', '').replace('\r', '').replace(' ', '').replace('/', '/')

DSA

DSA 算法的公钥和私钥长度一般为 1024 或 2048 位,相较于 RSA 算法的公钥和私钥长度要小很多。DSA 算法主要用于数字签名,而不是加解密;DSA 算法的密钥生成和签名验证速度比 RSA 算法要快一些,可是签名长度比 RSA 算法要长。一般不必于加密的话看一下就好

  • java
import java.security.*;
public class DESUtil {
    private static final String SIGNATURE_ALGORITHM = "SHA256withDSA";
    private static final int KEY_SIZE = 2048;
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
        keyGen.initialize(KEY_SIZE);
        return keyGen.generateKeyPair();
    }
    public static byte[] sign(byte[] data, Signature sig, java.security.PrivateKey privateKey) throws SignatureException {
        try {
            sig.initSign(privateKey);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        }
        sig.update(data);
        return sig.sign();
    }
    public static boolean verify(byte[] data, byte[] signature, Signature sig, java.security.PublicKey publicKey) throws SignatureException {
        try {
            sig.initVerify(publicKey);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        }
        sig.update(data);
        return sig.verify(signature);
    }
    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();
        java.security.PrivateKey privateKey = keyPair.getPrivate();
        java.security.PublicKey publicKey = keyPair.getPublic();
        String data = "Hello, world!000";
        Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
        byte[] signature = sign(data.getBytes(), sig, privateKey);
        boolean verified = verify(data.getBytes(), signature, sig, publicKey);
        System.out.println("Original data: " + data);
        System.out.println("Digital signature: " + new String(signature));
        System.out.println("Verification result: " + verified);
    }
}
  • python
from Crypto.Cipher import DES
import base64
class DESUtil:
    def __init__(self, key):
        self.key = key.encode()
        self.des = DES.new(self.key, DES.MODE_ECB)
    def pad(self, s):
        bs = DES.block_size
        return s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
    def unpad(self, s):
        return s[:-ord(s[len(s)-1:])]
    def encrypt(self, plaintext):
        plaintext = self.pad(plaintext)
        ciphertext = self.des.encrypt(plaintext.encode())
        return base64.b64encode(ciphertext).decode()
    def decrypt(self, ciphertext):
        ciphertext = base64.b64decode(ciphertext)
        plaintext = self.des.decrypt(ciphertext)
        return self.unpad(plaintext.decode())
def main():
    # 必须8位
    key = "0abcdefg"
    des = DESUtil(key)
    plaintext = "Hello, world!"
    ciphertext = des.encrypt(plaintext)
    print("加密前的明文:", plaintext)
    print("加密后的密文:", ciphertext)
    decrypted_plaintext = des.decrypt(ciphertext)
    print("解密后的明文:", decrypted_plaintext)
if __name__ == '__main__':
    main()