zoukankan      html  css  js  c++  java
  • Android安全防护防护———加密算法

    摘要

    这篇文章本来早就应该写了,但是由于项目一直开发新的需求,就拖后了。现在有时间了,必须得写了。现在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,我是要加密的数据

    以上就是我们比较常用的几种加密的一些内容。好了,这篇内容就到这,文中如果错误,麻烦大神指教,共同进步

  • 相关阅读:
    微服务简介
    docker跨主机通信扁平化网络的设计与实现
    docker学习ppt
    docker原理介绍
    我的博客搬家啦
    响应式之像素和viewport
    换行+省略号
    ITerms2在mac系统下的安装和配色,并和go2shell关联
    利其器之webstorm快捷键
    React入门 (2)—实现微博展示列表
  • 原文地址:https://www.cnblogs.com/huangjialin/p/9694488.html
Copyright © 2011-2022 走看看