前言

后端接口开发中,涉及到用户私密信息(用户名、密码)等,我们不能传输明文,必须使用加密方式传输。这次政府项目中,安全测试组提出了明文传输漏洞,抽空研究了下Java加解密相关知识,记录下。

散列函数

Java提供了一个名为MessageDigest的类,它属于java.security包。 此类支持诸如SHA-1SHA 256MD5之类的算法,以将任意长度的消息转换为信息摘要。

散列函数返回的值称为信息摘要或简称散列值。 下图说明了散列函数。

要使用散列函数加密数据,我们通常按照以下步骤执行:

创建MessageDigest对象

MessageDigest md = MessageDigest.getInstance("MD5");

MessageDigest提供了getInstance静态方法来获得MessageDigest实例,支持的类型可参考Wiki-SHA家族

将数据传递给创建的MessageDigest对象

md.update("gcdd1993".getBytes());

生成消息摘要

byte[] digest = md.digest();

通常我们会将其转换为Hex字符串

StringBuffer hexString = new StringBuffer();

for (byte aDigest : digest) {
hexString.append(Integer.toHexString(0xFF & aDigest));
}
System.out.println("Hex format : " + hexString.toString());

消息认证码

MAC(消息认证码)算法是一种对称密钥加密技术,用于提供消息认证。要建立MAC过程,发送方和接收方共享对称密钥K。

实质上,MAC是在基础消息上生成的加密校验和,它与消息一起发送以确保消息验证。

使用MAC进行身份验证的过程如下图所示

在Java中,javax.crypto包的Mac类提供了消息认证代码的功能。按照以下步骤使用此类创建消息身份验证代码。

创建KeyGenerator对象

KeyGenerator keyGen = KeyGenerator.getInstance("DES");

KeyGenerator支持以下类型:

  • AES (128)
  • DES (56)
  • DESede (168)
  • HmacSHA1
  • HmacSHA256

创建SecureRandom对象

SecureRandom secureRandom = new SecureRandom();

初始化KeyGenerator

keyGen.init(secureRandom);

生成密钥

Key key = keyGen.generateKey();

使用密钥初始化Mac对象

Mac mac = Mac.getInstance("HmacMD5");
mac.init(key);

Mac支持以下类型:

  • HmacMD5
  • HmacSHA1
  • HmacSHA256

完成mac操作

String msg = "gcdd1993";
byte[] bytes = msg.getBytes();
byte[] macResult = mac.doFinal(bytes);

数字签名

数字签名允许验证签名的作者,日期和时间,验证消息内容。 它还包括用于其他功能的身份验证功能。

优点

  • 认证

    数字签名有助于验证消息来源。

  • 完整性

    邮件签名后,邮件中的任何更改都将使签名无效。

  • 不可否认

    通过此属性,任何已签署某些信息的实体都不能在以后拒绝签名。

创建数字签名

创建KeyPairGenerator对象

KeyPairGenerator类提供getInstance()方法,该方法接受表示所需密钥生成算法的String变量,并返回生成密钥的KeyPairGenerator对象。

KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");

初始化KeyPairGenerator对象

KeyPairGenerator类提供了一个名为initialize()的方法,该方法用于初始化密钥对生成器。 此方法接受表示密钥大小的整数值。

keyPairGen.initialize(2048);

生成KeyPair

使用generateKeyPair()方法生成密钥对

KeyPair pair = keyPairGen.generateKeyPair();

从密钥对中获取私钥

PrivateKey privateKey = pair.getPrivate();

创建签名对象

Signature类的getInstance()方法接受表示所需签名算法的字符串参数,并返回相应的Signature对象。

Signature支持以下类型:

  • SHA1withDSA
  • SHA1withRSA
  • SHA256withRSA
Signature sign = Signature.getInstance("SHA256withDSA");

初始化签名对象

sign.initSign(privateKey);

将数据添加到Signature对象

String msg = "gcdd1993";
sign.update(msg.getBytes());

计算签名

byte[] signature = sign.sign();

验证签名

我们创建签名后,通常可以将私钥发送到客户端,以进行签名操作。服务端保存公钥,以进行签名验证

初始化签名对象以进行验证

使用公钥初始化签名对象

sign.initVerify(pair.getPublic());

更新要验证的数据

sign.update(msg.getBytes());

验证签名

boolean verify = sign.verify(signature);
Assert.assertTrue(verify);

公私钥加解密数据

可以使用javax.crypto包的Cipher类加密给定数据。

获取公私钥的步骤,与签名类似

KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair pair = keyPairGen.generateKeyPair();
PublicKey publicKey = pair.getPublic();

加密数据

创建一个Cipher对象

Cipher类的getInstance()方法接受表示所需转换的String变量,并返回实现给定转换的Cipher对象。

Cipher支持以下类型:

  • AES/CBC/NoPadding (128)
  • AES/CBC/PKCS5Padding (128)
  • AES/ECB/NoPadding (128)
  • AES/ECB/PKCS5Padding (128)
  • DES/CBC/NoPadding (56)
  • DES/CBC/PKCS5Padding (56)
  • DES/ECB/NoPadding (56)
  • DES/ECB/PKCS5Padding (56)
  • DESede/CBC/NoPadding (168)
  • DESede/CBC/PKCS5Padding (168)
  • DESede/ECB/NoPadding (168)
  • DESede/ECB/PKCS5Padding (168)
  • RSA/ECB/PKCS1Padding (1024, 2048)
  • RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
  • RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

使用公钥初始化Cipher对象

Cipher类的init()方法接受两个参数,一个表示操作模式的整数参数(加密/解密)和一个表示公钥的Key对象。

cipher.init(Cipher.ENCRYPT_MODE, publicKey);

将数据添加到Cipher对象

Cipher类的update()方法接受表示要加密的数据的字节数组,并使用给定的数据更新当前对象。

String msg = "gcdd1993";
cipher.update(msg.getBytes());

加密数据

byte[] cipherText = cipher.doFinal();

解密数据

使用私钥初始化Cipher对象

cipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());

解密数据

byte[] decipheredText = cipher.doFinal(cipherText);
Assert.assertEquals(msg, new String(decipheredText));

第三方类库

前后端适用且应用广泛的是Crypto-JS,使用 Crypto-JS 可以非常方便地在 JavaScript 进行 MD5、SHA1、SHA2、SHA3、RIPEMD-160 哈希散列,进行 AES、DES、Rabbit、RC4、Triple DES 加解密。

AES加密

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

一般来说,我们可以在服务端随机生成密钥,然后将密钥发送给客户端进行加密,上传密文到服务端,服务端进行解密。

本文只讨论Java的AES加解密方式。

引入Jar包

compile group: 'org.webjars.npm', name: 'crypto-js', version: '3.1.8'

生成密钥

Random random = new Random();
byte[] key = new byte[16];
random.nextBytes(key);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

生成偏移量

byte[] iv = new byte[16];
random.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);

创建Cipher对象

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

初始化Cipher为加密工作过程

cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

加密

byte[] original = cipher.doFinal(encrypted1);

AES解密

初始化Cipher为解密工作过程

cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

解密

byte[] bytes = cipher.doFinal(original);
Assert.assertEquals(data, new String(bytes, StandardCharsets.UTF_8));

AES加解密总结

实际项目中,可以按照以下方式实现对称加密

  1. 服务端提供一个接口,该接口负责随机生成key(密码)和iv(偏移量),并将其存入redis(设置超时时间)
  2. 客户端调用接口,获得key和iv以及一个redis_key,进行数据加密,将加密后的数据以及redis_key传到服务端
  3. 服务端使用redis_key获得key和iv,进行解密

总结

在Java EE安全里,主要是进行客户端加密,以及服务端解密的过程来实现数据安全传输的目的。在这个过程中,特别要注意以下几点:

  • 随机性:加密方式不可单一,可通过更换Cipher.getInstance()的String值来随机生成加密工人进行加密。
  • 保密性:加密使用的密钥或者偏移量等,需要使用超时、模糊目的等手段进行隐藏,加大破解成本。

没有完全有效的加密,但是只要做到破解成本大于加密成本,就是有效的加密。这样,我们可以不断地更换加密方式达到我们想要的效果。

Java安全笔记的更多相关文章

  1. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  2. 0035 Java学习笔记-注解

    什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...

  3. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

  4. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

  5. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  6. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  7. 0025 Java学习笔记-面向对象-final修饰符、不可变类

    final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...

  8. [Java入门笔记] 面向对象编程基础(二):方法详解

    什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...

  9. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  10. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

随机推荐

  1. BZOJ 3343 教主的魔法(分块)

    题意: 有一个1e6的数组,t次操作:将[l,r]内的值增加w,或者查询[l,r]内的值大于等于add的 思路: 分块,块大小为sqrt(n),每次只需要暴力头尾两块,中间的整块打标记, 对于查询查操 ...

  2. 2018icpc南京现场赛-I Magic Potion(最大流)

    题意: n个英雄,m个怪兽,第i个英雄可以打第i个集合里的怪兽,一个怪兽可以在多个集合里 有k瓶药水,每个英雄最多喝一次,可以多打一只怪兽,求最多打多少只 n,m,k<=500 思路: 最大流, ...

  3. 不用'+'完成a + b

    这也是'+'底层运算原理: #include<bits/stdc++.h> using namespace std; int a,b; int main() { scanf("% ...

  4. 12-Tomcat&Servlet:

    今日知识 1. web相关概念回顾 2. web服务器软件:tomcat 3. servletr入门学习 web相关概念回顾 1. 软件架构 1. C/S:客户端/服务器端 2. B/S:浏览器/服务 ...

  5. node -- express框架

    express node的一个框架 安装express cnpm install express -S 引入 const express = require("express"); ...

  6. css3 动画 示例

    /* animation */ .a-bounce,.a-flip,.a-flash,.a-shake,.a-swing,.a-wobble,.a-ring{-webkit-animation:1s ...

  7. 如何在 Linux 中找出 CPU 占用高的进程

    1) 怎样使用 top 命令找出 Linux 中 CPU 占用高的进程 在所有监控 Linux 系统性能的工具中,Linux 的 top 命令是最好的也是最知名的一个.top 命令提供了 Linux ...

  8. 2020/1/4 H5&&C3笔记

    1. 类名不能由数字开头 2.float 是float 属性定义元素在哪个方向浮动.有left / right / none / inherit四个 参考https://www.w3school.co ...

  9. printf函数中*修饰符的作用,如:%*d

    在printf函数中,我们可以用数字修饰来控制打印的字段宽度和精度,如下(为强调视觉效果,均填充0): #include <stdio.h> int main() { ; float f= ...

  10. 【STM32H7教程】第62章 STM32H7的MDMA,DMA2D和通用DMA性能比较

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第62章       STM32H7的MDMA,DMA2D和通 ...