摘要
这篇文章本来早就应该写了,但是由于项目一直开发新的需求,就拖后了。现在有时间了,必须得写了。现在Android应用程序对安全防范这方面要求越来越高了。特别是金融行业,如果金融app没有没有做好相应安全处理,那些很容易被一些Hacker(黑客)所攻击。并不是说做了这些安全防范,这个应用就百分之百的安全的。只是说能够尽可能加大破解难度。也许有些开发者或者企业觉得。我们公司的app,数据量这些少,会有那个黑客吃饱了没事做来破解啊。又不是支付宝,或者其他那些用户量很多的应用。如果是这样想的话,那只能说目光短浅了。
Android应用常用的加密算法
如果说按加密的内容是否可以还原,可以分为可逆加密和非可逆加密。
非可逆加密:也就是说加密后的数据是不能还原成原来的数据。比如MD5加密 加密一个密码:123456 加密后成: afabsbfbabf437hfbbff73(结果并不一定是这个,只是举例)。也就是说加密后的结果afabsbfbabf437hfbbff73是不能够在解密出123456这个值的。
可逆加密:可逆加密有一个公钥和一个私钥,通过公钥进行数据的加密,通过私钥进行解密。代表有:RSA,AES。
对称加密和非对称加密:可逆加密根据其使用加解密是否使用同一个密钥又分为对称加密(加解密使用同一个密钥)和非对称加密(加解密的密钥分开)
MD5
MD5的特点:
1、压缩性:任意长度的数据,算出来的MD5值的长度都是固定。
2、容易计算性:从原始数据计算出MD5值是很容易的。
3、抗修改性:愿数据只要有一点点的改动,得到的MD5差别都是很大的。
4、强抗碰撞性:从原数据计算出来的MD5,想要找到一个具有相同的MD5,非常难。
MD5的应用场景:
1、一致性验证(比如下载某个文件,不知道文件是否下载完成,可以MD5进行校验。加密文件比较耗时,需要放到子线程中)
2、密码的存储(如登陆注册这些,账号密码会保存到sp中,直接就保存到账号密码的MD5值就好了。这样也可以避免服务器权限者知道这个密码)
MD5的简单使用
先写一个MD5的工具类
1 package com.example.huangjialin.md5test; 2 3 import java.io.UnsupportedEncodingException; 4 import java.security.MessageDigest; 5 import java.security.NoSuchAlgorithmException; 6 7 /** 8 * Created by huangjialin on 2018/9/4. 9 */ 10 11 public class Utils { 12 13 public static String md5(String content) { 14 byte[] hash = null; 15 try { 16 hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8")); 17 } catch (NoSuchAlgorithmException e) { 18 e.printStackTrace(); 19 } catch (UnsupportedEncodingException e) { 20 e.printStackTrace(); 21 } 22 23 StringBuilder stringBuilder = new StringBuilder(hash.length * 2); 24 for (byte b: hash) { 25 if ((b & 0xFF) < 0x10){ 26 stringBuilder.append("0"); 27 28 } 29 stringBuilder.append(Integer.toHexString(b & 0xFF)); 30 31 } 32 return stringBuilder.toString(); 33 } 34 35 }
简单的解释一下上面的,首先是通过MessageDigest.getInstance(“MD5”)来获取到MessageDigest这个类,这个类是java自带的一个加密类,然后通过调用digest()方法来的获取到加密后的字节数组。该方法传入的参数是byte[] input 所以还需要将字符串转化为byte[]。得到加密后的字节数组以后,将他们转换成16禁止的字符串,然后拼接起来就可以了。
然后直接调用:
1 /** 2 3 * MD5加密 4 5 */ 6 7 button.setOnClickListener(new View.OnClickListener() { 8 9 @Override 10 11 public void onClick(View v) { 12 13 14 15 String md5_123456abc = Utils.md5("123456abc"); 16 17 String md5_huangjialin = Utils.md5("huangjialin"); 18 19 Log.i("huangjialin"," md5_123456abc算出的MD5值是: " + md5_123456abc); 20 21 Log.i("huangjialin"," md5_huangjialin算出的MD5值是: " + md5_huangjialin); 22 23 } 24 25 });
得出的结果:
1 09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin: md5_123456abc算出的MD5值是: df10ef8509dc176d733d59549e7dbfaf 2 3 09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin: md5_huangjialin算出的MD5值是: 08e768954478c8669619d7d087db0070
这里说一句题外话:Log输出日志有很多种如Log.i();Log.d()等等,但是现在有些手机厂商直接就把等级较低的日志给屏蔽掉,所以有些日志输出在有些手机可以看到,有些手机没有看到。解决办法就是换输出等级较高的就OK了。
RSA
RSA是现在比较流行的一种非对称加密的,它需要一对密钥(公钥和私钥)公钥进行加密,私钥进行解密。
RSA的加密原理
1、随机选择两个大的质数P和Q,P不等于Q,计算出结果:N = P*Q;
2、选择一个大于1,小于N的自然数E,E必须和(P-1)*(Q-1)互素。
3、用公式计算出D:D*E = mod(P-1)*(Q-1)
4、销毁P和Q
最终得到的N,E就是公钥,D就是私钥了。
RSA加解密步骤
1、甲方生成密钥对(公钥和私钥,公钥用来加密数据,私钥自己保留,用来解密数据)
2、甲方使用私钥加密数据,然后用私钥对加密后的数据签名,并把这些放送给乙方,乙方使用公钥,签名来验证带解密数据是否有效,如果有效就使用公钥对数据进行解密
3、乙方使用公钥加密数据,向甲方发送经过加密后的数据,甲方或者加密数据后,就可以通过私钥进行解密了。
RSA使用场景
项目中一些敏感的数据,比如身份证号,银行卡,等相关信息可通过加密后在传给服务器,服务器使用私钥进行解密。
RSA密钥对生成
RSA的密钥对生成方式有两种
1 /* 2 初始化KeyPairGenerator类,并获取到公钥和私钥 3 */ 4 byte[] publicKeyByte; 5 byte[] prvateKtyByte; 6 7 public void getKey() { 8 try { 9 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(“RSA”);//KeyPairGenerator类是java专门提供生成密钥对的一个类。 10 keyPairGenerator.initialize(1024); //设置密钥对的大小 11 KeyPair keyPair = keyPairGenerator.generateKeyPair(); 12 PrivateKey privateKey = keyPair.getPrivate();//获取私钥 13 PublicKey publicKey = keyPair.getPublic();//获取公钥 14 prvateKtyByte = privateKey.getEncoded();//私钥对应的字节数组 15 publicKeyByte = publicKey.getEncoded(); //公钥对应的字节数组 16 17 } catch (NoSuchAlgorithmException e) { 18 e.printStackTrace(); 19 } 20 }
当然上面这种生成密钥对的方式,基本很少会在项目中使用使用,用得比较多的还是第二中方式。
第二种是通过OpenSSl工具生成密钥对
这种生成密钥对的方式需要安装OpenSSl。这里就不说具体怎么安装了。这里简单的说一下生成密钥对所需要的一些命令
使用命令生成私钥:
1 genrsa -out rsa_private_key.pem 1024
这条命令是让openssl随机生成一份私钥,长度为1024
使用命令生成公钥:
1 rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
命令成功以后,就会在openSSL下的bin目录下生成公钥和私钥,然后就可以进行加密和解密了。
加密
1 /** 2 * 加密 3 */ 4 5 6 @RequiresApi(api = Build.VERSION_CODES.O) 7 public byte[] encryption(String content) { 8 byte[] result = null; 9 try { 10 Cipher cipher = Cipher.getInstance("RSA"); 11 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyByte); 12 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 13 PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); 14 cipher.init(Cipher.ENCRYPT_MODE, publicKey); 15 result = cipher.doFinal(content.getBytes()); 16 Log.i("huangjialin", "----> " + Base64.getEncoder().encodeToString(result)); 17 } catch (NoSuchAlgorithmException e) { 18 e.printStackTrace(); 19 } catch (NoSuchPaddingException e) { 20 e.printStackTrace(); 21 } catch (InvalidKeySpecException e) { 22 e.printStackTrace(); 23 } catch (InvalidKeyException e) { 24 e.printStackTrace(); 25 } catch (BadPaddingException e) { 26 e.printStackTrace(); 27 } catch (IllegalBlockSizeException e) { 28 e.printStackTrace(); 29 } 30 return result; 31 }
解密
1 /** 2 * 解密 3 */ 4 5 @RequiresApi(api = Build.VERSION_CODES.O) 6 7 public void decryption() { 8 9 Cipher cipher = null; 10 try { 11 cipher = Cipher.getInstance("RSA"); 12 //私钥需要通过PKCS8EncodedKeySpec来读取 13 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(prvateKtyByte); 14 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 15 //生成私钥 16 PrivateKey privateKey = keyFactory.generatePrivate(keySpec); 17 cipher.init(Cipher.DECRYPT_MODE, privateKey); 18 //String content = "123456"; 19 byte[] input = encryption("123456"); 20 byte[] result = cipher.doFinal(input); 21 Log.i("huangjialin", "--解密--> " + new String(result)); 22 //Assert.assertTrue(content.equals(new String(result))); 23 24 25 } catch (NoSuchAlgorithmException e) { 26 e.printStackTrace(); 27 } catch (NoSuchPaddingException e) { 28 e.printStackTrace(); 29 } catch (BadPaddingException e) { 30 e.printStackTrace(); 31 } catch (IllegalBlockSizeException e) { 32 e.printStackTrace(); 33 } catch (InvalidKeyException e) { 34 e.printStackTrace(); 35 } catch (InvalidKeySpecException e) { 36 e.printStackTrace(); 37 } 38 39 40 }
当然上面的代码是我写测试用的,真正项目中,还得封装好,把它弄成工具类,进行调用。
AES
AES是一个对称加密,也就是说使用AES进行加密和解密,他们使用的密钥都是一样的。AES加密算法是密码学中的高级加密标准,又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析并使用。同时AES他的算法加密强度大,执行效率很高。
AES使用场景
1、由于AES是对称加密,加解密都是使用同一个密钥,所以说在项目中一些敏感的数据需要保存到本地。可以先同AES的密钥进行加密,需要用的使用,将数据取出来再进行解密。
2、可以进行对一些敏感数据进行加密,然后在传递给服务器。
AES使用
在Android7.0之前可以这样获取到密钥
1 private SecretKey generateKey(String seed) throws Exception { 2 // 获取秘钥生成器 3 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 4 // 通过种子初始化 5 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto"); 6 7 secureRandom.setSeed(seed.getBytes("UTF-8")); 8 keyGenerator.init(128, secureRandom); 9 // 生成秘钥并返回 10 return keyGenerator.generateKey(); 11 }
但是在Android7.0之后就不支持了,移除了Crypto。当然也这种获取密钥方式在7.0之后Google也给出了解决方案,但是官方并不建议这样来获取。具体的可以看这里。https://android-developers.googleblog.com/2016/06/security-crypto-provider-deprecated-in.html
官方给出的是另一种方式,并不需要获取密钥,而是定义密码的形式。
1 package com.example.huangjialin.md5test; 2 3 import android.os.Bundle; 4 import android.support.v7.app.AppCompatActivity; 5 import android.util.Base64; 6 import android.util.Log; 7 import android.view.View; 8 import android.widget.Button; 9 import android.widget.EditText; 10 import android.widget.TextView; 11 12 import javax.crypto.Cipher; 13 import javax.crypto.SecretKey; 14 import javax.crypto.spec.SecretKeySpec; 15 16 public class MainActivity extends AppCompatActivity { 17 private EditText edittext; 18 private Button button, jiami, jiemi; 19 private TextView textView; 20 private SecretKey secretKey; 21 private byte[] bytes; 22 private String content = "huangjialin,我是要加密的数据"; 23 String password = "huangji黄家磷"; 24 25 @Override 26 protected void onCreate(Bundle savedInstanceState) { 27 super.onCreate(savedInstanceState); 28 setContentView(R.layout.activity_main); 29 30 edittext = findViewById(R.id.edittext); 31 button = findViewById(R.id.button); 32 textView = findViewById(R.id.textview); 33 jiami = findViewById(R.id.jiami); 34 jiemi = findViewById(R.id.jiemi); 35 36 Log.i("huagjialin", "--加密的数据-- > " + content); 37 38 /** 39 * 获取密钥 40 */ 41 /* button.setOnClickListener(new View.OnClickListener() { 42 @Override 43 public void onClick(View v) { 44 try { 45 secretKey = generateKey("huangjiain"); 46 } catch (Exception e) { 47 e.printStackTrace(); 48 } 49 50 } 51 });*/ 52 53 54 /** 55 * 加密 56 */ 57 58 jiami.setOnClickListener(new View.OnClickListener() { 59 @Override 60 public void onClick(View v) { 61 try { 62 bytes = encrypt(content, password); 63 String str = new String(bytes); 64 Log.i("huagjialin", "--加密后的数据-- > " + Base64.decode(str,Base64.DEFAULT)); 65 } catch (Exception e) { 66 e.printStackTrace(); 67 } 68 69 } 70 }); 71 72 /** 73 * 解密 74 */ 75 76 jiemi.setOnClickListener(new View.OnClickListener() { 77 @Override 78 public void onClick(View v) { 79 try { 80 byte[] by = decrypt(bytes, password); 81 String string = new String(by); 82 Log.i("huagjialin", "--解密后的数据-- > " + string); 83 } catch (Exception e) { 84 e.printStackTrace(); 85 } 86 } 87 }); 88 89 90 } 91 92 93 94 95 96 97 /** 98 * 另一种加密形式 99 */ 100 private byte[] encrypt(String content, String password) throws Exception { 101 // 创建AES秘钥 102 SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING"); 103 // 创建密码器 104 Cipher cipher = Cipher.getInstance("AES"); 105 // 初始化加密器 106 cipher.init(Cipher.ENCRYPT_MODE, key); 107 // 加密 108 return cipher.doFinal(content.getBytes("UTF-8")); 109 } 110 111 112 /** 113 * 解密 114 */ 115 private byte[] decrypt(byte[] content, String password) throws Exception { 116 // 创建AES秘钥 117 SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING"); 118 // 创建密码器 119 Cipher cipher = Cipher.getInstance("AES"); 120 // 初始化解密器 121 cipher.init(Cipher.DECRYPT_MODE, key); 122 // 解密 123 return cipher.doFinal(content); 124 } 125 126 }
1 09-20 21:12:36.394 15933-15933/com.example.huangjialin.md5test I/huagjialin: --加密的数据-- > huangjialin,我是要加密的数据 2 09-20 21:12:39.561 15933-15933/com.example.huangjialin.md5test I/huagjialin: --加密后的数据-- > [B@d62495e 3 09-20 21:12:41.829 15933-15933/com.example.huangjialin.md5test I/huagjialin: --解密后的数据-- > huangjialin,我是要加密的数据
以上就是我们比较常用的几种加密的一些内容。好了,这篇内容就到这,文中如果错误,麻烦大神指教,共同进步