加密解密流程及使用大揭秘

一、加密算法介绍

1.1 对称加密算法

我们听过最多的对称加密算法就是AES了。对称加密就是使用了同一个公钥用来加密解密。还有些其他的对称加密算法,但是都不推荐使用的了,这里罗列一下。

  1. DES(Data Encryption Standard) :是一种最早的对称加密算法之一,但由于其较短的密钥长度(56位)和已知的弱点,已经不再安全,不推荐使用。
  2. 3DES(Triple Data Encryption Standard) :是DES的一种改进版本,通过对数据应用三次DES加密来提高安全性。
  3. AES(Advanced Encryption Standard) :目前最常用的对称加密算法之一。它使用128位、192位或256位密钥长度,安全性高且效率较高。
  4. RC4(Rivest Cipher 4) :是一种流密码(Stream Cipher)算法,虽然在过去被广泛使用,但由于存在一些安全性问题,已经不推荐使用。
  5. RC5、RC6:这是由Ron Rivest设计的另外两个对称加密算法。它们在一些特定的应用中仍然有用,但并不像AES那样被广泛使用。
  6. Blowfish:是一种较为简单的对称加密算法,速度快,适用于一些需要高效率加密的场景,但由于其密钥长度可变的特性,安全性有所争议。
  7. Twofish:是Blowfish的改进版本,设计更为安全,但由于未被广泛采用,相对来说知名度较低。
  8. Serpent:是一种针对高安全性需求设计的对称加密算法,曾经是AES的候选算法之一,但由于速度较慢,实际应用并不多见。

AES被广泛认可和使用,因为它具有高度的安全性、效率和可靠性。AES支持128位、192位和256位三种密钥长度,其中128位密钥长度的AES最为常见。AES已被广泛应用于各种领域,包括互联网通信、数据存储和传输、加密文件等。其安全性和效率使得它成为当今互联网世界中最受信任的加密标准之一

1.2 非对称加密算法

当然我们听过最多的非对称加密算法是RSA了,非对称加密也是就利用了公钥和私钥。使用公钥加密,使用私钥来解密。当然还有些别的非对称加密算法。

  1. RSA(Rivest-Shamir-Adleman) :RSA是最早也是最广泛使用的非对称加密算法之一。它基于两个大素数的乘积难解性问题,并且被广泛用于数字签名、密钥交换等方面。
  2. DSA(Digital Signature Algorithm) :DSA是一种专门用于数字签名的非对称加密算法,通常与SHA(Secure Hash Algorithm)结合使用来确保数据的完整性和认证。
  3. ECC(Elliptic Curve Cryptography) :ECC利用椭圆曲线上的点来实现加密操作,相比RSA,它能够提供相同的安全性但使用更短的密钥长度,因此在资源受限的环境下更为适用。
  4. DH(Diffie-Hellman) :DH算法用于安全地交换密钥,而不涉及消息的加密本身。它被广泛应用于协商共享密钥的过程,例如在安全通信中的密钥交换。
  5. ElGamal:ElGamal算法是一种基于离散对数问题的加密算法,常用于加密通信和数字签名。

这些非对称加密算法在网络安全、数字签名、密钥交换等领域都有广泛的应用。选择哪种算法通常取决于安全性需求、性能要求以及特定场景的需求

二、加密流程

2.1 对称加密流程

看图应该很清晰了。这边梳理下流程,客户端用通过AES算法 使用生成的密钥(两端生成都可以,保持一致就行) 来进行加密,然后把密文传输给服务端(或者由服务端提供,客户端获取密钥),当然相对应的也需要把密钥传输给服务端。服务端通过相同的密钥的来对密文进行解密,获取到数据后,把需要返回的数据也用密钥进行加密,返回给客户端,这时候客户端就用密钥对密文解密获取相对应的数据。

2.2 非对称加密流程

这里涉及到公钥和私钥的概念。公钥用来加密,私钥用来解密 。所以图上会出现两个公钥和私钥。公钥一般是对外开放的,私钥是自己保留的,需要安全的存放,不能泄露给外部。流程非常明了了,要给对应的接受方发送加密消息时就需要用接收方提供的公钥进行加密,接收方才能用自己的私钥进行解密获得数据。

三、代码实现

3.1 对称加密实现

kotlin 复制代码
fun generateAESKey(): SecretKey {
    val keyGenerator = KeyGenerator.getInstance("AES")
    keyGenerator.init(128, SecureRandom())
    return keyGenerator.generateKey()
}

fun encryptAES(message: String, secretKey: SecretKey): String {
    val cipher = Cipher.getInstance("AES")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey)
    val encryptedBytes = cipher.doFinal(message.toByteArray())
    return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
}

fun decryptAES(encryptedMessage: String, secretKey: SecretKey): String {
    val cipher = Cipher.getInstance("AES")
    cipher.init(Cipher.DECRYPT_MODE, secretKey)
    val decryptedBytes = cipher.doFinal(Base64.decode(encryptedMessage, Base64.NO_WRAP))
    return String(decryptedBytes)
}

Android对称加密实现还是利用了Java包中的方法(Java Cryptography Architecture (JCA))。需要注意一下的是Cipher.getInstance("AES")这个方法模式有很多。下面介绍一下各种模式的区别。下面罗列的是加密模式,常用就是CBC模式。

  1. ECB(Electronic Codebook):ECB 模式是最简单的 AES 加密模式,它将明文分成固定大小的数据块,然后对每个数据块单独进行加密。由于每个数据块都是独立加密的,因此 ECB 模式不提供数据的保密性,相同的明文块会得到相同的密文块,这可能导致安全性问题。因此,ECB 模式在实际应用中很少被使用。
  2. CBC(Cipher Block Chaining):CBC 模式通过使用前一个密文块与当前明文块的异或运算来消除相同明文块的模式化问题。在加密时,首先将初始向量(IV)与第一个数据块进行异或运算,然后再进行加密。在解密时,需要使用相邻的密文块来进行解密操作。CBC 模式提供了一定的数据保密性,但不提供完整性保护。
  3. CFB(Cipher Feedback):CFB 模式将前一个密文块进行加密,然后与当前明文块进行异或运算,以生成当前密文块。在解密时,使用前一个密文块来进行加密操作,以生成当前明文块。CFB 模式可以实现流加密,即将 AES 块密码转换为流加密器。但由于对错误的传播较敏感,因此在实际应用中较少使用。
  4. OFB(Output Feedback):OFB 模式类似于 CFB 模式,但是它使用前一个输出作为密钥流,而不是密文块。这意味着同样的密钥流可以在多次加密和解密之间重用,因此 OFB 模式可以用于流加密,且对错误传播不敏感。
  5. CTR(Counter):CTR 模式使用一个计数器作为输入来生成密钥流,然后将密钥流与明文进行异或运算,以生成密文。在解密时,使用相同的计数器值和密钥流来生成明文。CTR 模式可以实现流加密,且具有高度的并行性和随机访问性。
  6. GCM (Galois/Counter Mode) :是一种组合加密模式,结合了加密和认证功能。它使用 Counter(CTR)模式进行加密和 Galois/Counter Mode (GCM) 认证来实现数据的加密和完整性保护。

除了加密模式可以选择外,还可以选择填充模式,因为AES算要求key为16位或者32位,这是时候就需要填充模式出场了。通常情况下,PKCS7Padding 是最常用的填充方案,因为它简单且通用,而且与许多加密库和标准兼容。

  1. NoPadding:不进行填充,要求明文长度必须是分组大小的整数倍,否则会抛出异常。这意味着如果明文长度不是分组大小的整数倍,需要手动补齐至分组大小的整数倍。
  2. PKCS5Padding / PKCS7Padding:使用 PKCS5 或 PKCS7 标准进行填充。在加密前,会对数据进行填充,使其长度达到分组大小的整数倍。解密时会移除填充内容。
  3. ISO10126Padding:在加密时,会使用随机字节填充数据,最后一个字节指定填充长度。解密时,根据最后一个字节的值来移除填充内容。

当你需要使用这些模式时,加密和解密方法都需要变动一下。以GCM模式为例。

kotlin 复制代码
fun generateAESKey(): SecretKey {
    val keyGenerator = KeyGenerator.getInstance("AES")
    keyGenerator.init(128, SecureRandom())
    return keyGenerator.generateKey()
}

var iv:ByteArray = byteArrayOf()
fun encryptAES(message: String, secretKey: SecretKey): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey)
    val encryptedBytes = cipher.doFinal(message.toByteArray())
    // 需要获取向量,用来解密
    iv = cipher.iv
    return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
}

fun decryptAES(encryptedMessage: String, secretKey: SecretKey): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    // 这里需要用对应的向量类来解密
    cipher.init(Cipher.DECRYPT_MODE, secretKey,GCMParameterSpec(128, iv))
    val decryptedBytes = cipher.doFinal(Base64.decode(encryptedMessage, Base64.NO_WRAP))
    return String(decryptedBytes)
}

看下cipher.init()方法,这个方法重载了很多。

java 复制代码
public final void init(int opmode, Key key) throws InvalidKeyException {
    throw new RuntimeException("Stub!");
}

public final void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
    throw new RuntimeException("Stub!");
}

public final void init(int opmode, Key key, AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException, InvalidKeyException {
    throw new RuntimeException("Stub!");
}

public final void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException, InvalidKeyException {
    throw new RuntimeException("Stub!");
}

public final void init(int opmode, Key key, AlgorithmParameters params) throws InvalidAlgorithmParameterException, InvalidKeyException {
    throw new RuntimeException("Stub!");
}

public final void init(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidAlgorithmParameterException, InvalidKeyException {
    throw new RuntimeException("Stub!");
}

这里只摘取部分重载方法。可以看到第三个参数是AlgorithmParameters,它其实是个接口,那么他有很多实现类。这里只是截取了部分实现,可以发现其中就有我们的GCM模式下的实现。所以我们使用其他模式时,可以选择对应的向量类来实现解密。

3.2 非对称加密实现

kotlin 复制代码
// 生成 RSA 密钥对
fun generateKeyPair(): KeyPair {
    val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
    keyPairGenerator.initialize(2048) // 指定密钥长度
    return keyPairGenerator.generateKeyPair()
}

// 使用公钥加密数据
fun encrypt(originalMessage: String, publicKey: PublicKey): ByteArray {
    val cipher = Cipher.getInstance("RSA")
    cipher.init(Cipher.ENCRYPT_MODE, publicKey)
    return cipher.doFinal(originalMessage.toByteArray())
}

// 使用私钥解密数据
fun decrypt(encryptedBytes: ByteArray, privateKey: PrivateKey): String {
    val cipher = Cipher.getInstance("RSA")
    cipher.init(Cipher.DECRYPT_MODE, privateKey)
    val decryptedBytes = cipher.doFinal(encryptedBytes)
    return String(decryptedBytes)
}

fun test() {
    // 生成 RSA 密钥对
    val keyPair = generateKeyPair()

    // 获取公钥和私钥
    val publicKey = keyPair.public
    val privateKey = keyPair.private

    // 要加密的数据
    val originalMessage = "Hello, world!"

    // 使用公钥加密数据
    val encryptedBytes = encrypt(originalMessage, publicKey)

    // 使用私钥解密数据
    val decryptedMessage = decrypt(encryptedBytes, privateKey)
}

非对称加密没有特定的加密模式,但是有填充模式,如下:

  1. PKCS1Padding:RSA 加密的标准填充方案,通常用于加密较小的数据块。
  2. OAEPPadding(Optimal Asymmetric Encryption Padding):OAEP 是一种更安全的填充方案,可以提供更好的安全性保证,适用于更大的数据块。

一般我们选择使用OAEP填充模式。

使用填充模式和对称加密类似,如Cipher.getInstance("RSA/NONE/OAEPPadding")。

四、项目使用

项目中使用一般是对称加密和非对称加密组合使用。比较常见的做法是服务端生成公钥和私钥客户端生成对称加密的密钥,在传输时,使用公钥对密钥加密,服务端用私钥解密得到密钥,使用密钥解密数据。服务端返回数据时,用密钥加密数据返回给客户端,客户端使用密钥解密。这样子外部拦截到密文,不知道私钥就无法得到密钥,也就无法解析到我们的数据。

相关推荐
AI软著研究员1 小时前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish1 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱2 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
砖厂小工4 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心5 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心5 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
Kapaseker7 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴8 小时前
Android17 为什么重写 MessageQueue
android
地平线开发者18 小时前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮18 小时前
大模型连载2:初步认识 tokenizer 的过程
算法