初始化向量是加密原语的固定大小输入。通常要求它是随机或伪随机的。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)但它不起作用(至少对于AES和CBC)。即使在禁用随机化之后,Cipher仍然会崩溃InvalidAlgorithmParameterException并继续需要默认初始化向量。
让我们使用带有初始化矢量的对称AES密钥对消息进行加密和解密:
完整的源代码在这里。