有道无术,术尚可求,有术无道,止于术。
文章目录
前言
支付
和金钱挂钩,
支付安全
显得尤为重要,微信、支付宝等第三方支付公司,为了确保支付安全问题,都下足了不少功夫,其底层还是和
信息安全
密切挂钩,所以在这之前,我们需要了解各种安全机制,才能更好地了解支付流程和安全性问题。
信息安全
信息安全
是一个很广泛的概念,涉及计算机和网络系统的各个方面。从总体上来讲,信息安全有5个基本要素∶
- 机密性∶确保信息不暴露给未授权的实体或进程。
- 完整性∶只有得到允许的人才能够修改数据,并能够判别数据是否已被篡改。
- 可用性∶得到授权的实体在需要时可访问数据。
- 可控性∶可以控制授权范围内的信息流向和行为方式。
- 可审查性∶对出现的安全问题提供调查的依据和手段。
加密机制
数据加密
即对明文(未经加密的数据)按照某种加密算法(数据的变换算法)进行处理,而形成难以理解的密文(经加密后的数据)。即使密文被截获,截获方也无法或难以解码,从而防止泄露信息。
按照加密密钥和解密密钥的异同,有两种密钥体制,分别是
对称密码体制
和
非对称密码体制
。
核心概念
1、明文
加密前的消息叫
明文
(plain text)。比如小明给小红发送一封情书没有加密,只要被截取,那么会直接看到明文的内容信息。连篇的土味情话被看到,盗信者直呼辣眼睛😭😭😭
2、密文
加密后的文本叫
密文
(cipher text)。为了防止别人看到,小明对情书内容进行加密,加密后内容变成了不可读的内容,就算被窃取,盗信者也无法看懂其内容。
3、密钥
拥有
钥匙
的人,使用
钥匙
将明文变为密文,使用
钥匙
将密文解析为明文,这把
钥匙
叫做
秘钥
(key)。
4、加密
使用
秘钥
,将明文变为密文的过程叫做
加密
(encrypt)。
5、解密
使用
秘钥
,将密文还原为明文的过程叫做
解密
(decrypt)。
6、加密算法
加密算法是一种密码学算法,就是指将明文变成密文的加密技术。所有的加密算法都是公开的,而算法使用的
密钥
则必须保密。
对称加密
对称加密
加密和解密采用相同的密钥。因为只使用一个密钥,密钥必须保密,一旦被窃取,消息会被破解。其优点是运算速度快。
它要求发送方和接收方在安全通信之前,商定一个
密钥
。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。
工作流程:
- 小明使用某种加密算法生成一个秘钥,并将这个秘钥给小红。
- 小明使用秘钥将情书进行加密,变为密文,并发送给小红。
- 小红收到情书后,使用秘钥将密文还原为明文。
非对称加密
非对称加密
有
公钥
和
私钥
两个概念,私钥自己拥有,不能给别人,公钥公开。比对称加密安全,但是运算速度更慢。
工作流程:
- 小红使用某种加密算法生成一对密钥(公钥和私钥),并将这个公钥给小明。
- 得到公钥的小明使用该密钥对机密信息进行加密后再发送给小红。
- 小红用自己的专用私钥对加密后的信息进行解密。 在传输过程中,即使攻击者截获了传输的密文,并得到了公钥,也无法破解密文,因为只有小红的私钥才能解密密文。
JCE
JCE
是
Java Cryptography Extension
三个单词的缩写,译为Java 加密扩展,是JDK 1.4 提供的一个用于加密、密钥生成算法等功能的扩展包。
接下来我们使用
JCE
实现对称、非对称加解密。
对称加解密
1. 创建密钥
创建密钥需要创建一个
javax.crypto.KeyGenerator
密钥生成器对象,
KeyGenerator
提供对称密钥生成器的功能,使用此类的
getInstance
方法获取实例对象。
KeyGenerator
对象可重复使用,也就是说,在生成密钥后,可以重复使用同一个
KeyGenerator
对象来生成更多的密钥。
KeyGenerator.getInstance()
方法返回指定算法的密钥生成器对象,三个重载方法说明如下:
// 返回生成指定算法的秘密密钥的 KeyGenerator 对象。// algorithm:密钥算法的标准名称publicstaticfinalKeyGeneratorgetInstance(String algorithm)throwsNoSuchAlgorithmException// 返回生成指定算法的秘密密钥的 KeyGenerator 对象。// algorithm:密钥算法的标准名称// provider:提供者的名称publicstaticfinalKeyGeneratorgetInstance(String algorithm,String provider)throwsNoSuchAlgorithmException,NoSuchProviderException// 返回生成指定算法的秘密密钥的 KeyGenerator 对象。// algorithm:密钥算法的标准名称// provider:提供者对象publicstaticfinalKeyGeneratorgetInstance(String algorithm,Provider provider)throwsNoSuchAlgorithmException
该方法需要两个重要参数:算法名称、服务提供者。
加密服务提供者
简称
CSP
,
java.security.Provider
是所有安全服务提供程序的基类。在进行加解密运算时,需要指定一个
CSP
。
JDK
默认安装并配置了一个或多个提供程序,也可以注册第三方或自定义服务提供者(例如第三方加密机)。如果没有指定该参数,会从可用的提供者中选取第一个。
通过以下代码,可以获取当前
JDK
环境所有提供者:
Provider[] providers =Security.getProviders();for(Provider provider : providers){String name = provider.getName();double version = provider.getVersion();System.out.println(name +": "+ version);}// 输出结果SUN: 1.8SunRsaSign: 1.8SunEC: 1.8SunJSSE: 1.8SunJCE: 1.8SunJGSS: 1.8SunSASL: 1.8XMLDSig: 1.8SunPCSC: 1.8SunMSCAPI: 1.8
算法名称:密钥算法的标准名称。常用的对称算法有:
AES、3DES、SM1(国密JDK不支持)、SM4(国密)
等。
算法名称秘钥长度加密强度性能版权DES56弱快美国3DES168中慢美国IDEA128强中瑞士AES128 192 256强快美国SM1128强中国SM4128强中国
创建了秘钥生成器后,还需要调用
init
方法进行初始化。常用方法:
// 初始化此密钥生成器,使其具有确定的密钥大小。// 参数:keysize 密钥长度。这是特定于算法的一种规格,是以位数为单位指定的。publicfinalvoidinit(int keysize)
密钥长度
是一个重要的参数,每种算法都有其支持的长度,长度越长的情况下,暴力破解的时间就越长。长度单位为位
(bit)
。
生成对称密钥代码如下:
// 获取密钥生成器,指定算法为AESKeyGenerator keyGenerator =KeyGenerator.getInstance("AES");// 初始化
keyGenerator.init(128);// 生成公钥SecretKey secretKey = keyGenerator.generateKey();// 打印秘钥:LDDoCClZOvP9ZncFM19jZg==byte[] keyEncoded = secretKey.getEncoded();System.out.println(Base64.getEncoder().encodeToString(keyEncoded));
2. 加密
在上一步骤中,我们生成了一个
Base64
编码的秘钥字符串:
LDDoCClZOvP9ZncFM19jZg==
,接下来我们使用该秘钥进行加密。
加解密时,需要使用
JCE
中的
javax.crypto.Cipher
对象。此类为加密和解密提供密码功能。它构成了
Java Cryptographic Extension
(JCE) 框架的核心。
调用
getInstance
方法获取该对象实例,该方法需要一个字符串参数。
// 返回实现指定转换的 Cipher 对象。此方法从首选 Provider 开始遍历已注册安全提供者列表。// 参数:transformation - 转换的名称,例如 DES/CBC/PKCS5Padding。publicstaticfinalCiphergetInstance(String transformation)
transformation
翻译过来是转换的意思,可以理解为一个转换规则,由三部分组成
算法/加密模式/填充规则
。
算法就是标准的算法名称,之间我们已经介绍过了。
加密模式
主要有以下两种:
- **ECB(电码本模式)**,将明文分成若干小段, 然后对每小段进行加密
- **CBC(密码分组链接模式)**,先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再使用密钥进行加密
填充规则
:在分组密码中,当数据长度不符合分组长度时,需要按一定的方式,将尾部明文分组进行填充,这种将尾部分组数据填满的方法称为填充(Padding)。
填充规则
主要有以下几种:
- NOPADDING:即不填充,要求明文的长度,必须是加密算法分组长度的整数倍。
- ANSIX9.23:在填充字节序列中,最后一个字节填充为需要填充的字节长度,其余字节填充0。
- PKCS5PADDING:在填充字节序列中,每个字节填充为需要填充的字节长度。
以下列举了几种算法对应的转换名称:
算法转换名称AESAES/CBC/NoPaddingAESAES/CBC/PKCS5PaddingAESAES/ECB/NoPaddingAESAES/ECB/PKCS5PaddingDESDES/CBC/NoPaddingDESDES/CBC/PKCS5PaddingDESDES/ECB/NoPaddingDESDES/ECB/PKCS5Padding
通过以上知识,加密代码如下:
// 原文String message ="爱老虎油~";// Base64编码的秘钥字符串String keyBase64 ="LDDoCClZOvP9ZncFM19jZg==";// 获取Cipher 对象Cipher cipher =Cipher.getInstance("AES/ECB/PKCS5Padding");// 将秘钥字符串转为秘钥对象SecretKey keySpec =newSecretKeySpec(Base64.getDecoder().decode(keyBase64),"AES");// 初始化,设置为加密模式,并传入秘钥
cipher.init(Cipher.ENCRYPT_MODE, keySpec);// 加密运算byte[] cipherData = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));// 打印密文:SJ+6fnHAWt9ui7Pq3AlFmg==System.out.println("密文:"+Base64.getEncoder().encodeToString(cipherData));
3. 解密
解密
就比较简单了,使用秘钥,调用
Cipher
对象进行解密即可,代码如下:
// 解密// 密文String enBase64Message="SJ+6fnHAWt9ui7Pq3AlFmg==";// Base64编码的秘钥字符串String enKeyBase64 ="LDDoCClZOvP9ZncFM19jZg==";// 获取Cipher 对象Cipher deCipher =Cipher.getInstance("AES/ECB/PKCS5Padding");// 将秘钥字符串转为秘钥对象SecretKey deKeySpec =newSecretKeySpec(Base64.getDecoder().decode(enKeyBase64),"AES");// 初始化,设置解密模式,并传入秘钥
deCipher.init(Cipher.DECRYPT_MODE, deKeySpec);// 解密运算byte[] bytes = deCipher.doFinal(Base64.getDecoder().decode(enBase64Message));// 打印明文System.out.println("明文:"+newString(bytes));
执行结果如下:
密文:SJ+6fnHAWt9ui7Pq3AlFmg==
明文:爱老虎油~
非对称加解密
1. 创建密钥
使用
KeyPairGenerator.getInstance()
获取一个秘钥生成器,参数类型和对称秘钥一样,区别在于算法名称不一样。
常见的非对称加密算法有:
- RSA:性能比较快,如果想要较高的加密难度,需要很长的秘钥。
- ECC:基于椭圆曲线提出。是目前加密强度最高的非对称加密算法。
- SM2:同样基于椭圆曲线问题设计。最大优势就是国家认可和大力支持。
代码如下:
// 获取非对称密钥生成器KeyPairGenerator keyPairGenerator =KeyPairGenerator.getInstance("RSA");// 初始化
keyPairGenerator.initialize(512);// 生成秘钥对KeyPair keyPair = keyPairGenerator.generateKeyPair();// 获取内部密钥对PrivateKey privateKey = keyPair.getPrivate();// 私钥PublicKey publicKey = keyPair.getPublic();// 公钥// 打印秘钥:System.out.println("公钥:"+Base64.getEncoder().encodeToString(publicKey.getEncoded()));System.out.println("私钥:"+Base64.getEncoder().encodeToString(privateKey.getEncoded()));
执行结果:
公钥:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL8SAEW1o3lMg4XVQi29n7XqQ02ei0voeKcXvj+bVH1Fu+8W9CzSLpnlHHQgL9bl8Kly/5/y0TgE+NBxsrdfZo8CAwEAAQ==
私钥:MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvxIARbWjeUyDhdVCLb2ftepDTZ6LS+h4pxe+P5tUfUW77xb0LNIumeUcdCAv1uXwqXL/n/LROAT40HGyt19mjwIDAQABAkEAm4TsYdERaSbFGsbVIePpPimadHAqkzN6GQ8zVBR7NaaEeeFjZ/XX/FmNTvrKp2IXN5p0d0LZDOUPq3UL/nzpQQIhAP3JnMQBa9yeb3NzmCguaDwty8175sEDsB/NJLrULuehAiEAwLxrkZItOaoFRV6c78qPsHQGKSTQQBAFS2vycehW4C8CIQDH6wwM8zmunzgYcFTKQlRmI4VKJ3JNVcRmKMnoSsFwQQIgTob2lPMn9gyt5RuteY3giZZcRDs5lkBwx9ANkheF/gUCIAwBGYCzZHNsEr6PIfImrwxRLs/MFPbbQAHpUQaLaWL9
2. 公钥加密
使用公钥对原文数据进行加密。
// 使用公钥加密// 原文String message ="爱老虎油~";// Base64编码的公钥字符串String publicKeyBase64 ="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMEC2gOpKGTzCdJ4flb6Oj9QmbwVANmwLn0zBkZINepGEeWnvORxlteakSK03O2XgtHSGyW4r520KBtvKEcdFAECAwEAAQ==";// 获取Cipher 对象Cipher cipher =Cipher.getInstance("RSA/ECB/PKCS1Padding");// 将公钥字符串转为公钥对象X509EncodedKeySpec bobPubKeySpec =newX509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64));KeyFactory keyFactory =KeyFactory.getInstance("RSA");PublicKey publicKeyByValue = keyFactory.generatePublic(bobPubKeySpec);// 初始化,设置为加密模式,并传入公钥
cipher.init(Cipher.ENCRYPT_MODE, publicKeyByValue);// 加密运算byte[] cipherData = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));// 打印密文:mMzlg+65E1QKJi+zOVdyzbYrq526fOd7ir0Zlkbvo2q37iGvrMiq/y9P4teBtPe9PRJF7HCsmezZ3/M0aiQbyw==System.out.println("密文:"+Base64.getEncoder().encodeToString(cipherData));
3. 私钥解密
使用私钥对密文数据进行解密。
// 使用私钥解密// 密文String enBase64Message ="mMzlg+65E1QKJi+zOVdyzbYrq526fOd7ir0Zlkbvo2q37iGvrMiq/y9P4teBtPe9PRJF7HCsmezZ3/M0aiQbyw==";// Base64编码的私钥字符串String privateKeyBase64 ="MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAwQLaA6koZPMJ0nh+Vvo6P1CZvBUA2bAufTMGRkg16kYR5ae85HGW15qRIrTc7ZeC0dIbJbivnbQoG28oRx0UAQIDAQABAkEAmQgjx6dFadTxQrlaoqe/qxfC7MdSZ0czdP0RyoPSi64k942Rj46in5zDw60SynysVvMmVinxPPFS+6rv5kIWaQIhAPMXt8l7jPGmF6pYTmE2cx9OjB4Q9q8uhfIKZc0E7T9HAiEAy0JkNwSVxK76I9mFZpoRSfH37xAqLyUKhdCRAn5fBncCIQDGw3Pg6Ia750SeYgnkbrL+vCjRRKmPX4jh+SJ32jlqbQIgIXe6FpELtAn3qAV+AKnnpNxRraxktcSMmgIAjn+OV/sCIFlpFzkKXkW2cTv6oVHbFkqgrDPhTAXbshsa/U301Oac";// 获取Cipher 对象Cipher deCipher =Cipher.getInstance("RSA/ECB/PKCS1Padding");// 将私钥字符串转为私钥对象PKCS8EncodedKeySpec privateKeySpec =newPKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64));KeyFactory privateKeyFactory =KeyFactory.getInstance("RSA");PrivateKey privateKeyByValue = privateKeyFactory.generatePrivate(privateKeySpec);// 初始化,设置解密模式,并传入私钥
deCipher.init(Cipher.DECRYPT_MODE, privateKeyByValue);// 解密运算byte[] bytes = deCipher.doFinal(Base64.getDecoder().decode(enBase64Message));// 打印明文: 明文:爱老虎油~System.out.println("明文:"+newString(bytes));
版权归原作者 云烟成雨TD 所有, 如有侵权,请联系我们删除。