精华内容
下载资源
问答
  • 加签验签
    2022-03-15 06:08:51

    1、加密

    对称加密:加密和解密使用同一把秘钥

    缺点:不安全,一旦一方秘钥泄露,密文就会被自由解析

    非对称加密: 加密解密使用不同钥匙,四把钥匙

    2、公钥、私钥、加密、签名、验签

    1)公钥、私钥

    公钥和私钥是一个相对概念 它们的公私性是相对于生成者来说的。 一对密钥生成后,保存在生成者手里的就是私钥, 生成者发布出去大家用的就是公钥

    对于上述过程来说,密钥A就是私钥,密钥B就是公钥

    2)加密和数字签名

    加密: 我们使用一对公私钥中的一个密钥来对数据进行加密,而使用另一个密钥来进行解密的技术。公钥和私钥都可以用来加密,也都可以用来解密。但这个加解密必须是一对密钥之间的互相加解密,否则不能成功。

    加密的目的是: 为了确保数据传输过程中的不可读性,就是不想让别人看到。

    签名:给我们将要发送的数据,做上一个唯一签名

    验签:对发送者发送过来的签名进行验证

    更多相关内容
  • SHA256withRSA java常用的json加签验签工具类 SHA256withRSA 完美工具类 RSAUtils,包含: 1、签名 2、验签 3、公钥加密》私钥解密 4、私钥加密》公钥解密
  • 下面小编就为大家带来一篇php对接java现实加签验签的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • spring框架中关于RSA sha-256加密解密,加签验签工具类
  • 国密SM2_SM3加密解密,加签验签操作C#源码,网络上很难找的资源,实现了密SM2_SM3加密解密,加签验签操作,.NET版
  • * RSA数字签名-俗称加签验签:私钥加签,公钥验签。  * RSA加密解密:私钥解密,公钥加密。 * RSA数字签名-俗称加签验签:私钥加签,公钥验签。  * RSA加密解密:私钥解密,公钥加密。 * RSA数字签名-俗称...
  • 主要介绍了Python3.7基于hashlib和Crypto实现加签验签功能,环境是基于python3.7,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
  • NULL 博文链接:https://bighappy1983.iteye.com/blog/1850818
  • 临商银行接口对接,加密解密加签验签整合,方便调用 临商银行接口对接,加密解密加签验签整合,方便调用 临商银行接口对接,加密解密加签验签整合,方便调用 临商银行接口对接,加密解密加签验签整合,方便调用 临商...
  • 加签验签概念 为什么需要加签、验签 加密算法简介 加签验签相关API 加签验签代码实现 公众号:捡田螺的小男孩 本文已经收录到个人github,文章有用的话,可以给个star呀: github.com/whx123/Java… 密码...

    前言

    我们在求职面试中,经常会被问到,如何设计一个安全对外的接口呢? 其实可以回答这一点,加签和验签,这将让你的接口更加有安全。接下来,本文将和大家一起来学习加签和验签。从理论到实战,加油哦~

    • 密码学相关概念
    • 加签验签概念
    • 为什么需要加签、验签
    • 加密算法简介
    • 加签验签相关API
    • 加签验签代码实现
    • 公众号:捡田螺的小男孩

    本文已经收录到个人github,文章有用的话,可以给个star呀:

    github.com/whx123/Java…

    密码学相关概念

    明文、密文、密钥、加密、解密

    • 明文:指没有经过加密的信息/数据。
    • 密文:明文被加密算法加密之后,会变成密文,以确保数据安全。
    • 密钥:是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥。
    • 加密:将明文变成密文的过程。
    • 解密:将密文还原为明文的过程。

    对称加密、非对称加密

    • 对称加密:加密和解密使用相同密钥的加密算法。

    • 非对称加密:非对称加密算法需要两个密钥(公开密钥和私有密钥)。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有对应的私钥才能解密。

    什么是公钥私钥?

    • 公钥与私钥是成对存在的密钥,如果用公钥对数据进行加密,只有用对应的私钥才能解密。
    • 其实,公钥就是公开的秘钥,私钥就是要你私自保存好的秘钥。
    • 非对称加密算法需要有一对公私钥~

    假设你有一个文件,你用字母a加密,只有字母b才能解密;或者你用b加密,只有a才能解密,那么a和b就是一对公私钥。如果密钥a公开,密钥b你就要私自保存好啦,这时候密钥a就是公钥,密钥b就是私钥。相反,如果b公开,a就要保存好,这时候呢,秘钥b就是公钥,秘钥a就是私钥。

    加签验签概念

    • 「加签」:用Hash函数把原始报文生成报文摘要,然后用私钥对这个摘要进行加密,就得到这个报文对应的数字签名。「注意啦,加签过程要包含一些特殊的私有的东西,比如个人私钥。」 通常来说呢,请求方会把「数字签名和报文原文」一并发送给接收方。

    • 「验签」:接收方拿到原始报文和数字签名后,用「同一个Hash函数」从报文中生成摘要A。另外,用对方提供的公钥对数字签名进行解密,得到摘要B,对比A和B是否相同,就可以得知报文有没有被篡改过。

    互联网网上的这个图,更容易理解一点:

     

    为什么需要加签验签

    上小节中,加签和验签我们已经知道概念啦,那么,为什么需要加签和验签呢?有些朋友可能觉得,我们不是用「公钥加密,私钥解密」就好了嘛?

    接下来呢,举个demo吧。

    假设现在有A公司,要接入C公司的转账系统。在一开始呢,C公司把自己的公钥寄给A公司,自己收藏好私钥。A公司这边的商户,发起转账时,A公司先用C公司的公钥,对请求报文加密,加密报文到达C公司的转账系统时,C公司就用自己的私钥把报文揭开。假设在加密的报文在传输过程中,被中间人Actor获取了,他也郁闷,因为他没有私钥,看着天鹅肉,又吃不了。本来想修改报文,给自己账号转一个亿的,哈哈。这个实现方式看起来是天衣无缝,稳得一匹的。

    但是呢,如果一开始,C公司把公钥发给公司A的时候,就被中间人Actor获取到呢,酱紫就出问题了。

    中间人Actor截取了C的公钥,他把自己的公钥发给了A公司,A误以为这就是C公司的公钥。A在发起转账时,用Actor的公钥,对请求报文加密,加密报文到在传输过程,Actor又截取了,这时候,他用自己的私钥解密,然后修改了报文(给自己转一个亿),再用C的公钥加密,发给C公司,C公司收到报文后,继续用自己的私钥解密。最后是不是A公司的转账账户损失了一个亿呢~

    C公司是怎么区分报文是不是来自A呢,还是被中间人修改过呢?为了表明身份和报文真实性,这就需要「加签验签」啦!

    A公司把自己的公钥也发送给C公司,私钥自己保留着。在发起转账时,先用自己的私钥对请求报文加签,于是得到自己的数字签名。再把数字签名和请求报文一起发送给C公司。C公司收到报文后,拿A的公钥进行验签,如果原始报文和数字签名的摘要内容不一致,那就是报文被篡改啦~

    有些朋友可能有疑问,假设A在发自己的公钥给C公司的时候,也被中间人Actor截取了呢。嗯嗯,我们来模拟一波Actor又截取了公钥,看看Actor能干出什么事情来~哈哈

    假设Actor截取到A的公钥后,随后也截取了到A发往C的报文。他截取到报文后,第一件想做的事肯定是修改报文内容。但是如果单单修改原始报文是不可以的,因为发过去C公司肯定验签不过啦。但是呢,数字签名似乎解不开,因为消息摘要算法(hash算法)无法逆向解开的,只起验证的作用呢....

    所以呢,公钥与私钥是用来加密与加密的,「加签与验签是用来证明身份」,以免被篡改的。

    常见加密相关算法简介

    • 消息摘要算法
    • 对称加密算法
    • 非对称加密算法
    • 国密算法

    消息摘要算法:

    • 相同的明文数据经过相同的消息摘要算法会得到相同的密文结果值。
    • 数据经过消息摘要算法处理,得到的摘要结果值,是无法还原为处理前的数据的。
    • 数据摘要算法也被称为哈希(Hash)算法或散列算法。
    • 消息摘要算法一般用于签名验签。

    消息摘要算法主要分三类:MD(Message Digest,消息摘要算法)、SHA(Secure Hash Algorithm,安全散列算法)和MAC(Message Authentication Code,消息认证码算法)。

    MD家族算法

    MD(Message Digest,消息摘要算法)家族,包括MD2,MD4,MD5。

    • MD2,MD4,MD5 计算的结果都是是一个128位(即16字节)的散列值,用于确保信息传输完整一致。
    • MD2的算法较慢但相对安全,MD4速度很快,但安全性下降,MD5则比MD4更安全、速度更快。
    • MD5被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。
    • MD5,可以被攻破,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞攻击,因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

    举个例子,看看如何获取字符串的MD5值吧:

    public class MD5Test {
    
        public static void main(String[] args) throws UnsupportedEncodingException {
            String s = "123";
            byte[] result = getMD5Bytes(s.getBytes());
            StringBuilder stringBuilder = new StringBuilder();
            for (byte temp : result) {
                if (temp >= 0 && temp < 16) {
                    stringBuilder.append("0");
                }
                stringBuilder.append(Integer.toHexString(temp & 0xff));
            }
            System.out.println(s + ",MD5加密后:" + stringBuilder.toString());
        }
    
        private static byte[] getMD5Bytes(byte[] content) {
            try {
                MessageDigest md5 = MessageDigest.getInstance("MD5");
                return md5.digest(content);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
    复制代码

    运行结果:

    123,MD5加密后:202cb962ac59075b964b07152d234b70
    复制代码

    ShA家族算法

    SHA(Secure Hash Algorithm,安全散列算法),包括SHA-0、SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384等)、SHA-3。它是在MD算法基础上实现的,与MD算法区别在于「摘要长度」,SHA 算法的摘要「长度更长,安全性更高」

    • SHA-0发布之后很快就被NSA撤回,因为含有会降低密码安全性的错误,它是SHA-1的前身。
    • SHA-1在许多安全协议中广为使用,包括TLS、GnuPG、SSH、S/MIME和IPsec,是MD5的后继者。
    • SHA-2包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。它的算法跟SHA-1基本上相似,目前还没有出现明显弱点。
    • SHA-3是2015年正式发布,由于对「MD5出现成功的攻破」,以及对SHA-0和SHA-1出现理论上攻破的方法,SHA-3应运而生。它与之前算法不同的是,它是可替换的加密散列算法。

    SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384)等算法是比较常用的,我们来看看跟MD5的对比吧

    算法类型摘要长度(bits)最大输入消息长度(bits)碰撞攻击(bits)性能示例(MiB/s)
    MD5128无限≤18(发现碰撞)335
    SHA-11602^64 − 1<63(发现碰撞)192
    SHA-2242242^64 − 1112139
    SHA-2562562^64 − 1128139
    SHA-3843842^128 − 1192154
    SHA-5125122^128 − 1256154

    MAC算法家族

    MAC算法 MAC(Message Authentication Code,消息认证码算法),是带密钥的Hash函数。输入密钥和消息,输出一个消息摘要。 它集合了MD和SHA两大系列消息摘要算法。

    • MD 系列算法: HmacMD2、HmacMD4 和 HmacMD5 ;
    • SHA 系列算法:HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384 和 HmacSHA512 。

    对称加密算法

    加密和解密使用「相同密钥」的加密算法就是对称加密算法。常见的对称加密算法有AES、3DES、DES、RC5、RC6等。

    DES

    数据加密标准(英语:Data Encryption Standard,缩写为 DES)是一种对称密钥加密块密码算法。 DES算法的入口参数有三个:Key、Data、Mode。

    • Key: 7个字节共56位,是DES算法的工作密钥;
    • Data: 8个字节64位,是要被加密或被解密的数据;
    • Mode: 加密或解密。

    3DES

    三重数据加密算法(英语:Triple Data Encryption Algorithm,又称3DES(Triple DES),是一种对称密钥加密块密码,相当于是对每个数据块应用三次数据加密标准(DES)算法。

    AES

    AES,高级加密标准(英语:Advanced Encryption Standard),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

    • 采用对称分组密码体制,密钥长度为 128 位、 192 位、256 位,分组长度128位
    • 相对于DES ,AES具有更好的 安全性、效率 和 灵活性。

    非对称加密算法

    非对称加密算法需要两个密钥:公钥和私钥。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有用对应的私钥才能解密。主要的非对称加密算法有:RSA、Elgamal、DSA、D-H、ECC。

    RSA算法

    • RSA加密算法是一种非对称加密算法,广泛应用于加密和数字签名
    • RSA算法原理:两个大素数的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
    • RSA是被研究得最广泛的公钥算法,从提出到现在,经历了各种攻击的考验,普遍认为是目前最优秀的公钥方案之一。

    DSA

    • DSA(Digital Signature Algorithm,数字签名算法),也是一种非对称加密算法。
    • DSA和RSA区别在,DSA仅用于数字签名,不能用于数据加密解密。其安全性和RSA相当,但其性能要比RSA好。

    ECC 算法

    • ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学),基于椭圆曲线加密。
    • Ecc主要优势是,在某些情况下,它比其他的方法使用更小的密钥,比如RSA加密算法,提供相当的或更高等级的安全级别。
    • 它的一个缺点是,加密和解密操作的实现比其他机制时间长 (相比RSA算法,该算法对CPU 消耗严重)。

    国密算法

    国密即国家密码局认定的国产密码算法。为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,即SM1,SM2,SM3,SM4等国密算法。

    SM1

    • SM1,为对称加密算法,加密强度为128位,基于硬件实现。
    • SM1的加密强度和性能,与AES相当。

    SM2

    • SM2主要包括三部分:签名算法、密钥交换算法、加密算法
    • SM2用于替换RSA加密算法,基于ECC,效率较低。

    SM3

    • SM3,即国产消息摘要算法。
    • 适用于商用密码应用中的数字签名和验证,消息认证码的生成与验证以及随机数的生成。

    SM4

    • SM4是一个分组算法,用于无线局域网产品。
    • 该算法的分组长度为128比特,密钥长度为128比特。
    • 加密算法与密钥扩展算法都采用32轮非线性迭代结构。
    • 解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
    • 它的功能类似国际算法的DES。

    加签验签相关Java的API

    这个小节先介绍一下加签验签需要用到的API吧~

    加签相关API

    - java.security.Signature.getInstance(String algorithm); //根据对应算法,初始化签名对象
    - KeyFactory.getInstance(String algorithm);// 根据对应算法,生成KeyFactory对象
    - KeyFactory.generatePrivate(KeySpec keySpec); //生成私钥
    - java.security.Signature.initSign(PrivateKey privateKey) //由私钥,初始化加签对象
    - java.security.Signature.update(byte[] data)  //把原始报文更新到加签对象
    - java.security.Signature.sign();//加签
    复制代码

    「Signature.getInstance(String algorithm);」

    • 根据对应算法,初始化签名对象
    • algorithm参数可以取SHA256WithRSA或者MD5WithRSA等参数,SHA256WithRSA表示生成摘要用的是SHA256算法,签名加签用的是RSA算法

    「KeyFactory.getInstance(String algorithm);」

    • 根据对应算法,生成KeyFactory对象,比如你的公私钥用的是RSA算法,那么就传入RSA

    「KeyFactory.generatePrivate(KeySpec keySpec)」

    • 生成私钥,加签用的是私钥哈,所以需要通过KeyFactory先构造一个私钥对象。

    「Signature.initSign(PrivateKey privateKey)」

    • 加签用的是私钥,所以传入私钥,初始化加签对象

    「Signature.update(byte[] data)」

    • 把原始报文更新到加签对象

    「java.security.Signature.sign();」

    • 进行加签操作

    验签相关API

    - java.security.Signature.getInstance(String algorithm); //根据对应算法,初始化签名对象
    - KeyFactory.getInstance(String algorithm);// 根据对应算法,生成KeyFactory对象
    - KeyFactory.generatePublic(KeySpec keySpec); //生成公钥
    - java.security.Signature.initVerify(publicKey); //由公钥,初始化验签对象
    - java.security.Signature.update(byte[] data)  //把原始报文更新到验签对象
    - java.security.Signature.verify(byte[] signature);//验签
    复制代码

    「Signature.getInstance(String algorithm)」

    • 根据对应算法,初始化签名对象,注意验签和加签是需要用相同的algorithm算法参数哦~

    「KeyFactory.getInstance(String algorithm);」

    • 根据对应算法,生成KeyFactory对象

    「KeyFactory.generatePublic(KeySpec keySpec);」

    • 生成公钥,验签用的是公钥,通过KeyFactory先构造一个公钥对象

    **Signature.initVerify(publicKey); **

    • 公钥验签,所以传入公钥对象参数,初始化验签对象

    「Signature.update(byte[] data)」

    • 把原始报文更新到加签对象

    「Signature.verify(byte[] signature);」

    • 进行验签操作

    加签验签代码实现

    前几个小节讨论完概念,是时候上代码实战了,我这边用的是SHA-256作为摘要算法,RSA作为签名验签算法,如下:

    package pattern;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.security.*;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.PKCS8EncodedKeySpec;
    
    /**
     * 加签验签demo
     *  @Author 捡田螺的小男孩
     */
    public class SignatureTest {
        //公钥字符串
        private static final String PUBLIC_KEY_STR = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaJzVjC5K6kbS2YE2fiDs6H8pB\n" +
                "JFDGEYqqJJC9I3E0Ebr5FsofdImV5eWdBSeADwcR9ppNbpORdZmcX6SipogKx9PX\n" +
                "5aAO4GPesroVeOs91xrLEGt/arteW8iSD+ZaGDUVV3+wcEdci/eCvFlc5PUuZJou\n" +
                "M2XZaDK4Fg2IRTfDXQIDAQAB";
        //私钥字符串
        private static final String PRIVATE_KEY_STR = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANonNWMLkrqRtLZg\n" +
                "TZ+IOzofykEkUMYRiqokkL0jcTQRuvkWyh90iZXl5Z0FJ4APBxH2mk1uk5F1mZxf\n" +
                "pKKmiArH09floA7gY96yuhV46z3XGssQa39qu15byJIP5loYNRVXf7BwR1yL94K8\n" +
                "WVzk9S5kmi4zZdloMrgWDYhFN8NdAgMBAAECgYA9bz1Bn0i68b2KfqRdgOfs/nbe\n" +
                "0XNN1DLQp2t7WDfRCg01iI1zPkZgyFVZWtI85f5/uIrLs5ArLosL1oNuqqc0nNne\n" +
                "CvJK+ZxvA98Hx3ZqYTzDnleR054YhofL5awbhSciYVic204DOG1rhSsYWMqtX7J7\n" +
                "3geoWL7TYdMfYXcCAQJBAPMMKsz6ZJh98EeQ1tDG5gpAGWFQkYNrxZDelP/LjeO0\n" +
                "TP3XkQnIpcaZoCs7V/rRGRGMWwQ2BUdc/01in89ZZ5ECQQDlx2oBc1CtOAm2UAhN\n" +
                "1xWrPkZWENQ53wTrwXO4qbTGDfBKon0AehLlGCSqxQ71aufLkNO7ZlX0IHTAlnk1\n" +
                "TvENAkAGSEQ69CXxgx/Y2beTwfBkR2/gghKg0QJUUkyLqBlMz3ZGAXJwTE1sqr/n\n" +
                "HiuSAiGhwH0ByNuuEotO1sPGukrhAkAMK26a2w+nzPL+u+hkrwKPykGRZ1zGH+Cz\n" +
                "19AYNKzFXJGgclCqiMydY5T1knBDYUEbj/UW1Mmyn1FvrciHoUG1AkAEMEIuDauz\n" +
                "JabEAU08YmZw6OoDGsukRWaPfjOEiVhH88p00veM1R37nwhoDMGyEGXVeVzNPvk7\n" +
                "cELg28MSRzCK";
    
    
        public static void main(String[] args) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidKeySpecException {
            //原始报文
            String plain = "欢迎大家关注我的公众号,捡田螺的小男孩";
            //加签
            byte[] signatureByte = sign(plain);
            System.out.println("原始报文是:" + plain);
            System.out.println("加签结果:");
            System.out.println(new BASE64Encoder().encode(signatureByte));
            //验签
            boolean verifyResult = verify(plain, signatureByte);
            System.out.println("验签结果:" + verifyResult);
        }
    
        /**
         * 加签方法
         * @param plain
         * @return
         * @throws NoSuchAlgorithmException
         * @throws InvalidKeyException
         * @throws UnsupportedEncodingException
         * @throws SignatureException
         */
        private static byte[] sign(String plain) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
            //根据对应算法,获取签名对象实例
            Signature signature = Signature.getInstance("SHA256WithRSA");
            //获取私钥,加签用的是私钥,私钥一般是在配置文件里面读的,这里为了演示方便,根据私钥字符串生成私钥对象
            PrivateKey privateKey = getPriveteKey(PRIVATE_KEY_STR);
            //初始化签名对象
            signature.initSign(privateKey);
            //把原始报文更新到对象
            signature.update(plain.getBytes("UTF-8"));
            //加签
            return signature.sign();
        }
    
        /**
         * 验签方法
         * @param plain
         * @param signatureByte
         * @return
         * @throws NoSuchAlgorithmException
         * @throws InvalidKeyException
         * @throws IOException
         * @throws SignatureException
         * @throws InvalidKeySpecException
         */
        private static boolean verify(String plain, byte[] signatureByte) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, InvalidKeySpecException {
            //获取公钥
            PublicKey publicKey = getPublicKey(PUBLIC_KEY_STR);
            //根据对应算法,获取签名对象实例
            Signature signature = Signature.getInstance("SHA256WithRSA");
            //初始化签名对象
            signature.initVerify(publicKey);
            //把原始报文更新到签名对象
            signature.update(plain.getBytes("UTF-8"));
            //进行验签
            return signature.verify(signatureByte);
        }
    
        private static PublicKey getPublicKey(String publicKeyStr) throws InvalidKeySpecException, IOException {
            PublicKey publicKey = null;
            try {
                java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
                        new BASE64Decoder().decodeBuffer(publicKeyStr));
                // RSA对称加密算法
                java.security.KeyFactory keyFactory;
                keyFactory = java.security.KeyFactory.getInstance("RSA");
                // 生成公钥对象
                publicKey = keyFactory.generatePublic(bobPubKeySpec);
               } catch (NoSuchAlgorithmException e) {
                 e.printStackTrace();
                }
            return publicKey;
          }
    
        private static PrivateKey getPriveteKey(String privateKeyStr) {
            PrivateKey privateKey = null;
            PKCS8EncodedKeySpec priPKCS8;
            try {
                priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKeyStr));
                KeyFactory keyf = KeyFactory.getInstance("RSA");
                privateKey = keyf.generatePrivate(priPKCS8);
            } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
                e.printStackTrace();
            }
            return privateKey;
        }
    }
    复制代码

    「运行结果:」

    原始报文是:欢迎大家关注我的公众号,捡田螺的小男孩
    加签结果:
    Oz15/aybGe42eGHbc+iMoSYHSCc8tfRskTVjjGSTPD4HjadL0CC5JUWNUW0WxHjUb4MvxWo2oeWE
    Qw0+m61d+JgBMto/TWcVDcgwL/AbObsbWdQ6E/fVRqG13clkE8MyKsjt9Z7tcbwpycYTv0rUR4co
    rndAVfBdtv5KeV+OXqM=
    验签结果:true
    复制代码


     

    展开全文
  • 点击蓝色“java大数据修炼之道”关注我哟 个“星标”,每晚21:00,一起学技术 来源: jianshu.com/p/5e9fe1fff6a3 作者: 不学无数的程序员面对MD5...
    点击蓝色“java大数据修炼之道”关注我哟
    
    加个“星标”,每晚21:00,一起学技术
    
    
    
    来源: jianshu.com/p/5e9fe1fff6a3
    作者: 不学无数的程序员

    面对MD5、SHA、DES、AES、RSA等等这些名词你是否有很多问号?这些名词都是什么?还有什么公钥加密、私钥解密、私钥加签、公钥验签。这些都什么鬼?或许在你日常工作没有听说过这些名词,但是一旦你要设计一个对外访问的接口,或者安全性要求高的系统,那么必然会接触到这些名词。所以加解密、加签验签对于一个合格的程序员来说是必须要掌握的一个概念。那么加解密相关的密码学真的离我们很遥远吗?其实生活中有很多常见的场景其实都用到了密码学的相关知识,我们不要把它想得太难,例如在《睡在我上铺的兄弟》这一段中作弊绕口令中,小瘪三代表A,小赤佬代表B,唉呀妈呀代表C,坑爹呀是D,所以其实密码学与我们生活息息相关,接下来我们就一文彻底搞懂这些概念。

    Part1没有硝烟的战场——浅谈密码技术

    没有根基也许可以建一座小屋,但绝对不能造一座坚固的大厦。

    密码这个词有很多种的解释,在现代社会如果不接触编程的话,那么普遍的认为是我们设置的登录密码、或者是去银行取钱时输入的数字。都是我们在注册时实现给提供服务的一方存储一组数字,以后我们登录的时候就用这组数字相当于就证明了我们的身份。这个数字通常来说就是叫做密码。

    而我们需要了解的不是上面说的密码,而是一种“密码术”,就是对于要传递的信息按照某种规则进行转换,从而隐藏信息的内容。这种方法可以使机密信息得以在公开的渠道传递而不泄密。使用这种方法,要经过加密过程。在加密过程中我们需要知道下面的这些概念:

    • 原文:或者叫明文,就是被隐藏的文字

    • 加密法:指隐藏原文的法则

    • 密文:或者叫伪文,指对原文按照加密法处理过后生成的可公开传递的文字

    • 密钥:在加密法中起决定性的因素,可能是数字、词汇,也可能是一些字母,或者这些东西的组合

    加密的结果生成了密文,要想让接受者能够读懂这些密文,那么就要把加密法以及密钥告诉接受者,否者接受者无法对密文解密,也就无法读懂原文。

    从历史的角度来看,密码学大概可以分为古典密码学和近现代密码学两个阶段。两者以现代信息技术的诞生为分界点,现在所讨论的密码学多指的是后者,建立在信息论和数学成果基础之上的。

    古典密码学

    古典密码学源自于数千年前,最早在公元前1900年左右的古埃及,就出现了通过使用特殊字符和简单替换式密码来保护信息。美索不达米亚平原上曾经出土一个公元前1500年左右的泥板,其上记录了加密描述的陶瓷器上釉的工艺配方。古希腊时期(公元前800 ﹣前146 年)还发明了通过物理手段来隐藏信息的“隐写术”,例如使用牛奶书写、用蜡覆盖文字等。后来在古罗马时期还出现了基于替换加密的凯撒密码,据称凯撒曾用此方法与其部下通信而得以命名。这些手段多数是采用简单的机械工具来保护秘密,在今天看来毫无疑问是十分简陋,很容易猜出来的。严格来看,可能都很难称为密码科学。

    凯撒密码是当偏移量是3的时候,所有的字母都A都将被替换成D,B变成E,以此类推。

    近代密码学

    近代密码学的研究来自于第一、二次世界大战中对于军事通信进行保护和猜出来的需求。1901年12月,意大利的工程师Guglielmo Marconi(奎里亚摩•马可尼)成功完成了跨越大西洋的无线电通信的实验,在全球范围内引发轰动,推动了无线电通信时代的到来。无线电大大提高了远程通信的能力,但是它有一个天然的缺陷——很难限制接收方,这就意味着你所传的信息有可能被拦截,因此就催生了加密技术的发展。

    对于无线电信息进行加密和解密也直接促进了近现代密码学和计算机技术的出现。反过来这些科技进步也影响了时代的发展。一战时期德国外交部长Arthur Zimmermann(阿瑟•齐默尔曼)拉拢墨西哥构成抗美军事同盟的电报(1917 年1月16日)被英国情报机构—40号办公室破译,直接导致了美国的参战;二战时期德国使用的恩尼格玛(Enigma)密码机(当时最先进的加密设备)被盟军成功破译(1939年到1941年),导致大西洋战役德国失败。据称,二战时期光英国从事密码学研究的人员就达到7000人,而他们的成果使二战结束的时间至少提前了一到两年时间。

    接下来就是可以称之为是密码学发展史上里程碑的事件了。1945年9月1日,Claude Elwood Shannon(克劳德•艾尔伍德•香农)完成了划时代的内部报告《A Mathematical Theory of Cryptography(密码术的一个数学理论)》,1949 年 10 月,该报告以《Communication Theory of Secrecy Systems(保密系统的通信理论)》为题在 Bell System Technical Journal(贝尔系统技术期刊)上正式发表。这篇论文首次将密码学和信息论联系到一起,为对称密码技术提供了数学基础。这也标志着近现代密码学的正式建立。这也是密码学发展史上的第一座里程碑性事件。

    密码学发展史上的第二个里程碑性事件是DES的出现。DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的分组密码算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。

    密码学发展史上的第三个里程碑性事件就是我们区块链中广泛应用的公钥密码,也就是非对称密码算法的出现。1976年11月,Whitfield Diffie 和 Martin E.Hellman 在 IEEE Transactions on Information Theory 上发表了论文《New Directions in Cryptography(密码学的新方向)》,探讨了无需传输密钥的保密通信和签名认证体系问题,正式开创了现代公钥密码学体系的研究。在公钥密码发现以前,如果需要保密通信,通信双方事先要对加解密的算法以及要使用的密钥进行事先协商,包括送鸡毛信,实际上是在传送密钥。但自从有了公钥密码,需要进行秘密通信的双方不再需要进行事前的密钥协商了。公钥密码在理论上是不保密的,在实际上是保密的。也就是说,公钥密码是可以猜出来的,但需要极长的时间,等到猜出来了,这个秘密也没有保密的必要了。

    上面我们说到了关于近现代的密码学相关的东西,基本上总结下来我们现在常用的就两个,一个是对称加密算法,一个是非对称加密算法。那么接下来我们就以介绍这两个概念为主线引出开题中我们提到的概念。

    Part2程序实现

    1对称加密算法

    对称加密指的就是加密和解密使用同一个秘钥,所以叫做对称加密。对称加密只有一个秘钥,作为私钥。具体的算法有:DES、3DES、TDEA、Blowfish,RC5,IDEA。但是我们常见的有:DES、AES等等。

    那么对称加密的优点是什么呢?算法公开、计算量小、加密速度快、加密效率高。缺点就是秘钥的管理和分发是非常困难的,不够安全。在数据传送前,发送方和接收方必须商定好秘钥,然后双方都必须要保存好秘钥,如果一方的秘钥被泄露了,那么加密的信息也就不安全了。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘钥,这会使得收、发双方所拥有的的钥匙数量巨大,秘钥管理也会成为双方的负担。

    加密的过程我们可以理解为如下:

    • 加密:原文+秘钥 = 密文

    • 解密:密文-秘钥 = 原文

    可以看到两次过程使用的都是一个秘钥。用图简单表示如下:

    实战演练

    既然我们知道关于对称加密算法的相关知识,那么我们日常用Java如何实现对称加密的加密和解密动作呢?常见的对称加密算法有:DES、AES等。

    DES

    DES加密算法是一种分组密码,以64位为分组对数据加密,它的密钥长度是56位,加密解密用同一算法。DES加密算法是对密钥进行保密,而公开算法,包括加密和解密算法。这样,只有掌握了和发送方相同密钥的人才能解读由DES加密算法加密的密文数据。因此,破译DES加密算法实际上就是搜索密钥的编码。对于56位长度的密钥来说,如果用穷举法来进行搜索的话,其运算次数为2的56次方。

    接下来用Java实现DES加密

    private final static String DES = "DES";
    
        public static void main(String[] args) throws Exception {
            String data = "123 456";
            String key = "wang!@#$";
            System.err.println(encrypt(data, key));
            System.err.println(decrypt(encrypt(data, key), key));
    
        }
    
        /**
         * Description 根据键值进行加密
         * @param data
         * @param key  加密键byte数组
         * @return
         * @throws Exception
         */
        public static String encrypt(String data, String key) throws Exception {
            byte[] bt = encrypt(data.getBytes(), key.getBytes());
            String strs = new BASE64Encoder().encode(bt);
            return strs;
        }
    
        /**
         * Description 根据键值进行解密
         * @param data
         * @param key  加密键byte数组
         * @return
         * @throws IOException
         * @throws Exception
         */
        public static String decrypt(String data, String key) throws IOException,
                Exception {
            if (data == null)
                return null;
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] buf = decoder.decodeBuffer(data);
            byte[] bt = decrypt(buf,key.getBytes());
            return new String(bt);
        }
    
        /**
         * Description 根据键值进行加密
         * @param data
         * @param key  加密键byte数组
         * @return
         * @throws Exception
         */
        private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
            // 生成一个可信任的随机数源
            SecureRandom sr = new SecureRandom();
    
            // 从原始密钥数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key);
    
            // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey securekey = keyFactory.generateSecret(dks);
    
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance(DES);
    
            // 用密钥初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
    
            return cipher.doFinal(data);
        }
    
    
        /**
         * Description 根据键值进行解密
         * @param data
         * @param key  加密键byte数组
         * @return
         * @throws Exception
         */
        private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
            // 生成一个可信任的随机数源
            SecureRandom sr = new SecureRandom();
    
            // 从原始密钥数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key);
    
            // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey securekey = keyFactory.generateSecret(dks);
    
            // Cipher对象实际完成解密操作
            Cipher cipher = Cipher.getInstance(DES);
    
            // 用密钥初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
    
            return cipher.doFinal(data);
        }
    

    输出以后可以看到数据被加密了

    5fiw/XhRJ0E=
    123 456
    

    在Java中用DES加密有一个特殊的地方

    1. 秘钥设置的长度必须大于等于8

    2. 秘钥设置的长度如果大于8的话,那么只会取前8个字节作为秘钥

    为什么呢,我们可以看到在初始化DESKeySpec类的时候有下面一段,其中var1是我们传的秘钥。可以看到他进行了截取。只截取前八个字节。

    public DESKeySpec(byte[] var1, int var2) throws InvalidKeyException {
        if (var1.length - var2 < 8) {
            throw new InvalidKeyException("Wrong key size");
        } else {
            this.key = new byte[8];
            System.arraycopy(var1, var2, this.key, 0, 8);
        }
    }
    

    AES

    AES加密算法是密码学中的高级加密标准,该加密算法采用对称分组密码体制,密钥长度的最少支持为128、192、256,分组长度128位,算法应易于各种硬件和软件实现。这种加密算法是美国联邦政府采用的区块加密标准,AES标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

    JCE,Java Cryptography Extension,在早期JDK版本中,由于受美国的密码出口条例约束,Java中涉及加解密功能的API被限制出口,所以Java中安全组件被分成了两部分: 不含加密功能的JCA(Java Cryptography Architecture )和含加密功能的JCE(Java Cryptography Extension)。

    JCE的API都在javax.crypto包下,核心功能包括:加解密、密钥生成(对称)、MAC生成、密钥协商。

    加解密功能由Cipher组件提供,其也是JCE中最核心的组件。

    在设置Cipher类的时候有几个注意点:

    1. Cipher在使用时需以参数方式指定transformation

    2. transformation的格式为algorithm/mode/padding,其中algorithm为必输项,如: AES/DES/CBC/PKCS5Padding,具体有哪些可看下表

    3. 缺省的mode为ECB,缺省的padding为PKCS5Padding

    4. 在block算法与流加密模式组合时, 需在mode后面指定每次处理的bit数, 如DES/CFB8/NoPadding, 如未指定则使用缺省值, SunJCE缺省值为64bits

    5. Cipher有4种操作模式: ENCRYPT_MODE(加密), DECRYPT_MODE(解密), WRAP_MODE(导出Key), UNWRAP_MODE(导入Key),初始化时需指定某种操作模式

    算法/模式/填充16字节加密后数据长度不满16字节加密后长度
    AES/CBC/NoPadding16不支持
    AES/CBC/PKCS5Padding3216
    AES/CBC/ISO10126Padding3216
    AES/CFB/NoPadding16原始数据长度
    AES/CFB/PKCS5Padding3216
    AES/CFB/ISO10126Padding3216
    AES/ECB/NoPadding16不支持
    AES/ECB/PKCS5Padding3216
    AES/ECB/ISO10126Padding3216
    AES/OFB/NoPadding16原始数据长度
    AES/OFB/PKCS5Padding3216
    AES/OFB/ISO10126Padding3216
    AES/PCBC/NoPadding16不支持
    AES/PCBC/PKCS5Padding3216
    AES/PCBC/ISO10126Padding3216

    秘钥的可以由我们自己定义,也可以是由AES自己生成,当自己定义是需要是要注意:

    1. 根据 AES 规范,可以是 16 字节、24 字节和32 字节长,分别对应 128 位、192 位和 256 位;

    2. 为便于传输,一般对加密后的数据进行 base64 编码:

    public static void main(String[] args) throws Exception {
            /*
             * 此处使用AES-128-ECB加密模式,key需要为16位。
             */
            String cKey = "1234567890123456";
            // 需要加密的字串
            String cSrc = "buxuewushu";
            System.out.println(cSrc);
            // 加密
            String enString = Encrypt(cSrc, cKey);
            System.out.println("加密后的字串是:" + enString);
    
            // 解密
            String DeString = Decrypt(enString, cKey);
            System.out.println("解密后的字串是:" + DeString);
        }
    
        // 加密
        public static String Encrypt(String sSrc, String sKey) throws Exception {
            if (sKey == null) {
                System.out.print("Key为空null");
                return null;
            }
            // 判断Key是否为16位
            if (sKey.length() != 16) {
                System.out.print("Key长度不是16位");
                return null;
            }
            byte[] raw = sKey.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
    
            return new Base64().encodeToString(encrypted);//此处使用BASE64做转码功能,同时能起到2次加密的作用。
        }
    
        // 解密
        public static String Decrypt(String sSrc, String sKey) throws Exception {
            try {
                // 判断Key是否正确
                if (sKey == null) {
                    System.out.print("Key为空null");
                    return null;
                }
                // 判断Key是否为16位
                if (sKey.length() != 16) {
                    System.out.print("Key长度不是16位");
                    return null;
                }
                byte[] raw = sKey.getBytes("utf-8");
                SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, skeySpec);
                byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
                try {
                    byte[] original = cipher.doFinal(encrypted1);
                    String originalString = new String(original,"utf-8");
                    return originalString;
                } catch (Exception e) {
                    System.out.println(e.toString());
                    return null;
                }
            } catch (Exception ex) {
                System.out.println(ex.toString());
                return null;
            }
        }
    

    2非对称加密算法

    非对称加密算法中加密和解密用的不是同一个秘钥,所以叫作非对称加密算法。在非对称加密算法每个用户都有两把钥匙,一把公钥一把私钥。公钥是对外发布的,所有人都看的到所有人的公钥,私钥是自己保存,每个人都只知道自己的私钥而不知道别人的。而也正是在非对称加密算法中有加密和解密、加签和验签的概念。接下来我们解释一下这几个概念是什么意思。

    加密和解密

    用该用户的公钥加密后只能该用户的私钥才能解密。这种情况下,公钥是用来加密信息的,确保只有特定的人(用谁的公钥就是谁)才能解密该信息。所以这种我们称之为加密和解密

    下面我拿A银行和小明来举例子吧。假设这2者之间是用不对称的加密算法来保证信息传输的安全性(不被第三人知道信息的含义及篡改信息)。大致流程如下:首先小明发了一条信息给A银行“我要存500元”。这条信息小明会根据A银行的对外发布的公钥把这条信息加密了,加密之后,变成“XXXXXXX”发给A银行。中间被第三者截获,由于没有A银行的私钥无法解密,不能知道信息的含义,也无法按正确的方式篡改。所以拿这条加密信息是没办法的。最后被A银行接受,A银行用自己的私钥去解密这条信息,解密成功,读取内容,执行操作。然后得知消息是小明发来的,便去拿小明的公钥,把“操作成功(或失败)”这条信息用小明的公钥加密,发给小明。同理最后小明用自己的私钥解开,得知知乎发来的信息内容。其他人截获因为没有小明的私钥所以也没有用。

    加签和验签

    还有第二种情况,公钥是用来解密信息的,确保让别人知道这条信息是真的由我发布的,是完整正确的。接收者由此可知这条信息确实来自于拥有私钥的某人,这被称作数字签名,公钥的形式就是数字证书。所以这种我们称之为加签和验签

    继续拿小明和银行A举例子。银行A发布了一个银行客户端的补丁供所有用户更新,那为了确保人家下载的是正确完整的客户端,银行A会为这个程序打上一个数字签名(就是用银行A的私钥对这个程序加密然后发布),你需要在你的电脑里装上银行A的数字证书(就是银行对外发布的公钥),然后下载好这个程序,数字证书会去解密这个程序的数字签名,解密成功,补丁得以使用。同时你能知道这个补丁确实是来自这个银行A,是由他发布的,而不是其他人发布的。

    实战演练

    我们在开发过程中经常使用的非对称加密算法就是RSA算法。接下来我们使用Java实现RSA算法。

    生成密钥

    首先是生成key的部分,生成key有好多种做法,这里我介绍三种

    1. 命令行:可以使用openssl进行生成公钥和私钥

    -- 生成公钥和私钥
    openssl genrsa -out key.pem 1024
           -out 指定生成文件,此文件包含公钥和私钥两部分,所以即可以加密,也可以解密
           1024 生成密钥的长度
    
    1. 使用网站:生成密钥的网站

    2. 使用代码:可以指定生成密钥的长度,最低是512

    public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
       final int keySize = 2048;
       KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
       keyPairGenerator.initialize(keySize);
       return keyPairGenerator.genKeyPair();
    }
    

    加密

    有了密钥,就可以进行加密的操作了,接下来就介绍关于RSA的加密操作,非常简单只要传进来公钥和需要加密的数据即可。

    --- 加密
    public static byte[] encrypt(PublicKey publicKey, String message) throws Exception {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    
            return cipher.doFinal(message.getBytes(UTF8));
        }
    

    解密

    --- 解密
    public static byte[] decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
    
        return cipher.doFinal(encrypted);
    }
    

    加签

    /**
     * 使用RSA签名
     */
    private static String signWithRSA(String content, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initSign(privateKey);
        signature.update(content.getBytes("utf-8"));
        byte[] signed = signature.sign();
        return base64Encode(signed);
    }
    

    验签

    /**
     * 使用RSA验签
     */
    private static boolean checkSignWithRSA(String content, PublicKey publicKey,String sign) throws Exception {
        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initVerify(publicKey);
        signature.update(content.getBytes("utf-8"));
        return signature.verify(base64Decode(sign));
    }
    

    在加签验签的时候需要传入一个数字签名标准,我们这里填的是SHA1WithRSA,它的意思是用SHA算法进行签名,用RSA算法进行加密。

    算法说明:在对进行SHA1算法进行摘要计算后,要求对计算出的摘要进行处理,而不是直接进行RSA算法进行加密。要求把SHA1摘要的数据进行压缩到20个字节。在前面插入15个字节标示数据。所以结构如下

    30(数据类型结构)21(总长度)30(数据类型)09(长度)06 05 2B 0E 03 02 1A 0500【数据具体类型不清楚-请专家指正】 04 (数据类型) 14 (长度) + SHA1签名数据
    

    最后进行RSA加密。所以我们填写的XXXWithRSA,这个XXX代表的就是使用什么摘要算法进行加签,至于摘要算法是什么,随后会有详细的说明。

    调用实验一下

    public static void main(String[] args) throws Exception {
        KeyPair keyPair = buildKeyPair();
    
        byte[] encryptData = encrypt(keyPair.getPublic(), "不学无数");
    
        System.out.println(String.format("加密后的数据:%s",base64Encode(encryptData)));
    
        System.out.println(String.format("解密后的数据:%s",new String(decrypt(keyPair.getPrivate(),encryptData),UTF8)));
    
        String context = "加签的字符串";
    
        String sign = signWithRSA(context, keyPair.getPrivate());
    
        System.out.println(String.format("生成的签名:%s",sign));
    
        Boolean checkSignWithRSA = checkSignWithRSA(context, keyPair.getPublic(), sign);
    
        System.out.println(String.format("校验的结果:%s",checkSignWithRSA.toString()));
    }
    

    输出为

    加密后的数据:Bi8b4eqEp+rNRhDaij8vVlNwKuICbPJfFmyzmEXKuAgEgzMPb8hAmYiGN+rbUKWeZYJKJd0fiOXv
    6YrYqd7fdast/m443qQreRLxdQFScwvCvj9g1YnPzbU2Q/jIwqAPopTyPHNNngBmFki+R/6V4DYt
    HA5gniaUMYzynHdD+/W+x8ZYmwiuuS63+7wXqL36aLKe0H50wELOpSn45Gvni8u+5zPIoHV7PBiz
    trCnQvne5LxFKDprrS3td1/76qyupFd+Ul3hsd+gjbAyN2MlXcAFMrGVaRkopWwc9hP1BsPvS52q
    /8jOVdbeyU9BziVhViz1V0TtGW8bfbEnIStc3Q==
    解密后的数据:不学无数
    生成的签名:wvUXtr2UI0tUXmyMTTUBft8oc1dhvtXSBrFFetI5ZoxMm91TbXRWD31Pgqkg72ADxx9TEOAM3Bm1
    kyzfBCZZpoq6Y9SM4+jdJ4sMTVtw0wACPglnPDAGs8sG7nnLhXWNQ1Y4pl4ziY6uLxF1TzQLFTxu
    NAS7nyljbG69wrb9R3Sv5t8r1I54rYCVGSVFmTrGf+dSCjxABZv6mH8nygVif7zN1vU1+nSDKcON
    Vtrpv0xCQHVBqnHPA6OiDm5GzBQxjD5aQt8mfgv8JJrB52TEa4JPYoC5Zw4JHlL++OvPwMpJgnuG
    yg5vnWhxE2ncTzM+/pZ+CnXF2Dqv/JMQOfX6tA==
    校验的结果:true
    

    3摘要算法

    数据摘要算法是密码学算法中非常重要的一个分支,它通过对所有数据提取指纹信息以实现数据签名、数据完整性校验等功能,由于其不可逆性,有时候会被用做敏感信息的加密。数据摘要算法也被称为哈希(Hash)算法或散列算法。

    消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。(摘要可以比方为指纹,消息摘要算法就是要得到文件的唯一职位)

    特点

    无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。一般地,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息(不可逆性)。

    好的摘要算法,没有人能从中找到“碰撞”或者说极度难找到,虽然“碰撞”是肯定存在的(碰撞即不同的内容产生相同的摘要)。

    应用

    一般地,把对一个信息的摘要称为该消息的指纹或数字签名。数字签名是保证信息的完整性和不可否认性的方法。数据的完整性是指信宿接收到的消息一定是信源发送的信息,而中间绝无任何更改;信息的不可否认性是指信源不能否认曾经发送过的信息。其实,通过数字签名还能实现对信源的身份识别(认证),即确定“信源”是否是信宿意定的通信伙伴。数字签名应该具有唯一性,即不同的消息的签名是不一样的;同时还应具有不可伪造性,即不可能找到另一个消息,使其签名与已有的消息的签名一样;还应具有不可逆性,即无法根据签名还原被签名的消息的任何信息。这些特征恰恰都是消息摘要算法的特征,所以消息摘要算法适合作为数字签名算法

    有哪些具体的消息摘要算法?

    • CRC8、CRC16、CRC32:CRC(Cyclic Redundancy Check,循环冗余校验)算法出现时间较长,应用也十分广泛,尤其是通讯领域,现在应用最多的就是 CRC32 算法,它产生一个4字节(32位)的校验值,一般是以8位十六进制数,如FA 12 CD 45等。CRC算法的优点在于简便、速度快,严格的来说,CRC更应该被称为数据校验算法,但其功能与数据摘要算法类似,因此也作为测试的可选算法。

    • MD2 、MD4、MD5:这是应用非常广泛的一个算法家族,尤其是 MD5(Message-Digest Algorithm 5,消息摘要算法版本5),它由MD2、MD3、MD4发展而来,由Ron Rivest(RSA公司)在1992年提出,目前被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。MD2、MD4、MD5 都产生16字节(128位)的校验值,一般用32位十六进制数表示。MD2的算法较慢但相对安全,MD4速度很快,但安全性下降,MD5比MD4更安全、速度更快。

    • SHA1、SHA256、SHA384、SHA512:SHA(Secure Hash Algorithm)是由美国专门制定密码算法的标准机构——美国国家标准技术研究院(NIST)制定的,SHA系列算法的摘要长度分别为:SHA为20字节(160位)、SHA256为32字节(256位)、 SHA384为48字节(384位)、SHA512为64字节(512位),由于它产生的数据摘要的长度更长,因此更难以发生碰撞,因此也更为安全,它是未来数据摘要算法的发展方向。由于SHA系列算法的数据摘要长度较长,因此其运算速度与MD5相比,也相对较慢。

    • RIPEMD、PANAMA、TIGER、ADLER32 等: RIPEMD是Hans Dobbertin等3人在对MD4,MD5缺陷分析基础上,于1996年提出来的,有4个标准128、160、256和320,其对应输出长度分别为16字节、20字节、32字节和40字节。TIGER由Ross在1995年提出。Tiger号称是最快的Hash算法,专门为64位机器做了优化。

    实战演练

    在单独的使用摘要算法时我们通常使用的MD5算法,所以我们这里就单独说明使用Java实现MD5算法。

    public static String getMD5Str(String str) throws Exception {
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update(str.getBytes());
            // digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
            return new BigInteger(1, md .digest()).toString(16);
        } catch (Exception e) {
            throw new Exception("MD5加密出现错误,"+e.toString());
        }
    }
    

    在微信公众号后台回复关键字: PDF 

    获取:面试题学习资源合集 , 感谢支持 !)

    PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。

    ▽加我微信,交个朋友
    

    长按/扫码添加↑↑↑
    
    
    
    

    看完本文有收获?请转发分享给更多人

    请长按二维码,关注 Java大数据修炼之道.

    推荐程序员必备公众号 

    Java大数据修炼之道

    公众号:

    gh_9119f24d3793

    推荐理由:

    在这里,我们分享程序员相关技术,职场生活,行业热点资讯。不定期还会分享IT趣文和趣图。这里属于我们程序员自己的生活,工作和娱乐空间。

     ▼长按下方↓↓↓二维码识别关注

    关注 java大数据修炼之道

        每天学习java技术你想学的Java知识这里都有

    点「在看」的人都涨薪了哦

    
    
    展开全文
  • 最后,我们再来看用RSA如何对数据进行加签验签,具体代码如下: 1 import java.io.UnsupportedEncodingException;2 import java.security.*;3 /**4 * 加签 验签 签名验证:验证数据的合法来源 即验证数据来源的合法...

    RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。1987年7月首次在美国公布,当时他们三人都在麻省理工学院工作实习。RSA就是他们三人姓氏开头字母拼在一起组成的。

    RSA是目前最有影响力和最常用的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。

    RSA公开密钥密码体制。所谓的公开密钥密码体制就是使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。

    在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK。

    基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA 密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可以在网络服务器中注册。为提高保密强度,RSA密钥至少为500位长,一般推荐使用1024位。这就使加密的计算量很大。

    RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。RSA是被研究得最广泛的公钥算法,从提出到现今的三十多年里,经历了各种攻击的考验,逐渐为人们接受,截止2017年被普遍认为是最优秀的公钥方案之一。

    SET(Secure Electronic Transaction)协议中要求CA采用2048bits长的密钥,其他实体使用1024比特的密钥。RSA密钥长度随着保密级别提高,增加很快。

    RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。

    我们接下来看下Java中如何实现RSA加密解密与加签验签。我们先来看RSA加密解密。

    1 import javax.crypto.BadPaddingException;2 import javax.crypto.Cipher;3 import javax.crypto.IllegalBlockSizeException;4 import javax.crypto.NoSuchPaddingException;5 import java.security.*;6 import java.util.Base64;7 /**8 * RSA加密解密操作步骤9 */

    10 public classTest1 {11 public static voidmain(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {12 //先给出一个待加密的字符串

    13 String data="青青子衿,悠悠我心。但为君故,沉吟至今。";14 //1.构建公私钥匙对

    15 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");16 KeyPair keyPair =keyPairGenerator.generateKeyPair();17 //2.获取钥匙对中的公钥

    18 PublicKey publicKey =keyPair.getPublic();19 //3.获取钥匙对中的私钥

    20 PrivateKey privateKey =keyPair.getPrivate();21 //4.对待加密的数据进行加密

    22 Cipher cipher = Cipher.getInstance("RSA");23 cipher.init(Cipher.ENCRYPT_MODE,publicKey);24 byte[] bytesEncrypt = cipher.doFinal(data.getBytes());//产生的是乱码,需要用Base64进行转码25 //5.Base64编码

    26 byte[] encodeBase64 =Base64.getEncoder().encode(bytesEncrypt);27 System.out.println("加密后的数据:"+newString(encodeBase64));28 //6.在解密时,先对用Base64编码的信息进行解码

    29 byte[] bytesDecode =Base64.getDecoder().decode(encodeBase64);30 //7.解密

    31 Cipher cipher2=Cipher.getInstance("RSA");32 cipher2.init(Cipher.DECRYPT_MODE,privateKey);33 byte[] bytesDecrypt =cipher2.doFinal(bytesDecode);34 System.out.println("解密后的数据:"+newString(bytesDecrypt));35 }36 }

    公钥和私钥本身存储的信息是乱码,在实际使用中,我们还可以通过Base64将这些乱码编码为可识别的ASCII码,然后将公钥和私钥信息持久化存储到文件中,在以后需要使用时,可以从文件中读取公钥和私钥信息。为此,我们可以写一个RSA的工具类,从一个储存公钥和私钥信息的文件里读取公钥和私钥信息,然后定义获取公钥和私钥的方法,以及加密和解密数据的方法。首先,我们提供一对公私钥信息,假定公钥信息储存在一个名称为rsa_public_key.pem的文件里,信息如下:

    -----BEGIN RSA PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDY90KtriCa4KjNe3mgrGGbDB95

    8A2byBKf+wOmPmOopP3gGeg7+DFAPNYCC+tL8h2bpUI3IPKOm2Hon8kM/p628i1J

    Z7JjopYVZW6JKqA2ImyneeUEK748FXwXTRAAMCTqQG/7a178BGawTdHi6hk+M6UF

    lT0EhL6JA8ULKFoiHwIDAQAB-----BEGIN RSA PUBLIC KEY-----

    假定私钥信息储存在一个名称为rsa_private_key.pem的文件里,信息如下::

    -----BEGIN RSA PRIVATE KEY-----MIICXQIBAAKBgQDY90KtriCa4KjNe3mgrGGbDB958A2byBKf+wOmPmOopP3gGeg7+DFAPNYCC+tL8h2bpUI3IPKOm2Hon8kM/p628i1JZ7JjopYVZW6JKqA2ImyneeUE

    K748FXwXTRAAMCTqQG/7a178BGawTdHi6hk+M6UFlT0EhL6JA8ULKFoiHwIDAQAB

    AoGBAIJFhF2wLZeQyQoH13Gnzzs/Pi8C+cjNipFQMFLDJyd9WYoTRCOt1DST0pOM

    AI2rJCfuRCHBwKHrnhAE0LzirPxkmvyHTIBXIoz3fHiSkIKkUVG04BcgTYpNKWPB

    ISlzdhSaw7CnmJjTthTrD5LLPtpqUl350lUYFEHVNR6Ys9JRAkEA9JUEVxzSvQkV

    V6hxhbvlxl0mATbPfiNKDBTPdr48dyYdgluAoGfAPf9rmgoCpdEd2hZBIfdy7xdL

    LvP7ztb/rQJBAOMYNC/lZLz9A9cDJ5bibrJnmyRG0SAGAzu4ffYdBoGb0kRRKzTe

    5jxfRnbiUPQU4GQXhADfikGn2ogRqbtDsnsCQQCJdp+D3n1LJanLJK75PQv9myjb

    EdU4zdi2RZP85xrQ1KlNNORsQyO3NLFjWDD4xTmD83IUByGf43WsJBDoxcnZAkA3

    i84IARX42/I6fz0JvOzSmmDqKKAyMwZLbz7wGf1jalet+iSVVAgAsFUt8wFWEl0o

    XlAdXpAUqxfavGdFtLNNAkABS576xgLcLmyw51f9hoM9RiamLn+WNzoA5TLOZjGI

    dZZnX/A8SoFYGoJoN1O0hp5DxDdl+gjW/mH51+gliEIB-----END RSA PRIVATE KEY-----

    接下来,我们写一个RSAUtil工具类,代码如下:

    1 import javax.crypto.BadPaddingException;2 import javax.crypto.Cipher;3 import javax.crypto.IllegalBlockSizeException;4 import javax.crypto.NoSuchPaddingException;5 import java.io.BufferedReader;6 import java.io.FileReader;7 import java.io.IOException;8 import java.security.*;9 import java.security.spec.InvalidKeySpecException;10 import java.security.spec.PKCS8EncodedKeySpec;11 import java.security.spec.X509EncodedKeySpec;12 import java.util.Base64;13 /**14 * 工具类15 */

    16 public classRSAUtil {17 /*

    18 读取秘钥数据19 */

    20 public static byte[] readKeyDatas(String keyFilePath){21 BufferedReader bufferedReader=null;22 try{23 bufferedReader = new BufferedReader(newFileReader(keyFilePath));24 String str=null;25 StringBuilder stringBuilder=newStringBuilder();26 while ((str=bufferedReader.readLine())!=null){27 if(str.contains("---")){28 continue;29 }30 stringBuilder.append(str);31 }32 returnstringBuilder.toString().getBytes();33 }catch(IOException e) {34 e.printStackTrace();35 }finally{36 try{37 bufferedReader.close();38 } catch(IOException e) {39 e.printStackTrace();40 }41 }42 return null;43 }44 /*

    45 生成公钥46 */

    47 public staticPublicKey getPublicKey(String publicKeyPath){48 //1.读取公钥文件,获取公钥数据

    49 byte[] bytesPublicBase64 =readKeyDatas(publicKeyPath);50 //2.对读取回来的数据进行Base64解码

    51 byte[] bytesPublic =Base64.getDecoder().decode(bytesPublicBase64);52 //3.把解码后的数据,重新封装成一个PublicKey对象

    53 X509EncodedKeySpec keySpec = newX509EncodedKeySpec(bytesPublic);54 KeyFactory keyFactory=null;55 try{56 keyFactory = KeyFactory.getInstance("RSA");57 PublicKey publicKey =keyFactory.generatePublic(keySpec);58 returnpublicKey;59 } catch(NoSuchAlgorithmException e) {60 e.printStackTrace();61 } catch(InvalidKeySpecException e) {62 e.printStackTrace();63 }64 return null;65 }66 /*

    67 生成私钥68 */

    69 public staticPrivateKey getPrivateKey(String privateKeyPath){70 //1.读取私钥文件,获取私钥数据

    71 byte[] bytesPrivateBase64 =readKeyDatas(privateKeyPath);72 //2.对读取回来的数据进行Base64解码

    73 byte[] bytesPrivate =Base64.getDecoder().decode(bytesPrivateBase64);74 //3.把解码后的数据,重新封装成一个PrivateKey对象

    75 PKCS8EncodedKeySpec keySpec = newPKCS8EncodedKeySpec(bytesPrivate);76 KeyFactory keyFactory=null;77 try{78 keyFactory = KeyFactory.getInstance("RSA");79 PrivateKey privateKey =keyFactory.generatePrivate(keySpec);80 returnprivateKey;81 } catch(NoSuchAlgorithmException e) {82 e.printStackTrace();83 } catch(InvalidKeySpecException e) {84 e.printStackTrace();85 }86 return null;87 }88 /*

    89 加密数据90 */

    91 public staticString encodeData(PublicKey publicKey,String originData){92 try{93 Cipher cipher = Cipher.getInstance("RSA");94 cipher.init(Cipher.ENCRYPT_MODE,publicKey);95 byte[] bytesEncrypt =cipher.doFinal(originData.getBytes());96 //Base64编码

    97 byte[] bytesEncryptBase64 =Base64.getEncoder().encode(bytesEncrypt);98 return newString(bytesEncryptBase64);99 } catch(NoSuchAlgorithmException e) {100 e.printStackTrace();101 } catch(NoSuchPaddingException e) {102 e.printStackTrace();103 } catch(InvalidKeyException e) {104 e.printStackTrace();105 } catch(BadPaddingException e) {106 e.printStackTrace();107 } catch(IllegalBlockSizeException e) {108 e.printStackTrace();109 }110 return null;111 }112 /*

    113 解密数据114 */

    115 public staticString decodeData(PrivateKey privateKey,String encodeData){116 try{117 //Base64解码

    118 byte[] bytesEncrypt =Base64.getDecoder().decode(encodeData);119 //加密

    120 Cipher cipher = Cipher.getInstance("RSA");121 cipher.init(Cipher.DECRYPT_MODE,privateKey);122 byte[] bytesDecrypt =cipher.doFinal(bytesEncrypt);123 return newString(bytesDecrypt);124 } catch(NoSuchAlgorithmException e) {125 e.printStackTrace();126 } catch(NoSuchPaddingException e) {127 e.printStackTrace();128 } catch(InvalidKeyException e) {129 e.printStackTrace();130 } catch(BadPaddingException e) {131 e.printStackTrace();132 } catch(IllegalBlockSizeException e) {133 e.printStackTrace();134 }135 return null;136 }137 }

    这样的话,以后需要使用公钥和私钥,以及加密解密时,调用上述工具类中的相应方法即可。

    最后,我们再来看用RSA如何对数据进行加签验签,具体代码如下:

    1 import java.io.UnsupportedEncodingException;2 import java.security.*;3 /**4 * 加签 验签 签名验证:验证数据的合法来源 即验证数据来源的合法性5 * 加签:私钥6 * 验签:公钥7 */

    8 public classTest2 {9 private static String privateKeyPath="储存私钥信息的文件路径";10 private static String publicKeyPath="储存公钥信息的文件路径";11 public static voidmain(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, SignatureException {12 String data="验证该数据是否为合法的服务器发送";13 /**14 * 加签过程15 */

    16 PrivateKey privateKey =RSAUtil.getPrivateKey(privateKeyPath);17 Signature signature = Signature.getInstance("Sha1WithRSA");18 signature.initSign(privateKey);19 signature.update(data.getBytes("UTF-8"));20 byte[] signed =signature.sign();21 /**22 * 验签过程23 */

    24 PublicKey publicKey =RSAUtil.getPublicKey(publicKeyPath);25 Signature signature2 = Signature.getInstance("Sha1WithRSA");26 signature2.initVerify(publicKey);27 signature2.update(data.getBytes("UTF-8"));28 boolean verify =signature2.verify(signed);29 System.out.println("验签结果:"+verify);30 }31 }

    展开全文
  • 1.申请测试环境 2.修改179report.cs最下面的参数: public static CustomsReportSetting Current = new CustomsReportSetting() { CertNo = "", CertFilePath = "/cert/cert.cer", CertPassword = "", ...
  • 程序员必备基础:加签验签

    千次阅读 多人点赞 2020-08-19 18:01:00
    来源:捡田螺的小男孩我们在求职面试中,经常会被问到,如何设计一个安全对外的接口呢? 其实可以回答这一点,加签验签,这将让你的接口更加有安全。接下来,本文将和大家一起来学习加签验签。从...
  • 下面小编就为大家带来一篇Java Http接口加签验签操作方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 必备基础:加签验签

    2022-04-23 22:15:50
    密码学相关概念、加签验签概念、为什么需要加签/验签、加密算法简介、加签验签相关API、加签验签代码实现
  • 加密解密与加签验签

    千次阅读 2020-06-09 10:32:51
    加签验签开始前,收发双方会提前交换彼此公钥。 加签:发送方用一个哈希函数从报文文本中生成报文摘要(一般使用MD5算法:不可逆的加密算法),然后用自己的私人密钥对这个摘要进行加密,得到的就是这个报文对应的...
  • java软加签验签(二代银行系统),不买签名服务器,节省点钱,但是也多点风险。
  • HuTool 加签验签

    2021-08-07 15:24:01
    http://www.mianshigee.com/tutorial/hutool/ddd3d1548d256455.md https://www.jianshu.com/p/e051944eab8f
  • rsa加签验签

    2018-03-08 15:07:27
    因为工作需要,在项目中使用rsa验签,所以在网上找了找,因为要和java互通,所以网上找的都不能用,所以就自己改了...所有加签、加密返回结果都是base64的。 http://blog.csdn.net/u013608482/article/details/79484283
  • java加签验签demo

    2022-02-17 11:33:41
    java加签验签demoSignUtils工具类使用main总结 SignUtils工具类 import com.alipay.api.internal.util.StreamUtil; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils;...
  • RSA加解解密加签验签

    2022-06-09 13:51:53
    亲自总结验证,源码可用,RSA加密、解密、加签验签,提醒:私钥加签公租验签。其中一对测试的公钥和私钥,密码在资源说明文件中,还包括java生成公私钥的方法,后面会上传C#生成租钥pfx和证书的方法和操作说明。
  • 加签验签学习笔记

    2020-07-03 23:35:39
    1.加签验签一般是发送方和接收方交互时使用的,如果你是发送方(请求方),一定要让接收方提供加签方法(如果是证书加签就让他提供证书。除非是开源的加密方法,比如md5,aes),最好有个demo(第一次联调献给了银行,一个...
  • 浅谈 加签验签 概念

    千次阅读 2021-09-29 15:31:13
    密码学相关概念 加签验签概念 为什么需要加签、验签 加密算法简介 加签验签相关API 加签验签代码实现
  • 招行一网通支付 加签验签工具类 提供给做支付的小伙伴
  • Rsa加签验签工具

    2021-08-12 16:04:47
    Rsa加签验签工具 为了方便对外接口加签验签,写了个通用的加签验签工具。 一、上代码 1.1 RsaUtils 代码 PRIVATE_KEY、PUBLIC_KEY 可以在线生成或者通过支付宝工具生成。 在线生成公钥私钥对,RSA公私钥生成 import ...
  • 3,验证签名目前使用的UTF8格式,加签验签必须保持统一,私钥加密,公钥解密。 4,静态库分release版本和debug版本 5,注意VC6.0中编译的使用的环境 MDd 还是MD 工程环境和静态库环境一直 6,本工程使用的库为:...
  • Python3.7 基于 pycryptodome 的AES加密解密、RSA加密解密、加签验签,具体代码如下所示: #!/usr/bin/env python # -*- coding: utf8 -*- import os import rsa import json import hashlib import base64 from ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,510
精华内容 1,404
关键字:

加签验签

友情链接: MPU6050.zip