摘要
这篇文章本来早就应该写了, 但是由于项目一直开发新的需求, 就拖后了. 现在有时间了, 必须得写了. 现在 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 的工具类
- package com.example.huangjialin.md5test;
- import java.io.UnsupportedEncodingException;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- /**
- * Created by huangjialin on 2018/9/4.
- */
- public class Utils {
- public static String md5(String content) {
- byte[] hash = null;
- try {
- hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8"));
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- StringBuilder stringBuilder = new StringBuilder(hash.length * 2);
- for (byte b: hash) {
- if ((b & 0xFF) <0x10){
- stringBuilder.append("0");
- }
- stringBuilder.append(Integer.toHexString(b & 0xFF));
- }
- return stringBuilder.toString();
- }
- }
简单的解释一下上面的, 首先是通过 MessageDigest.getInstance("MD5")来获取到 MessageDigest 这个类, 这个类是 java 自带的一个加密类, 然后通过调用 digest()方法来的获取到加密后的字节数组. 该方法传入的参数是 byte[] input 所以还需要将字符串转化为 byte[]. 得到加密后的字节数组以后, 将他们转换成 16 禁止的字符串, 然后拼接起来就可以了.
然后直接调用:
- /**
- * MD5 加密
- */
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String md5_123456abc = Utils.md5("123456abc");
- String md5_huangjialin = Utils.md5("huangjialin");
- Log.i("huangjialin","md5_123456abc 算出的 MD5 值是:" + md5_123456abc);
- Log.i("huangjialin","md5_huangjialin 算出的 MD5 值是:" + md5_huangjialin);
- }
- });
得出的结果:
09-20 15:33:12.208 7352-7352/com.example.huangjialin.md5test I/huangjialin: md5_123456abc 算出的 MD5 值是: df10ef8509dc176d733d59549e7dbfaf
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 的密钥对生成方式有两种
- /*
- 初始化 KeyPairGenerator 类, 并获取到公钥和私钥
- */
- byte[] publicKeyByte;
- byte[] prvateKtyByte;
- public void getKey() {
- try {
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");//KeyPairGenerator 类是 java 专门提供生成密钥对的一个类.
- keyPairGenerator.initialize(1024); // 设置密钥对的大小
- KeyPair keyPair = keyPairGenerator.generateKeyPair();
- PrivateKey privateKey = keyPair.getPrivate();// 获取私钥
- PublicKey publicKey = keyPair.getPublic();// 获取公钥
- prvateKtyByte = privateKey.getEncoded();// 私钥对应的字节数组
- publicKeyByte = publicKey.getEncoded(); // 公钥对应的字节数组
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- }
- }
当然上面这种生成密钥对的方式, 基本很少会在项目中使用使用, 用得比较多的还是第二中方式.
第二种是通过 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 目录下生成公钥和私钥, 然后就可以进行加密和解密了.
加密
- /**
- * 加密
- */
- @RequiresApi(API = Build.VERSION_CODES.O)
- public byte[] encryption(String content) {
- byte[] result = null;
- try {
- Cipher cipher = Cipher.getInstance("RSA");
- X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyByte);
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- result = cipher.doFinal(content.getBytes());
- Log.i("huangjialin", "---->" + Base64.getEncoder().encodeToString(result));
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (NoSuchPaddingException e) {
- e.printStackTrace();
- } catch (InvalidKeySpecException e) {
- e.printStackTrace();
- } catch (InvalidKeyException e) {
- e.printStackTrace();
- } catch (BadPaddingException e) {
- e.printStackTrace();
- } catch (IllegalBlockSizeException e) {
- e.printStackTrace();
- }
- return result;
- }
解密
- /**
- * 解密
- */
- @RequiresApi(API = Build.VERSION_CODES.O)
- public void decryption() {
- Cipher cipher = null;
- try {
- cipher = Cipher.getInstance("RSA");
- // 私钥需要通过 PKCS8EncodedKeySpec 来读取
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(prvateKtyByte);
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- // 生成私钥
- PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- //String content = "123456";
- byte[] input = encryption("123456");
- byte[] result = cipher.doFinal(input);
- Log.i("huangjialin", "-- 解密 -->" + new String(result));
- //Assert.assertTrue(content.equals(new String(result)));
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (NoSuchPaddingException e) {
- e.printStackTrace();
- } catch (BadPaddingException e) {
- e.printStackTrace();
- } catch (IllegalBlockSizeException e) {
- e.printStackTrace();
- } catch (InvalidKeyException e) {
- e.printStackTrace();
- } catch (InvalidKeySpecException e) {
- e.printStackTrace();
- }
- }
当然上面的代码是我写测试用的, 真正项目中, 还得封装好, 把它弄成工具类, 进行调用.
AES
AES 是一个对称加密, 也就是说使用 AES 进行加密和解密, 他们使用的密钥都是一样的. AES 加密算法是密码学中的高级加密标准, 又称 Rijndael 加密法, 是美国联邦政府采用的一种区块加密标准. 这个标准用来替代原先的 DES, 已经被多方分析并使用. 同时 AES 他的算法加密强度大, 执行效率很高.
AES 使用场景
1, 由于 AES 是对称加密, 加解密都是使用同一个密钥, 所以说在项目中一些敏感的数据需要保存到本地. 可以先同 AES 的密钥进行加密, 需要用的使用, 将数据取出来再进行解密.
2, 可以进行对一些敏感数据进行加密, 然后在传递给服务器.
AES 使用
在 Android7.0 之前可以这样获取到密钥
- private SecretKey generateKey(String seed) throws Exception {
- // 获取秘钥生成器
- KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
- // 通过种子初始化
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto");
- secureRandom.setSeed(seed.getBytes("UTF-8"));
- keyGenerator.init(128, secureRandom);
- // 生成秘钥并返回
- return keyGenerator.generateKey();
- }
但是在 Android7.0 之后就不支持了, 移除了 Crypto. 当然也这种获取密钥方式在 7.0 之后 Google 也给出了解决方案, 但是官方并不建议这样来获取. 具体的可以看这里.
官方给出的是另一种方式, 并不需要获取密钥, 而是定义密码的形式.
- package com.example.huangjialin.md5test;
- import Android.os.Bundle;
- import Android.support.v7.App.AppCompatActivity;
- import Android.util.Base64;
- import Android.util.Log;
- import Android.view.View;
- import Android.widget.Button;
- import Android.widget.EditText;
- import Android.widget.TextView;
- import javax.crypto.Cipher;
- import javax.crypto.SecretKey;
- import javax.crypto.spec.SecretKeySpec;
- public class MainActivity extends AppCompatActivity {
- private EditText edittext;
- private Button button, jiami, jiemi;
- private TextView textView;
- private SecretKey secretKey;
- private byte[] bytes;
- private String content = "huangjialin, 我是要加密的数据";
- String password = "huangji 黄家磷";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- edittext = findViewById(R.id.edittext);
- button = findViewById(R.id.button);
- textView = findViewById(R.id.textview);
- jiami = findViewById(R.id.jiami);
- jiemi = findViewById(R.id.jiemi);
- Log.i("huagjialin", "-- 加密的数据 -->" + content);
- /**
- * 获取密钥
- */
- /* button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- secretKey = generateKey("huangjiain");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });*/
- /**
- * 加密
- */
- jiami.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- bytes = encrypt(content, password);
- String str = new String(bytes);
- Log.i("huagjialin", "-- 加密后的数据 -->" + Base64.decode(str,Base64.DEFAULT));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- /**
- * 解密
- */
- jiemi.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- byte[] by = decrypt(bytes, password);
- String string = new String(by);
- Log.i("huagjialin", "-- 解密后的数据 -->" + string);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- }
- /**
- * 另一种加密形式
- */
- private byte[] encrypt(String content, String password) throws Exception {
- // 创建 AES 秘钥
- SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING");
- // 创建密码器
- Cipher cipher = Cipher.getInstance("AES");
- // 初始化加密器
- cipher.init(Cipher.ENCRYPT_MODE, key);
- // 加密
- return cipher.doFinal(content.getBytes("UTF-8"));
- }
- /**
- * 解密
- */
- private byte[] decrypt(byte[] content, String password) throws Exception {
- // 创建 AES 秘钥
- SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES/CBC/PKCS5PADDING");
- // 创建密码器
- Cipher cipher = Cipher.getInstance("AES");
- // 初始化解密器
- cipher.init(Cipher.DECRYPT_MODE, key);
- // 解密
- return cipher.doFinal(content);
- }
- }
09-20 21:12:36.394 15933-15933/com.example.huangjialin.md5test I/huagjialin: -- 加密的数据 --> huangjialin, 我是要加密的数据
09-20 21:12:39.561 15933-15933/com.example.huangjialin.md5test I/huagjialin: -- 加密后的数据 --> [B@d62495e
09-20 21:12:41.829 15933-15933/com.example.huangjialin.md5test I/huagjialin: -- 解密后的数据 --> huangjialin, 我是要加密的数据
以上就是我们比较常用的几种加密的一些内容. 好了, 这篇内容就到这, 文中如果错误, 麻烦大神指教, 共同进步
来源: https://www.cnblogs.com/huangjialin/p/9694488.html