客户端与服务器交互使用AES+RSA混合加密原理详解

前言

最近维护公司APP应用的登录模块,由于测试人员用Fiddler抓包工具抓取到了公司关于登录时候的明文登录信息。虽然使用的是HTTPS的方式进行http请求的,但还是被Fiddler抓到了明文内容。因此,需要对之前未加密的登录信息进行加密。在网上搜到一篇关于AES+RSA加密方案的文章,如下面链接所示,按照该方案成功解决了加密问题,在这里记录一下。


首先来看看未加密时,通过Fiddler抓包获取的明文信息如图1所示:未加密的抓包截图

图1 未加密时候,Fiddler抓包获取的请求体


图2 未加密时候,Fiddler抓包获取的请求头


通过图1可以明显地看到所有的http请求信息都是透明的。如果真的有有心人去盗窃用户的信息的话,会造成多大的损失。<喎?"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="加密之后的抓包截图">加密之后的抓包截图 图4 加密之后,Fiddler抓包获取的请求体


图5 加密之后,Fiddler抓包获取的请求头


通过图4,可以看到所有的请求体都通过AES加密后,再使用Base64进行编解码转换后的请求体,即使是被有心人去窃取了,也很难在有效的时间内进行破解。


首先来看一张图,来看看实现该需求用到的几个常用的加解密名词

图6 加解密常用名词


基本需求及概念

随着Internet网的广泛应用,信息安全问题日益突出,以数据加密技术为核心的信息安全技术也得到了极大的发展。目前的数据加密技术根据加密密钥类型可分私钥加密(对称加密)系统和公钥加密(非对称加密)系统。

对称加密算法是较传统的加密体制,通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单和加密速度快的优点,目前仍然是主流的密码体制之一。最常用的对称密码算法是数据加密标准(DES)算法,但是由于DES密钥长度较短,已经不适合当今分布式开放网络对数据加密安全性的要求。最后,一种新的基于Rijndael算法对称高级数据加密标准AES取代了数据加密标准DES。非对称加密由于加/解密钥不同(公钥加密,私钥解密),密钥管理简单,也得到广泛应用。RSA是非对称加密系统最著名的公钥密码算法。

AES算法

AES基本原理及算法流程

美国国家标准和技术研究所(NIST)经过三轮候选算法筛选,从众多的分组密码中选中Rijndael算法作为高级加密标准(AES)。Rijndael密码是一个迭代型分组密码,分组长度和密码长度都是可变的,分组长度和密码长度可以独立的指定为128比特,192比特或者256比特。AES的加密算法的数据处理单位是字节,128位的比特信息被分成16个字节,按顺序复制到一个4*4的矩阵中,称为状态(state),AES的所有变换都是基于状态矩阵的变换。

用Nr表示对一个数据分组加密的轮数(加密轮数与密钥长度的关系如表1所示)。在轮函数的每一轮迭代中,包括四步变换,分别是字节代换运算(ByteSub())、行变换(ShiftRows())、列混合(MixColumns())以及轮密钥的添加变换AddRoundKey()[3],其作用就是通过重复简单的非线形变换、混合函数变换,将字节代换运算产生的非线性扩散,达到充分的混合,在每轮迭代中引入不同的密钥,从而实现加密的有效性。

表1 是三种不同类型的AES加密密钥分组大小与相应的加密轮数的对照表。加密开始时,输入分组的各字节按表2 的方式装入矩阵state中。如输入ABCDEFGHIJKLMNOP,则输入块影射到如表2的状态矩阵中。

表1:

AES类型 密钥长度 分组长度 加密轮数
AES-128 4字 4字 10
AES-192 6字 4字 12
AES-256 8字 4字 14

表2:

A E I M
B F J N
C G K O
D H L P

1、字节代换运算(ByteSub())

字节代换运算是一个可逆的非线形字节代换操作,对分组中的每个字节进行,对字节的操作遵循一个代换表,即S盒。S盒由有限域 GF(28)上的乘法取逆和GF(2)上的仿射变换两步组成。

2、行变换ShiftRows()

行变换是一种线性变换,其目的就是使密码信息达到充分的混乱,提高非线形度。行变换对状态的每行以字节为单位进行循环右移,移动字节数根据行数来确定,第0行不发生偏移,第一行循环右移一个字节,第二行移两个,依次类推。

+3、 列混合变换MixColumns()

列变换就是从状态中取出一列,表示成多项式的形式后,用它乘以一个固定的多项式a(x),然后将所得结果进行取模运算,模值为 x4+1。其中a(x)={03}x3+{02}x2+{01}x+{02},
这个多项式与x4+1互质,因此是可逆的。列混合变换的算术表达式为:s’(x)= a(x) s(x),其中, s(x)表示状态的列多项式。

4、轮密钥的添加变换AddRoundKey()
在这个操作中,轮密钥被简单地异或到状态中,轮密钥根据密钥表获得,其长度等于数据块的长度Nb。

AES算法流程

对于发送方,它首先创建一个AES私钥,并用口令对这个私钥进行加密。然后把用口令加密后的AES密钥通过Internet发送到接收方。发送方解密这个私钥,并用此私钥加密明文得到密文,密文和加密后的AES密钥一起通过Internet发送到接收方。接收方收到后再用口令对加密密钥进行解密得到AES密钥,最后用解密后的密钥把收到的密文解密成明文。图7中是这个过程的实现流程。 图7 AES算法流程


RSA算法

RSA算法基本原理及流程

RSA是在1977年发明RSA密码系统的三个人的名字的首字母的缩写,他们是:Ron Rivest、Adi Shamir和Leonard Adleman。它是第一个公钥加密算法,在很多密码协议中都有应用,如SSL和S/MIME。RSA算法是基于大质数的因数分解的公匙体系。简单的讲,就是两个很大的质数,一个作为公钥,另一个作为私钥,如用其中一个加密,则用另一个解密。密钥长度从40到2048位可变,密钥越长,加密效果越好,但加密解密的开销也大。RSA算法可简单描述如下:

1
2
3
<code>公开密钥:n=pq,(p,q为两个不同的很大的质数,p和q必须保密)
将(p- 1 )和(q- 1 )相乘得到φ(n)
选择一个整数e ( 1 <e<φ(n))与φ(n)互质 c= "mc(mod" code= "" d= "e-1modφ(n),即计算一个数字d,使得它满足公式" de= "1" m= "cd(mod" ></e<φ(n))与φ(n)互质></code>

RSA算法实现流程

首先,接收方创建RSA密匙对,即一个公钥和一个私钥,公钥被发送到发送方,私钥则被保存在接收方。发送方在接收到这个公钥后,用该公钥对明文进行加密得到密文,然后把密文通过网络传输给接收方。接收方在收到它们后,用RSA私钥对收到的密文进行解密,最后得到明文。图8是整个过程的实现流程。

图8 RSA算法实现流程


AES与RSA相结合数据加密方案

RSA算法是公开密钥系统的代表,其安全性建立在具有大素数因子的合数,其因子分解困难这一法则之上的。Rijndael算法作为新一代的高级加密标准,运行时不需要计算机有非常高的处理能力和大的内存,操作可以很容易的抵御时间和空间的攻击,在不同的运行环境下始终能保持良好的性能。这使AES将安全,高效,性能,方便,灵活性集于一体,理应成为网络数据加密的首选。相比较,因为AES密钥的长度最长只有256比特,可以利用软件和硬件实现高速处理,而RSA算法需要进行大整数的乘幂和求模等多倍字长处理,处理速度明显慢于AES[5];所以AES算法加解密处理效率明显高于RSA算法。在密钥管理方面,因为AES算法要求在通信前对密钥进行秘密分配,解密的私钥必须通过网络传送至加密数据接收方,而RSA采用公钥加密,私钥解密(或私钥加密,公钥解密),加解密过程中不必网络传输保密的密钥;所以RSA算法密钥管理要明显优于AES算法。

从上面比较得知,由于RSA加解密速度慢,不适合大量数据文件加密,因此在网络中完全用公开密码体制传输机密信息是没有必要,也是不太现实的。AES加密速度很快,但是在网络传输过程中如何安全管理AES密钥是保证AES加密安全的重要环节。这样在传送机密信息的双方,如果使用AES对称密码体制对传输数据加密,同时使用RSA不对称密码体制来传送AES的密钥,就可以综合发挥AES和RSA的优点同时避免它们缺点来实现一种新的数据加密方案。加解密实现流程如图(9)。

图9 AES与RSA相结合数据加密方案流程

具体过程是先由接收方创建RSA密钥对,接收方通过Internet发送RSA公钥到发送方,同时保存RSA私钥。而发送方创建AES密钥,并用该AES密钥加密待传送的明文数据,同时用接受的RSA公钥加密AES密钥,最后把用RSA公钥加密后的AES密钥同密文一起通过Internet传输发送到接收方。当接收方收到这个被加密的AES密钥和密文后,首先调用接收方保存的RSA私钥,并用该私钥解密加密的AES密钥,得到AES密钥。最后用该AES密钥解密密文得到明文。

Android端 AES+RSA结合实践

基本要求

保证传输数据的安全性 保证数据的完整性 能够验证客户端的身份

基本流程

Android端

1
2
3
4
5
6
7
<code><code> 1 . 服务器端(server)分别生成自己的RSA密钥对,并提供接口给Android客户端获取RSA公钥(rsaPublicKey)
2 . client生成AES密钥(aesKey)
3 . client使用自己的AES密钥(aesKey)对转换为json格式的请求明文数据(data)进行加密,得到加密后的请求数据encryptData
4 . client提供server提供的接口获取RSA公钥(rsaPublicKey)
5 . client使用获取RSA公钥(rsaPublicKey)对AES密钥(aesKey)进行加密,得到encryptAesKey
6 . client将encryptAesKey作为http请求头参数,将加密后的请求数据encryptData作为请求体一起传输给服务器端
</code></code>

服务器端

1
2
3
4
5
<code><code> 1 . server 响应client的http请求,读取http请求头。获得client传过来的加密后的AES密钥(encryptAesKey),读取http请求体,获得client传过来的加密后的请求数据(encryptData)。
  2 . server使用自己的RSA私钥(rsaPrivateKey)对加密后的AES密钥(encryptAesKey)进行RSA解密,得到AES密钥(aesKey)
  3 . 使用解密后的AES密钥(aesKey)对加密后的请求数据(encryptData),进行AES解密操作,得到解密后的请求数据(data),该数据为json格式
  4 . 对解密后的请求数据(data)进行json解析,然后做相关的响应操作。
</code></code>

基本上如下图所示的流程:


  • 16
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
以下是一个简单的Java Swing界面,用于演示如何使用AESRSA混合加密。 首先,请确保您已经安装了Java SDK和Eclipse IDE。接下来,按照以下步骤操作: 1. 在Eclipse创建一个新的Java项目。 2. 在项目创建一个名为“aesrsa”的包。 3. 在“aesrsa”包创建一个名为“Main.java”的类,该类将包含主方法和Swing用户界面。 以下是完整的代码: ```java import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.crypto.*; import java.security.*; public class Main { private JFrame frame; private JLabel messageLabel; private JTextField messageField; private JLabel aesLabel; private JTextField aesKeyField; private JLabel rsaLabel; private JTextField rsaKeyField; private JButton encryptButton; private JButton decryptButton; private JTextArea outputArea; private Cipher aesCipher; private Cipher rsaCipher; public Main() { initUI(); initCiphers(); } private void initUI() { frame = new JFrame("AES and RSA Hybrid Encryption"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500, 400); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(4, 2)); messageLabel = new JLabel("Message:"); messageField = new JTextField(); aesLabel = new JLabel("AES Key:"); aesKeyField = new JTextField(); rsaLabel = new JLabel("RSA Key:"); rsaKeyField = new JTextField(); panel.add(messageLabel); panel.add(messageField); panel.add(aesLabel); panel.add(aesKeyField); panel.add(rsaLabel); panel.add(rsaKeyField); encryptButton = new JButton("Encrypt"); encryptButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { encrypt(); } }); decryptButton = new JButton("Decrypt"); decryptButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { decrypt(); } }); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout()); buttonPanel.add(encryptButton); buttonPanel.add(decryptButton); outputArea = new JTextArea(); outputArea.setEditable(false); Container contentPane = frame.getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(panel, BorderLayout.NORTH); contentPane.add(buttonPanel, BorderLayout.CENTER); contentPane.add(outputArea, BorderLayout.SOUTH); frame.setVisible(true); } private void initCiphers() { try { aesCipher = Cipher.getInstance("AES"); KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES"); SecureRandom random = new SecureRandom(); aesKeyGen.init(random); SecretKey aesKey = aesKeyGen.generateKey(); aesKeyField.setText(bytesToHex(aesKey.getEncoded())); rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA"); rsaKeyGen.initialize(1024, random); KeyPair rsaKeyPair = rsaKeyGen.generateKeyPair(); rsaKeyField.setText(bytesToHex(rsaKeyPair.getPublic().getEncoded())); } catch (Exception e) { e.printStackTrace(); } } private void encrypt() { try { String message = messageField.getText(); byte[] aesKeyBytes = hexToBytes(aesKeyField.getText()); SecretKeySpec aesKeySpec = new SecretKeySpec(aesKeyBytes, "AES"); aesCipher.init(Cipher.ENCRYPT_MODE, aesKeySpec); byte[] encryptedMessage = aesCipher.doFinal(message.getBytes()); byte[] rsaKeyBytes = hexToBytes(rsaKeyField.getText()); X509EncodedKeySpec rsaKeySpec = new X509EncodedKeySpec(rsaKeyBytes); KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA"); PublicKey rsaPublicKey = rsaKeyFactory.generatePublic(rsaKeySpec); rsaCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey); byte[] encryptedAesKey = rsaCipher.doFinal(aesKeyBytes); outputArea.setText("Encrypted message:\n" + bytesToHex(encryptedMessage) + "\n\nEncrypted AES key:\n" + bytesToHex(encryptedAesKey)); } catch (Exception e) { e.printStackTrace(); } } private void decrypt() { try { byte[] encryptedMessage = hexToBytes(outputArea.getText().split("\n")[1]); byte[] encryptedAesKey = hexToBytes(outputArea.getText().split("\n")[4]); byte[] rsaKeyBytes = hexToBytes(rsaKeyField.getText()); PKCS8EncodedKeySpec rsaKeySpec = new PKCS8EncodedKeySpec(rsaKeyBytes); KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA"); PrivateKey rsaPrivateKey = rsaKeyFactory.generatePrivate(rsaKeySpec); rsaCipher.init(Cipher.DECRYPT_MODE, rsaPrivateKey); byte[] aesKeyBytes = rsaCipher.doFinal(encryptedAesKey); SecretKeySpec aesKeySpec = new SecretKeySpec(aesKeyBytes, "AES"); aesCipher.init(Cipher.DECRYPT_MODE, aesKeySpec); byte[] decryptedMessage = aesCipher.doFinal(encryptedMessage); outputArea.append("\n\nDecrypted message:\n" + new String(decryptedMessage)); } catch (Exception e) { e.printStackTrace(); } } private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X", b)); } return sb.toString(); } private static byte[] hexToBytes(String hex) { int len = hex.length(); byte[] bytes = new byte[len / 2]; for (int i = 0; i < len; i += 2) { bytes[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i+1), 16)); } return bytes; } public static void main(String[] args) { new Main(); } } ``` 在代码,我们使用了Java的加密API来实现AESRSA混合加密。我们还使用了Java的Swing库来创建用户界面。 在用户界面,我们有三个文本字段:消息、AES密钥和RSA密钥。我们还有两个按钮:“加密”和“解密”。当用户单击“加密”按钮时,我们将使用AES加密消息,并使用RSA加密AES密钥。当用户单击“解密”按钮时,我们将解密AES密钥,然后使用AES解密消息。 注意,在代码,我们使用了一些辅助方法来将字节数组转换为十六进制字符串,以及将十六进制字符串转换回字节数组。 您可以将此代码复制到Eclipse,并运行它以查看它的工作原理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值