Android 应用安全 - 加密算法(AES RSA Hash
版权归作者所有,转发请注明出处:https://www.jianshu.com/p/02c654b5b18e
Android 应用安全 - 应用安全概览
Android 应用安全 - 移动应用安全
Android 应用安全 - Mobile安全漏洞Top10(OWASP)
Android 应用安全 - 案例
Android 应用安全 - 检测设备是否Root
Android 应用安全 - 加密算法
前言
提高应用的安全性有助于维护用户信任和设备完整性
加密.PNG互联网应用加密技术以及无处不在,手机,密码,SSL,数据等都在密码学的运用范围之内,正确的使用加密方式对于加密的有效性至关重要,任何加密过程中的配置或者小错误都会导致失去加密的保护而被轻易破解,从而泄露个人数据以及损失公司形象
1. 加密服务
身份认证
在密码校验系统中,可以建立远程的身份校验,用户的不同身份体现在不同的密钥上,不安全的密钥会降低身份认证的安全
唯一性校验
金融或电子商务类应用对此特别重要,通常使用加密工具来证明唯一用户已经发出交易请求,通常通过对交易请求进行数字签名来证明用户授权了交易
私密性
加密方式最大的用途就是用来保证信息的私密性,也是加密方式产生的初衷,无论是你的登录密码还是个人私密敏感数据,加密都可以保证只有有权访问适当密钥的用户才能访问
数据的完整性
确保数据在存储和传输期间不会查看或更改数据,加密哈希可以提供安全的校验来保护数据
2. 加密算法
不同的加密算法有不同的优势,通常分为性能强劲安全但是运行缓慢的算法,或者速度快但是安全性较低的算法,最常见的就是将两种方法结合使用(SSL),我们使用安全缓慢的算法建立连接,然后使用较弱但是速度较快的算法进行频繁的数据传输提高响应速度
对称加密
对称加密系统中,参与的各方共享一个公共的秘密(密码,密码短语或密钥),使用相同的密钥对数据进行加密解密,对称加密算法往往比较快,但是除非相关各方已经交换了密钥,否则无法使用它们,拥有密钥的任何一方都可以是引用该密钥加密或者解密消息
对称加密常见的加密算法有DES,3DES和AES
DES(Data Encryption Standard),即加密算法标准,使用56位短密钥,是一种使用密钥加密的块算法
3DES(Triple DES),与DES使用相同的算法,使用不同的56位密钥对数据加密了三次,是DES向AES过渡的一种算法
AES(Advanced Encryption Standard),即高级加密标准,使目前推荐使用的对称加密算法,采用对称分组密码体制,子密钥组是通过策略从主密钥产生
IV(Initialization vector) 对于每个加密操作,大多数模式都需要使用唯一的二进制序列,通常称为初始化向量(IV),IV必须是非重复的,对于某些模式IV也必须是随机的。初始化向量确保即使使用相同的密钥对同一纯文本进行多次独立加密,也可以保证生成不同的密文,不需要生成不同的密钥从而降低效率,对于大多数分组密码模式,重要的是永远不要在同一密钥下重复使用初始化向量
Block Model 分组密码操作模式是一种使用分组密码来提供信息安全性的算法,分组密码本身仅适用于一个称为分组的固定长度位组的安全 密码转换(加密/解密),一种操作模式描述了如何重复应用密码的单块操作来安全的转换大于块的数据量
区块模式:ECB,CBC,OFB,CFB,CTR等,不同的模式对区块的加密策略不同
ECB(Electronic Code book):消息被分成快,分块加密,每个块都使用相同的方式加密,导致数据不会扩散,只是会以相同的加密方式加密
(彩色照片->黑白照片)ECB以外的其他模式会导致伪随机性
注意:ECB弱加密模式可能会存在安全问题
CBC(Cipher Block Chaining):密码块链接,每个数据块加密之前先于之前的密文块进行异或,要使每条消息唯一,必须在第一个块中使用初始化向量。
PCBC(Propagating Cipher Block Chaining):传播密码块链接,在PCBC模式中,每个明文块在加密之前与先前的明文块和先前的密文块进行异或。
与CBC模式一样,在第一个块中使用初始化向量。
Padding(填充方式) 分组模式适用于固定大小(称为块)的单位,但是由于消息的长度不同,所以某些填充模式需要在加密之前填充最后一块,以使其达到块大小的倍数
Android的密钥库系统 列出了所有支持的AES算法组合以及使用方法,提供了安全的密钥存储库,并且可以使用密钥之前对用户进行身份验证(图案/密码/指纹),保证密钥的安全存储
密钥的生成:
//The alias is the name of the entry in which the generated key will appear in Android KeyStore.
private static String KEY_ALIAS = "KEY_ALIAS";
//The AES Model ALGORITHM name+block model name+encryption padding name
/*
* KeyProperties.KEY_ALGORITHM_AES 使用AES加密算法
* KeyProperties.BLOCK_MODE_CBC 使用CBC的block model
* KeyProperties.ENCRYPTION_PADDING_NONE no paddings
*
* 128 bit keySize + 128 bit block size + no paddings
* 数据bit如果不是16的倍数则会javax.crypto.IllegalBlockSizeException 配合Base64使用可以避免问题
*/
private static String AES_MODE_CBC = KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;
public static SecretKey createKEY() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
/*
* 1. Use AES ALG
* 2. Save the key/data in AndroidKeyStore
* will be return a AndroidKeyStoreSecretKey Object
*/
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
/*
* Need to build a KeyGenParameterSpec
* IS the properties of the keys we want. such as. we wanted the key to expire after a certain amount of time
*
* ALIAS: Can use any string.
* KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT config can use to ENCRYPT and DECRYPT
*/
KeyGenParameterSpec build = new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
/*
* blockModes: Just special block models can used ENCRYPT and DECRYPT
* https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
*/
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
/*
* encryption Paddings: Sets the set of padding schemes
*/
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build();
keyGenerator.init(build);
/*
* Get the secure (symmetric) key.
*/
return keyGenerator.generateKey();
}
加密操作:
//initialization vector.used to decyption(NOTE 不要在一个KEY下面重用IV) 使用统一个KEY 每次做解密加密的IV 不能用作下一次解密加密操作(如果这样需要把加密数据和IV绑定存储)
private static byte[] cbc_iv = null;
/**
* @param plaintext data
* @param secretKey key
* @return encyption data (Note. 加密之后的数据不要转为字符串再去解密 因为转化为String数据不可逆)
* @throws Exception ..
*/
public static byte[] encyption(String plaintext, SecretKey secretKey) throws Exception {
//Get the Cipher with transformation name
Cipher cipher = Cipher.getInstance(AES_MODE_CBC);
//Init Cipher with opModel and secretKey
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
//encyption
byte[] encodedBytes = cipher.doFinal(plaintext.getBytes());
//Get initialization vector (IV used for block ALG)
cbc_iv = cipher.getIV();
return encodedBytes;
}
解密操作:
public static byte[] decyption(byte[] encrypted) throws Exception {
Cipher cipher = Cipher.getInstance(AES_MODE_CBC);
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
//Used alias to get the security key
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
//decyption with IV
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(cbc_iv));
//Transfer to base64
return cipher.doFinal(encrypted);
}
测试代码:
try {
//1.创建相关的KEY
val key: SecretKey = EncryptALGForAES.createKEY()
Log.e("mike", "onContentChanged: 加密的KEY${key}")
val encyption = EncryptALGForAES.encyption("123", key)
//2.获取到加密之后的数据
val value = Base64.encodeToString(encyption,Base64.DEFAULT)
Log.e("mike", "onContentChanged: 加密之后的数据" + value)
val decyption = EncryptALGForAES.decyption(Base64.decode(value,Base64.DEFAULT))
//3.解密加密之后的数据
Log.e("mike", "onContentChanged: 解密之后的数据" + String(decyption))
} catch (e: NoSuchProviderException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: InvalidAlgorithmParameterException) {
e.printStackTrace()
} catch (e: Exception) {
e.printStackTrace()
}
加密的KEYandroid.security.keystore.AndroidKeyStoreSecretKey@e158c979
加密之后的数据v5HxYlV9g7W2M/+GKZvdDg==
解密之后的数据123
非对称加密(公、私钥加密)
非对称算法有两个密钥,一个用于数据加密,一个用于数据解密,这一对密钥是一起生成的,公钥可以自由分发暴露给外部,私钥必须隐藏
公私密钥有多种用途
1.客户A要发送数据局给服务端B,需要确保数据只有B可以看到,那么客户A可以获取到服务端B的公钥,然后将数据加密,然后发送到服务端B,只有B持有对应的私钥可以将数据进行解密
2.用户A向用户B发送消息,需要确保消息只有B可以看到,那么用户A将数据使用私钥加密,只能使用A的公钥对数据进行解密,确保消息是由A创建的
密钥对生成:
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private static final String RSA_MODEL = KeyProperties.KEY_ALGORITHM_RSA+"/"+KeyProperties.BLOCK_MODE_ECB+"/"+KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
private static final String ALIAS = "TEST_ALIAS";
public static KeyPair createKey() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
KeyGenParameterSpec parameterSpec =
new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
/*
* Sets the set of digests algorithms.Like SHA-256 SHA-384
* 用作验签操作(验签:私钥加密,公钥解密 用于验证数据传输的完整信)
*/
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setKeySize(2048)
.build();
generator.initialize(parameterSpec);
return generator.generateKeyPair();
}
加密操作:
public static byte[] encyption(byte[] data) throws NoSuchPaddingException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(RSA_MODEL);
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
PublicKey publicKey = keyStore.getCertificate(ALIAS).getPublicKey();
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
解密操作:
public static byte[] decyption(byte[] data) throws NoSuchPaddingException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnrecoverableKeyException {
Cipher cipher = Cipher.getInstance(RSA_MODEL);
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(ALIAS, null);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
测试代码:
try {
val key = EncryptALGForRSA.createKey()
Log.e("mike", "Public KEY " + String(Base64.encode(key.public.encoded, Base64.DEFAULT)))
val encyption = EncryptALGForRSA.encyption("abc".toByteArray())
val base64Encyption = Base64.encodeToString(encyption, Base64.DEFAULT)
Log.e("mike", "onContentChanged: 加密文本内容" + base64Encyption)
val decyption = EncryptALGForRSA.decyption(encyption)
Log.e("mike", "onContentChanged: 解密文本内容" + String(decyption))
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: NoSuchProviderException) {
e.printStackTrace()
} catch (e: InvalidAlgorithmParameterException) {
e.printStackTrace()
} catch (e: BadPaddingException) {
e.printStackTrace()
} catch (e: KeyStoreException) {
e.printStackTrace()
} catch (e: InvalidKeyException) {
e.printStackTrace()
} catch (e: UnrecoverableKeyException) {
e.printStackTrace()
} catch (e: CertificateException) {
e.printStackTrace()
} catch (e: NoSuchPaddingException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: IllegalBlockSizeException) {
e.printStackTrace()
}
Public KEY MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKL4a1LvWRZtsq6NK5eP5shtS9XoLfnD
OYwZWpis9dy5NqeGx/Azk6wh/0FuD+iDKE7MsRDPX6uCTWyDqSvXM4VMaHel4slSyZjL3ORxj1r7
EbFVnwsEnDfv8YNpN4jcWE60a/du6i+A3eJ823HCRw36DU5Lm5npm7JjDznH++n/86TAo1rr/tCV
0NJXXAqdv6F5JgVASUN+RvsGYULPnM473HpgamQRdY9jE2laZ3cQ0QUcdj3CLa9vumZdmpfmmE7W
bz/nzu8qfn9eiwoW3uAqh2TzyfcV3zd+nx2iFK9uqwii7SWGuFrEdTL+kKdcp45w2PN6ycv4FXp9
OJYw6wIDAQAB
加密文本内容jcv3RZCw1jnG0+qJgefBzpSZoPYFxtcnUZsUYV5hcHQ12fIP1q544MdE/VUuuXi5WfvnmsaL4nNo
O1uAAzWWXWfv2TIZ6ydvVliunO4F+nGtNtYFnOenObK34dvXdmmb2frkmXdLjP8DMHVEtz11JeU9
UCf8h9G+mH0j8msOGrisphinibl8ZauNY+njHWNNN+OGysvmWq9Q1+zaeYbKxvI6XADTaOnF7Lw+
WROVy4bApefYnnF7SOCL3fUkYXD/Fe0hMLD4Xe1nGTrvUTVLGZtlDDh5fPD8nxgJc7Rpj4yeReOW
/J6vrqRLIwjKXxV/Cj6ayC8g8lPY9V/yNI6kYQ==
解密文本内容abc
散列
哈希算法的作用是,对任意一种输入进行计算,得到一个固定长度的输出摘要
- 相同的输入一定得到相同的输出
- 不同的输入大概率得到不同的输出
哈希碰撞:不同的输入得到了相同的输出,哈希算法的输出长度越长碰撞的概率就越小越安全
代码示例:
MessageDigest.getInstance("SHA-512")?.let {
val result = StringBuffer()
it.digest("abc".toByteArray()).forEach {
result.append(((it and 0xff.toByte()) + 0x100).toString(16).substring(1))
}
Log.e("mike","sha-512 $result")
}
sha-512 df3513617aac417349e204131126a4e997e20aee64b553a21292a274f1836a3c233ebd454d4423643c80e2aa94f54c4f
密钥交换算法
在未知安全的地方进行加密密钥的交换
3.算法选择
推荐使用公认的强加密算法
- 不安全的算法:RC2 MD4 MD5 SHA1 3DES DES RC4 BLOWFISH
- 推荐使用的算法: SHA256 SHA512 AES RSA
4.密钥存储
加密技术通过密钥来确保数据安全,加密密钥的不安全存储泄露,会导致加密数据很容易被解密泄露敏感信息
- 尽可能使用系统SDK内置框架去管理密钥的生成以及存储
- 确认所有密钥和密码都受到保护,防止未经授权的访问
- 确保始终遵循行业标准的密钥管理流程
欢迎关注Mike的简书
Android 知识整理