转:
Android安全加密专题文章索引
- Android安全加密:对称加密
- Android安全加密:非对称加密
- Android安全加密:消息摘要Message Digest
- Android安全加密:数字签名和数字证书
- Android安全加密:Https编程
以上学习所有内容,对称加密、非对称加密、消息摘要、数字签名等知识都是为了理解数字证书工作原理而作为一个预备知识。数字证书是密码学里的终极武器,是人类几千年历史总结的智慧的结晶,只有在明白了数字证书工作原理后,才能理解Https 协议的安全通讯机制。最终才能在SSL 开发过程中得心应手。
另外,对称加密和消息摘要这两个知识点是可以单独拿来使用的。
知识点串联:
数字证书使用到了以上学习的所有知识
- 对称加密与非对称加密结合使用实现了秘钥交换,之后通信双方使用该秘钥进行对称加密通信。
- 消息摘要与非对称加密实现了数字签名,根证书机构对目标证书进行签名,在校验的时候,根证书用公钥对其进行校验。若校验成功,则说明该证书是受信任的。
- Keytool 工具可以创建证书,之后交给根证书机构认证后直接使用自签名证书,还可以输出证书的RFC格式信息等。
- 数字签名技术实现了身份认证与数据完整性保证。
- 加密技术保证了数据的保密性,消息摘要算法保证了数据的完整性,对称加密的高效保证了数据处理的可靠性,数字签名技术保证了操作的不可否认性。
通过以上内容的学习,我们要能掌握以下知识点:
- 基础知识:bit 位、字节、字符、字符编码、进制转换、io
- 知道怎样在实际开发里怎样使用对称加密解决问题
- 知道对称加密、非对称加密、消息摘要、数字签名、数字证书是为了解决什么问题而出现的
- 了解SSL 通讯流程
- 实际开发里怎样请求Https 的接口
==================================================
一、数字签名
1. 概述
数字签名是非对称加密与数字摘要的组合应用
2. 应用场景
- 校验用户身份(使用私钥签名,公钥校验,只要用公钥能校验通过,则该信息一定是私钥持有者发布的)
- 校验数据的完整性(用解密后的消息摘要跟原文的消息摘要进行对比)
3. 签名过程
“发送报文时,发送方用一个哈希函数从报文文本中生成报文摘要,然后用自己的私人密钥对这个摘要进行加密,这个加密后的摘要将作为报文的数字签名和报文一起发送给接收方,接收方首先用与发送方一样的哈希函数从接收到的原始报文中计算出报文摘要,接着再用发送方的公用密钥来对报文附加的数字签名进行解密,如果这两个摘要相同、那么接收方就能确认该数字签名是发送方的。
数字签名有两种功效:一是能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。二是数字签名能确定消息的完整性。因为数字签名的特点是它代表了文件的特征,文件如果发生改变,数字摘要的值也将发生变化。不同的文件将得到不同的数字摘要。一次数字签名涉及到一个哈希函数、发送者的公钥、发送者的私钥。”
4. 使用步骤
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//获取signature 对象,初始化算法:MD2withRSA, MD5withRSA, or SHA1withRSA</span> Signature signature = Signature.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MD5withRSA"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建私钥(从磁盘上读取)</span> PrivateKey privateKey = (PrivateKey)SerializableUtil.readObject( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"heima.privateKey"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用私钥进行初始化</span> signature.initSign(privateKey); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//传入需要签名的数据</span> signature.update(content.getBytes()); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//执行签名</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] sign = signature.sign(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建公钥(从磁盘上读取)</span> PublicKey publicKey = (PublicKey) SerializableUtil.readObject( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"heima.publicKey"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用公钥进行初始化</span> signature.initVerify(publicKey); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//传入需要校验的数据(即上面的原文)</span> signature.update(content.getBytes()); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//执行校验</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> verify = signature.verify(sign);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li></ul>
5. 总结
数字签名一般不单独使用,基本都是用在数字证书里实现SSL 通信协议。下面将学习的数字证书就是基于数字签名技术实现的。
二、数字证书
1. 概述
数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet 上验证通信实体身份的方式,数字证书不是数字身份证,而是身份认证机构盖在数字身份证上的一个章或印(或者说加在数字身份证上的一个签名)。它是由权威机构——CA 机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的身份。
2. 应用场景
- 交易者身份的确定性、不可否认性、不可修改性
- 对应用进行签名认证(例如Android 的apk)
3. 数字证书格式
数字证书的格式普遍采用的是X.509V3 国际标准,一个标准的X.509 数字证书包含以下一些内容:
- 证书的版本信息;
- 证书的序列号,每个证书都有一个唯一的证书序列号;
- 证书所使用的签名算法;
- 证书的发行机构名称,命名规则一般采用X.500 格式;
- 证书的有效期,通用的证书一般采用UTC 时间格式,它的计时范围为1950-2049;
- 证书所有人的名称,命名规则一般采用X.500 格式;
- 证书所有人的公开密钥;
- 证书发行者对证书的签名。
4. 数字证书原理
数字证书是安全领域里的终极武器,SSL 通信协议里最核心的东西就是数字证书。他涉及到前面提到的所有知识:对称加密、非对称加密、消息摘要、数字签名等。
数字证书可以通过java 自带的KeyTool 工具生成,生成后的数字证书一般保管在KeyStore 里。KeyStore可以叫做秘钥仓库。
秘钥仓库可以保管3 种类型的数据:KeyStore.PrivateKeyEntry(非对称机密里的私钥)、KeyStore.SecretKeyEntry (对称加密里的秘钥)、KeyStore.TrustedCertificateEntry(受信任的证书)
5. KeyTool工具
路径:jre\bin\keytool.exe
常用命令:
生成keypair
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">keytool -genkeypair keytool -genkeypair -alias lisi(后面部分是为证书指定别名,否则采用默认的名称为mykey)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
看看keystore 中有哪些项目:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">keytool -list 或keytool -list -v keytool -exportcert -alias lisi -file lisi.cer</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
生成可打印的证书:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">keytool -exportcert -alias lisi -file lisi.cer –rfc</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
显示数字证书文件中的证书信息:
<code class="hljs lasso has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">keytool <span class="hljs-attribute" style="box-sizing: border-box;">-printcert</span> <span class="hljs-attribute" style="box-sizing: border-box;">-file</span> lisi<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>cer</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
直接双击lisi.cer,用window 系统的内置程序打开lisi.cer
6. Android 的keystore 相关知识
debug 签名路径:user.android\debug.keystore
debug.keystore 的别名(alias)及密码:
别名:androiddebugkey,密码:android
签名命令(jdk1.6):
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">jarsigner -verbose -keystore debug.keystore -signedjar <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>signed.apk <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.</span>apk androiddebugkey</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
签名命令(jdk1.7):
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">jarsigner -verbose -keystore debug.keystore -signedjar <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>signed.apk <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.</span>apk androiddebugkey -digestalg SHA1 -sigalg MD5withRSA</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
优化命令:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">zipalign -v <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>signed<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.apk</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>signedaligned<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.apk</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
验证签名是否成功:
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">jarsigner -verify <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">signed</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.apk</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
7. 补充
签名证书:
由权威颁发机构颁发给服务器或者个人用于证明自己身份的东西,默认客户端都是信任的。主要目的是用来加密和保证数据的完整性和不可抵赖性
例如根证书机构Symantec 颁发给百度的就是签名证书,是受信任的。
自签名证书:
由服务器自己颁发给自己,用于证明自己身份的东西,非权威颁发机构发布,默认客户端都是不信任的,主要目的是用来加密和保证数据的完整性和不可抵赖性,与签名证书相同.
例如中铁集团(SRCA)办法给12306 的证书就是自签名证书,自己给自己颁发的。
==================================================
一、凯撒密码
1. 概述
凯撒密码作为一种最为古老的对称加密体制,在古罗马的时候都已经很流行,他的基本思想是:通过把字母移动一定的位数来实现加密和解密。明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是3 的时候,所有的字母A 将被替换成D,B 变成E,由此可见,位数就是凯撒密码加密和解密的密钥。
例如:字符串”ABC”的每个字符都右移3 位则变成”DEF”,解密的时候”DEF”的每个字符左移3 位即能还原,如下图所示:
2. 准备知识
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//字符转换成ASCII 码数值</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> charA = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'a'</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> intA = charA; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//char 强转为int 即得到对应的ASCII 码值,’a’的值为97</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//ASCII 码值转成char</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> intA = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">97</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//97 对应的ASCII 码’a’</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> charA = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>) intA; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//int 值强转为char 即得到对应的ASCII 字符,即'a'</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>
3. 凯撒密码的简单代码实现
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 加密 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> input 数据源(需要加密的数据) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> key 秘钥,即偏移量 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span> 返回加密后的数据 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> String <span class="hljs-title" style="box-sizing: border-box;">encrypt</span>(String input, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> key) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到字符串里的每一个字符</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>[] array = input.toCharArray(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < array.length; ++i) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//字符转换成ASCII 码值</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> ascii = array[i]; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//字符偏移,例如a->b</span> ascii = ascii + key; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//ASCII 码值转换为char</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> newChar = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>) ascii; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//替换原有字符</span> array[i] = newChar; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//以上4 行代码可以简写为一行</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//array[i] = (char) (array[i] + key);</span> } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//字符数组转换成String</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> String(array); } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 解密 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> input 数据源(被加密后的数据) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> key 秘钥,即偏移量 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span> 返回解密后的数据 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> String <span class="hljs-title" style="box-sizing: border-box;">decrypt</span>(String input, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> key) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到字符串里的每一个字符</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>[] array = input.toCharArray(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < array.length; ++i) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//字符转换成ASCII 码值</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> ascii = array[i]; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//恢复字符偏移,例如b->a</span> ascii = ascii - key; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//ASCII 码值转换为char</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> newChar = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>) ascii; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//替换原有字符</span> array[i] = newChar; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//以上4 行代码可以简写为一行</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//array[i] = (char) (array[i] - key);</span> } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//字符数组转换成String</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> String(array); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li></ul>
代码输出结果:
4. 破解凯撒密码:频率分析法
凯撒密码加密强度太低,只需要用频度分析法即可破解。
在任何一种书面语言中,不同的字母或字母组合出现的频率各不相同。而且,对于以这种语言书写的任意一段文本,都具有大致相同的特征字母分布。比如,在英语中,字母E 出现的频率很高,而X 则出现得较少。
英语文本中典型的字母分布情况如下图所示:
5. 破解流程
- 统计密文里出现次数最多的字符,例如出现次数最多的字符是是’h’。
- 计算字符’h’到’e’的偏移量,值为3,则表示原文偏移了3 个位置。
- 将密文所有字符恢复偏移3 个位置。
注意点:统计密文里出现次数最多的字符时,需多统计几个备选,因为最多的可能是空格或者其他字符,例如下图出现次数最多的字符’#’是空格加密后的字符,’h’才是’e’偏移后的值。
解密时要多几次尝试,因为不一定出现次数最多的字符就是我们想要的目标字符,如下图,第二次解密的结果才是正确的。
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 频率分析法破解凯撒密码 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">FrequencyAnalysis</span> {</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//英文里出现次数最多的字符</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> MAGIC_CHAR = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'e'</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//破解生成的最大文件数</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> DE_MAX_FILE = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> Exception { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//测试1,统计字符个数</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//printCharCount("article1_en.txt");</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//加密文件</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//int key = 3;</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//encryptFile("article1.txt", "article1_en.txt", key);</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//读取加密后的文件</span> String artile = file2String(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"article1_en.txt"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//解密(会生成多个备选文件)</span> decryptCaesarCode(artile, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"article1_de.txt"</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">printCharCount</span>(String path) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException{ String data = file2String(path); List<Entry<Character, Integer>> mapList = getMaxCountChar(data); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<Character, Integer> entry : mapList) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//输出前几位的统计信息</span> System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"字符'"</span> + entry.getKey() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"'出现"</span> + entry.getValue() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"次"</span>); } } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">encryptFile</span>(String srcFile, String destFile, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> key) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException { String artile = file2String(srcFile); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//加密文件</span> String encryptData = MyEncrypt.encrypt(artile, key); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//保存加密后的文件</span> string2File(encryptData, destFile); } <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 破解凯撒密码 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> input 数据源 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span> 返回解密后的数据 */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">decryptCaesarCode</span>(String input, String destPath) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> deCount = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//当前解密生成的备选文件数</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//获取出现频率最高的字符信息(出现次数越多越靠前)</span> List<Entry<Character, Integer>> mapList = getMaxCountChar(input); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<Character, Integer> entry : mapList) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//限制解密文件备选数</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (deCount >= DE_MAX_FILE) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//输出前几位的统计信息</span> System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"字符'"</span> + entry.getKey() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"'出现"</span> + entry.getValue() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"次"</span>); ++deCount; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//出现次数最高的字符跟MAGIC_CHAR的偏移量即为秘钥</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> key = entry.getKey() - MAGIC_CHAR; System.out.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"猜测key = "</span> + key + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">", 解密生成第"</span> + deCount + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"个备选文件"</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\n"</span>); String decrypt = MyEncrypt.decrypt(input, key); String fileName = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"de_"</span> + deCount + destPath; string2File(decrypt, fileName); } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//统计String里出现最多的字符</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> List<Entry<Character, Integer>> <span class="hljs-title" style="box-sizing: border-box;">getMaxCountChar</span>(String data) { Map<Character, Integer> map = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HashMap<Character, Integer>(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>[] array = data.toCharArray(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> c : array) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(!map.containsKey(c)) { map.put(c, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{ Integer count = map.get(c); map.put(c, count + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>); } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//输出统计信息</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*for (Entry<Character, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + "出现" + entry.getValue() + "次"); }*/</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//获取获取最大值</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> maxCount = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (Entry<Character, Integer> entry : map.entrySet()) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//不统计空格</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*entry.getKey() != ' ' && */</span>entry.getValue() > maxCount) { maxCount = entry.getValue(); } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//map转换成list便于排序</span> List<Entry<Character, Integer>> mapList = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ArrayList<Map.Entry<Character,Integer>>(map.entrySet()); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//根据字符出现次数排序</span> Collections.sort(mapList, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Comparator<Entry<Character, Integer>>(){ <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">compare</span>(Entry<Character, Integer> o1, Entry<Character, Integer> o2) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> o2.getValue().compareTo(o1.getValue()); } }); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mapList; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> String <span class="hljs-title" style="box-sizing: border-box;">file2String</span>(String path) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException { FileReader reader = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileReader(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(path)); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>[] buffer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1024</span>]; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> len = -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; StringBuffer sb = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> StringBuffer(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> ((len = reader.read(buffer)) != -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) { sb.append(buffer, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, len); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> sb.toString(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">string2File</span>(String data, String path){ FileWriter writer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { writer = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileWriter(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(path)); writer.write(data); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) { e.printStackTrace(); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">finally</span> { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (writer != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> { writer.close(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (IOException e) { e.printStackTrace(); } } } } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li><li style="box-sizing: border-box; padding: 0px 5px;">94</li><li style="box-sizing: border-box; padding: 0px 5px;">95</li><li style="box-sizing: border-box; padding: 0px 5px;">96</li><li style="box-sizing: border-box; padding: 0px 5px;">97</li><li style="box-sizing: border-box; padding: 0px 5px;">98</li><li style="box-sizing: border-box; padding: 0px 5px;">99</li><li style="box-sizing: border-box; padding: 0px 5px;">100</li><li style="box-sizing: border-box; padding: 0px 5px;">101</li><li style="box-sizing: border-box; padding: 0px 5px;">102</li><li style="box-sizing: border-box; padding: 0px 5px;">103</li><li style="box-sizing: border-box; padding: 0px 5px;">104</li><li style="box-sizing: border-box; padding: 0px 5px;">105</li><li style="box-sizing: border-box; padding: 0px 5px;">106</li><li style="box-sizing: border-box; padding: 0px 5px;">107</li><li style="box-sizing: border-box; padding: 0px 5px;">108</li><li style="box-sizing: border-box; padding: 0px 5px;">109</li><li style="box-sizing: border-box; padding: 0px 5px;">110</li><li style="box-sizing: border-box; padding: 0px 5px;">111</li><li style="box-sizing: border-box; padding: 0px 5px;">112</li><li style="box-sizing: border-box; padding: 0px 5px;">113</li><li style="box-sizing: border-box; padding: 0px 5px;">114</li><li style="box-sizing: border-box; padding: 0px 5px;">115</li><li style="box-sizing: border-box; padding: 0px 5px;">116</li><li style="box-sizing: border-box; padding: 0px 5px;">117</li><li style="box-sizing: border-box; padding: 0px 5px;">118</li><li style="box-sizing: border-box; padding: 0px 5px;">119</li><li style="box-sizing: border-box; padding: 0px 5px;">120</li><li style="box-sizing: border-box; padding: 0px 5px;">121</li><li style="box-sizing: border-box; padding: 0px 5px;">122</li><li style="box-sizing: border-box; padding: 0px 5px;">123</li><li style="box-sizing: border-box; padding: 0px 5px;">124</li><li style="box-sizing: border-box; padding: 0px 5px;">125</li><li style="box-sizing: border-box; padding: 0px 5px;">126</li><li style="box-sizing: border-box; padding: 0px 5px;">127</li><li style="box-sizing: border-box; padding: 0px 5px;">128</li><li style="box-sizing: border-box; padding: 0px 5px;">129</li><li style="box-sizing: border-box; padding: 0px 5px;">130</li><li style="box-sizing: border-box; padding: 0px 5px;">131</li><li style="box-sizing: border-box; padding: 0px 5px;">132</li><li style="box-sizing: border-box; padding: 0px 5px;">133</li><li style="box-sizing: border-box; padding: 0px 5px;">134</li><li style="box-sizing: border-box; padding: 0px 5px;">135</li><li style="box-sizing: border-box; padding: 0px 5px;">136</li><li style="box-sizing: border-box; padding: 0px 5px;">137</li><li style="box-sizing: border-box; padding: 0px 5px;">138</li><li style="box-sizing: border-box; padding: 0px 5px;">139</li></ul>
二、对称加密
1、概述
加密和解密都使用同一把秘钥,这种加密方法称为对称加密,也称为单密钥加密。
简单理解为:加密解密都是同一把钥匙。
凯撒密码就属于对称加密,他的字符偏移量即为秘钥。
2、对称加密常用算法
AES、DES、3DES、TDEA、Blowfish、RC2、RC4、RC5、IDEA、SKIPJACK 等。
DES:全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1976 年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。
3DES:也叫Triple DES,是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。
它相当于是对每个数据块应用三次DES 加密算法。由于计算机运算能力的增强,原版DES 密码的密钥长度变得容易被暴力破解;3DES 即是设计用来提供一种相对简单的方法,即通过增加DES 的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
AES: 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael 加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001 年11 月26 日发布于FIPS PUB 197,并在2002 年5 月26 日成为有效的标准。2006 年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
3、DES 算法简介
DES 加密原理(对比特位进行操作,交换位置,异或等等,无需详细了解)
准备知识
Bit 是计算机最小的传输单位。以0 或1 来表示比特位的值
例如数字3 对应的二进制数据为:00000011
代码示例
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">97</span>; String bit = Integer.toBinaryString(i); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//输出:97 对应的二进制数据为: 1100001</span> System.out.println(i + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"对应的二进制数据为: "</span> + bit);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
Byte 与Bit 区别
数据存储是以“字节”(Byte)为单位,数据传输是以大多是以“位”(bit,又名“比特”)为单位,一个位就代表一个0 或1(即二进制),每8 个位(bit,简写为b)组成一个字节(Byte,简写为B),是最小一级的信息单位。
Byte 的取值范围:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//byte 的取值范围:-128 到127</span> System.out.println(Byte.MIN_VALUE + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"到"</span> + Byte.MAX_VALUE);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
即10000000 到01111111 之间,一个字节占8 个比特位
二进制转十进制图示:
任何字符串都可以转换为字节数组
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">String data = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"1234abcd"</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] bytes = data.getBytes();<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//内容为:49 50 51 52 97 98 99 100</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
上面数据49 50 51 52 97 98 99 100 对应的二进制数据(即比特位为):
00110001
00110010
00110011
00110100
01100001
01100010
01100011
01100100
将他们间距调大一点,可看做一个矩阵:
之后可对他们进行各种操作,例如交换位置、分割、异或运算等,常见的加密方式就是这样操作比特位的,例如下图的IP 置换以及S-Box 操作都是常见加密的一些方式:
IP 置换:
S-BOX 置换:
DES 加密过程图解(流程很复杂,只需要知道内部是操作比特位即可):
对称加密应用场景
- 本地数据加密(例如加密android 里SharedPreferences 里面的某些敏感数据)
- 网络传输:登录接口post 请求参数加密{username=lisi,pwd=oJYa4i9VASRoxVLh75wPCg==}
- 加密用户登录结果信息并序列化到本地磁盘(将user 对象序列化到本地磁盘,下次登录时反序列化到内存里)
- 网页交互数据加密(即后面学到的Https)
DES 算法代码实现
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//1,得到cipher 对象(可翻译为密码器或密码系统)</span> Cipher cipher = Cipher.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DES"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//2,创建秘钥</span> SecretKey key = KeyGenerator.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DES"</span>).generateKey(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//3,设置操作模式(加密/解密)</span> cipher.init(Cipher.ENCRYPT_MODE, key); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//4,执行操作</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] result = cipher.doFinal(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"黑马"</span>.getBytes());</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>
AES 算法代码实现
用法同上,只需把”DES”参数换成”AES”即可。
使用Base64 编码加密后的结果
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] result = cipher.doFinal(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"黑马"</span>.getBytes()); System.out.println(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> String(result));</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
输出结果:
加密后的结果是字节数组,这些被加密后的字节在码表(例如UTF-8 码表)上找不到对应字符,会出现乱码,当乱码字符串再次转换为字节数组时,长度会变化,导致解密失败,所以转换后的数据是不安全的。
使用Base64 对字节数组进行编码,任何字节都能映射成对应的Base64 字符,之后能恢复到字节数组,利于加密后数据的保存于传输,所以转换是安全的。同样,字节数组转换成16 进制字符串也是安全的。
密文转换成Base64 编码后的输出结果:
密文转换成16 进制编码后的输出结果:
Java 里没有直接提供Base64 以及字节数组转16 进制的Api,开发中一般是自己手写或直接使用第三方提供的成熟稳定的工具类(例如apache 的commons-codec)。
Base64 字符映射表
对称加密的具体应用方式
1. 生成秘钥并保存到硬盘上,以后读取该秘钥进行加密解密操作,实际开发中用得比较少
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//生成随机秘钥</span> SecretKey secretKey = KeyGenerator.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"AES"</span>).generateKey(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//序列化秘钥到磁盘上</span> FileOutputStream fos = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileOutputStream(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"heima.key"</span>)); ObjectOutputStream oos = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ObjectOutputStream(fos); oos.writeObject(secretKey); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//从磁盘里读取秘钥</span> FileInputStream fis = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileInputStream(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"heima.key"</span>)); ObjectInputStream ois = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ObjectInputStream(fis); Key key = (Key) ois.readObject();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>
2. 使用自定义秘钥(秘钥写在代码里)
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建密钥写法1</span> KeySpec keySpec = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DESKeySpec(key.getBytes()); SecretKey secretKey = SecretKeyFactory.getInstance(ALGORITHM). generateSecret(keySpec); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建密钥写法2</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//SecretKey secretKey = new SecretKeySpec(key.getBytes(), KEY_ALGORITHM);</span> Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到key 后,后续代码就是Cipher 的写法,此处省略...</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>
注意事项
把秘钥写在代码里有一定风险,当别人反编译代码的时候,可能会看到秘钥,android 开发里建议用JNI 把秘钥值写到C 代码里,甚至拆分成几份,最后再组合成真正的秘钥
算法/工作模式/填充模式
初始化cipher 对象时,参数可以直接传算法名:例如:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">Cipher c = Cipher.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DES"</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
也可以指定更详细的参数,格式:”algorithm/mode/padding” ,即”算法/工作模式/填充模式”
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">Cipher c = Cipher.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DES/CBC/PKCS5Padding"</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
密码块工作模式
块密码工作模式(Block cipher mode of operation),是对于按块处理密码的加密方式的一种扩充,不仅仅适用于AES,包括DES, RSA 等加密方法同样适用。
填充模式
填充(Padding),是对需要按块处理的数据,当数据长度不符合块处理需求时,按照一定方法填充满块长的一种规则。
具体代码:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//秘钥算法</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String KEY_ALGORITHM = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DES"</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//加密算法:algorithm/mode/padding 算法/工作模式/填充模式</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String CIPHER_ALGORITHM = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"DES/ECB/PKCS5Padding"</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//秘钥</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String KEY = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"12345678"</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//DES 秘钥长度必须是8 位或以上</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//private static final String KEY = "1234567890123456";//AES 秘钥长度必须是16 位</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//初始化秘钥</span> SecretKey secretKey = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> SecretKeySpec(KEY.getBytes(), KEY_ALGORITHM); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//加密</span> cipher.init(Cipher.ENCRYPT_MODE, secretKey); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] result = cipher.doFinal(input.getBytes());</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>
注意:AES、DES 在CBC 操作模式下需要iv 参数
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//AES、DES 在CBC 操作模式下需要iv 参数</span> IvParameterSpec iv = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> IvParameterSpec(key.getBytes()); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//加密</span> cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
三、总结
DES 安全度在现代已经不够高,后来又出现的3DES 算法强度提高了很多,但是其执行效率低下,AES算法加密强度大,执行效率高,使用简单,实际开发中建议选择AES 算法。实际android 开发中可以用对称加密(例如选择AES 算法)来解决很多问题,例如:
- 做一个管理密码的app,我们在不同的网站里使用不同账号密码,很难记住,想做个app 统一管理,但是账号密码保存在手机里,一旦丢失了容易造成安全隐患,所以需要一种加密算法,将账号密码信息加密起来保管,这时候如果使用对称加密算法,将数据进行加密,秘钥我们自己记在心里,只需要记住一个密码。需要的时候可以还原信息。
- android 里需要把一些敏感数据保存到SharedPrefrence 里的时候,也可以使用对称加密,这样可以在需要的时候还原。
- 请求网络接口的时候,我们需要上传一些敏感数据,同样也可以使用对称加密,服务端使用同样的算法就可以解密。或者服务端需要给客户端传递数据,同样也可以先加密,然后客户端使用同样算法解密。
==================================================
1. 介绍
与对称加密算法不同,非对称加密算法需要两个密钥:公钥(publickey)和私钥(privatekey)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密;如果用私钥对数据进行加密,那么只有用对应的公钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
简单理解为:加密和解密是不同的钥匙
2. 常见算法
RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等,其中支付宝使用的就是RSA算法
3. RSA 算法原理
质因数、欧拉函数、模反元素
原理很复杂,只需要知道内部是基于分解质因数和取模操作即可
4. 使用步骤
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//1,获取cipher 对象</span> Cipher cipher = Cipher.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"RSA"</span>); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//2,通过秘钥对生成器KeyPairGenerator 生成公钥和私钥</span> KeyPair keyPair = KeyPairGenerator.getInstance(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"RSA"</span>).generateKeyPair(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用公钥进行加密,私钥进行解密(也可以反过来使用)</span> PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//3,使用公钥初始化密码器</span> cipher.init(Cipher.ENCRYPT_MODE, publicKey); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//4,执行加密操作</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] result = cipher.doFinal(content.getBytes()); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用私钥初始化密码器</span> cipher.init(Cipher.DECRYPT_MODE, privateKey); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//执行解密操作</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] deResult = cipher.doFinal(result);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>
5. 注意点
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//一次性加密数据的长度不能大于117 字节</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> ENCRYPT_BLOCK_MAX = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">117</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//一次性解密的数据长度不能大于128 字节</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> DECRYPT_BLOCK_MAX = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">128</span>;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
6. 分批操作
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * 分批操作 * *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> content 需要处理的数据 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> cipher 密码器(根据cipher 的不同,操作可能是加密或解密) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> blockSize 每次操作的块大小,单位为字节 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span> 返回处理完成后的结果 *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @throws</span> Exception */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] <span class="hljs-title" style="box-sizing: border-box;">doFinalWithBatch</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] content, Cipher cipher, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> blockSize) throwseption { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> offset = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//操作的起始偏移位置</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> len = content.length;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//数据总长度</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] tmp;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//临时保存操作结果</span> ByteArrayOutputStream baos = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ByteArrayOutputStream(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果剩下数据</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (len - offset > <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (len - offset >= blockSize) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//剩下数据还大于等于一个blockSize</span> tmp = cipher.doFinal(content, offset, blockSize); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//剩下数据不足一个blockSize</span> tmp = cipher.doFinal(content, offset, len - offset); } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将临时结果保存到内存缓冲区里</span> baos.write(tmp); offset = offset + blockSize; } baos.close(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> baos.toByteArray(); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li></ul>
7. 非对称加密用途
身份认证
一条加密信息若能用A 的公钥能解开,则该信息一定是用A 的私钥加密的,该能确定该用户是A。
陌生人通信
A 和B 两个人互不认识,A 把自己的公钥发给B,B 也把自己的公钥发给A,则双方可以通过对方的公钥加密信息通信。C 虽然也能得到A、B 的公钥,但是他解不开密文。
秘钥交换
A 先得到B 的公钥,然后A 生成一个随机秘钥,例如13245768,之后A 用B 的公钥加密该秘钥,得到加密后的秘钥,例如dxs#fd@dk,之后将该密文发给B,B 用自己的私钥解密得到123456,之后双方使用13245768 作为对称加密的秘钥通信。C 就算截获加密后的秘钥dxs#fd@dk,自己也解不开,这样A、B 二人能通过对称加密进行通信。
8. 总结
非对称加密一般不会单独拿来使用,他并不是为了取代对称加密而出现的,非对称加密速度比对称加密慢很多,极端情况下会慢1000 倍,所以一般不会用来加密大量数据,通常我们经常会将对称加密和非对称加密两种技术联合起来使用,例如用非对称加密来给称加密里的秘钥进行加密(即秘钥交换)。