【译】Android中的安全数据-初始化向量

目录

  • 初始化向量
  • 默认值
  • 自订值
  • 空值
  • 随机加密
  • 使用范例
  • 下一步是什么
  • 安全提示

初始化向量

初始化向量是加密原语的固定大小输入。通常要求它是随机或伪随机的。IV的重点是允许使用相同的密钥来加密几个不同的消息。

在大多数提供程序(包括提供程序和提供程序)中,块算法模式(如CBC中的AES)都是必需的。AndroidKeyStoreBC


在API 18上,BC如果在解密过程中未指定IV,则使用默认Java的提供程序密钥Cipher将落入IllegalArgumentException

 

 

在具有AndroidKeyStore提供者密钥的API 23上,InvalidKeyException将引发:

<span style="color:#292929">InvalidKeyException:解密时需要IV。使用IvParameterSpec或AlgorithmParameters提供它。</span>

默认值

实现初始化向量支持的最简单方法是使用由密码在加密过程中生成的字节数组数据。可以使用以下cipher.getIV()方法进行检索:

cipher.init(Cipher.ENCRYPT_MODE,key)
val iv = cipher.iv //返回自动生成的IV值... 
//使用密码加密数据... 
//使用密码加密数据

注意,默认值是在Cipher初始化期间生成的,因此cipher.init()必须首先调用它,否则cipher.getIv()将返回空数据。


然后,在解密期间,使用IvParameterSpec根据生成的IV值创建的Cipher进行初始化:

val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.DECRYPT_MODE,key,ivSpec)... 
//使用IV初始化的Cipher解密数据... 
//使用IV初始化的Cipher解密数据

Salt值一样,初始化向量可以与加密数据一起存储在公共存储中。

存储IV数据的一种可能方法是将数据添加到加密结果中:

 

并在解密之前根据加密数据进行解析:

 

完整的源代码在这里。

自订值

正如Dorian Cussen在其博客中提到的IV,由提供的默认值Cipher主要取决于Provider的实现。

未实现时可能会遇到这种情况cipher.getIV() -返回“空”或null数组。

为了安全起见并避免此类不愉快的时刻,请使用SecureRandomclass 明确声明一个初始化向量。


要创建自定义IV值,请使用类中的nextBytes(byte[] key)方法SecureRandom

<span style="color:#292929">val iv = ByteArray(ivLength)
SecureRandom()。nextBytes(iv)</span>

ivLength依赖于操作模式。对于大多数模式,包括CBC,IV必须与其中的块具有相同的长度 

AES算法使用128位块,因此初始化矢量的长度等于128位(ivLength = 16// bytes)。


生成自定义IV值时,只需Cipher使用它初始化实例:

<span style="color:#292929">cipher.init(Cipher.ENCRYPT_MODE,密钥,IvParameterSpec(iv))
cipher.init(Cipher.DECRYPT_MODE,密钥,IvParameterSpec(iv))</span>

注意,生成的值必须被传递到init()用于方法加密和解密模式。

空值

可能(但不建议)作弊Cipher。无需在加密之前每次都生成新的随机初始化向量数据,然后在解密之前进行保存和解析,而是将IV作为数组初始化一次,具有固定长度的静态值,并且可以随时随地使用:

//创建一个16字节的数组,填充为0 [0,0,0,0 ..0] 
val iv = ByteArray(16)//在加密和解密期间将此数组用作IV数据
cipher.init(Cipher.ENCRYPT_MODE,key,IvParameterSpec(iv))
cipher.init(Cipher.DECRYPT_MODE,key,IvParameterSpec(iv))//在加密和解密期间将此数组用作IV数据
cipher.init(Cipher.ENCRYPT_MODE,key,IvParameterSpec(iv))
cipher.init(Cipher.DECRYPT_MODE,key,IvParameterSpec(iv))

请记住,这种实现扼杀了IV的本质。

随机加密

为了保护用户数据免于使用不正确的(不是随机的或空的)IV,默认情况下,AndroidKeyStoreProvider不允许使用自定义IV值:

val iv = ByteArray(16)
cipher.init(Cipher.ENCRYPT_MODE,key,IvParameterSpec(iv))//将抛出InvalidAlgorithmParameterException:调用者提供的IV //不允许
cipher.doFinal(data.toByteArray())//将抛出InvalidAlgorithmParameterException:调用者提供的IV //不允许
cipher.doFinal(data.toByteArray())

在API 23上,该setRandomizedEncryptionRequired方法已添加到KeyGenParameterSpec类中,该方法应允许您控制IV是否可以自定义。从文档:

设置是否必须将使用此密钥进行的加密充分随机化,以便每次都为相同的明文生成不同的密文。

当需要IND-CPA时:在使用IV的块模式(例如GCM,CBC和CTR)中,加密时将拒绝调用方提供的IV,以确保仅使用随机IV。

val builder = KeyGenParameterSpec.Builder()//强制仅使用由IV生成的默认生成。
// 默认值。
builder.setRandomizedEncryptionRequired(true)//启用自定义生成的IV。
//不起作用。
builder.setRandomizedEncryptionRequired(false)//强制仅使用由IV生成的默认生成。
// 默认值。
builder.setRandomizedEncryptionRequired(true)//启用自定义生成的IV。
//不起作用。
builder.setRandomizedEncryptionRequired(false)

但它不起作用(至少对于AESCBC)。即使在禁用随机化之后,Cipher仍然会崩溃InvalidAlgorithmParameterException并继续需要默认初始化向量。

使用范例

让我们使用带有初始化矢量的对称AES密钥对消息进行加密和解密:

 

完整的源代码在这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AES是一种对称加密算法,使用相同的密钥进行加密和解密。在AES加密,初始向量IV)是一个随机生成的值,用于增加加密强度并避免相同明文产生相同的密文。 在Java使用AES加密并设置初始向量的示例代码如下: ```java import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; public class AesExample { public static void main(String[] args) throws Exception { String key = "0123456789abcdef0123456789abcdef"; // 256位密钥 String iv = "0123456789abcdef"; // 128位初始向量 String plaintext = "Hello, AES!"; byte[] encrypted = encrypt(plaintext, key, iv); String decrypted = decrypt(encrypted, key, iv); System.out.println("Original: " + plaintext); System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted)); System.out.println("Decrypted: " + decrypted); } public static byte[] encrypt(String plaintext, String key, String iv) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec); return cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); } public static String decrypt(byte[] ciphertext, String key, String iv) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec); byte[] decrypted = cipher.doFinal(ciphertext); return new String(decrypted, StandardCharsets.UTF_8); } } ``` 在上述示例代码,我们使用了AES-256加密算法(密钥长度为256位),初始向量的长度为128位。你可以根据需要修改密钥和初始向量的值。请注意,密钥和初始向量的长度需要符合AES算法规范。 希望以上代码对你有所帮助!如有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值