首页 > 编程语言 >Java MD5与RSA加密使用

Java MD5与RSA加密使用

时间:2023-04-23 15:23:24浏览次数:39  
标签:公钥 加密 String RSA Java 私钥 MD5

Java MD5与RSA加密使用

转发数据到广州,那边要求 HTTP 请求的头部需要用 MD5 签名,请求体数据需要使用 RSA 加密,研究了一下。

MD5

MD5(Message Digest Algorithm 5)是一种广泛使用的加密哈希函数,可将任意长度的消息转换为128位的哈希值(通常以32个十六进制字符表示)。MD5 算法是一种单向加密算法,也就是说只能进行加密,无法解密。因此,它常用于密码的加密存储和数字签名验证等场景。

MD5 算法的主要原理是通过多次对输入数据进行压缩、迭代和变换,最终生成一个哈希值。MD5 算法的具体实现包括以下几个步骤:

  1. 填充消息:首先需要对消息进行填充,使其长度满足一个特定的条件(例如长度为64的倍数),以便于后续的处理。
  2. 初始值设置:设置4个32位的寄存器(A、B、C、D)的初始值,作为哈希值的组成部分。
  3. 消息分块:将填充后的消息按照长度为512位的块进行分组,每组包含16个32位的字。
  4. 循环迭代:对每个消息块进行循环迭代,共进行64轮,每轮都包含4个变换步骤。
  5. 输出结果:最后将4个寄存器的值连接起来,形成128位的哈希值。

MD5 算法被广泛应用于互联网安全领域,例如密码加密、数字签名、消息认证等。但是,由于 MD5 算法存在一些漏洞,例如碰撞攻击,因此在一些安全性要求更高的场合,如用户密码存储,建议使用更为安全的加密算法,例如 SHA-2 系列的哈希函数。

彩虹表攻击:彩虹表攻击是一种密码破解方法,通常用于破解哈希函数加密的密码。彩虹表是一种预先计算好的表,其中包含了大量可能的明文和它们对应的哈希值。攻击者使用彩虹表可以快速地反推出哈希值对应的明文密码。这种攻击方法相对于暴力破解等其他破解方法来说速度非常快,因为攻击者不需要每次都重新计算哈希值,而是直接在预先计算好的彩虹表中查找匹配。

简单来说,MD5 加密是单向的,是没法从加密后的密文解密得到明文的。因此主要的应用就是密码的密文保存、对比数据是否被篡改。

MD5Util

MD5 加密的应用比较简单,放一个 MD5Util 看一下:

public class MD5Util {

    public static String encipherHeader(String loginName, String loginPwd, String timestamp) {

        //String timestampStr = Long.toString(timestamp);
        StringBuffer sb = new StringBuffer();
        sb.append("appkey=");
        sb.append(loginName);
        sb.append(",secret=RSA,signt=");
        sb.append(timestamp);
        sb.append(",secretKey=");
        sb.append(loginPwd);
        String header = sb.toString();

        String md5Header = md5Encipher(header);

        return md5Header;
    }

    public static String md5Encipher(String str){

        String md5Str = null;
        StringBuffer sb = new StringBuffer();

        try {
            // 创建 MessageDigest 对象并指定算法为 MD5
            MessageDigest md = MessageDigest.getInstance("MD5");

            // 将字符串转换为字节数组并进行加密
            byte[] md5Bytes = md.digest(str.getBytes());

            // 将字节数组转换为字符串
            for (byte b : md5Bytes) {
                sb.append(String.format("%02x", b));
            }
            md5Str = sb.toString();

            System.out.println("MD5 加密前原始数据:" + str);
            System.out.println("MD5 加密结果:" + md5Str);

        } catch (NoSuchAlgorithmException e) {
            System.err.println("不支持的加密算法:MD5");
            e.printStackTrace();
        }
        return md5Str;
    }

}

其中 md5Encipher 是对字符串进行加密的方法,headerEncipher 是我自己要用的对 HTTPHeader 加密的方法。

测试

写一个 Main 方法测试一下加密:

    public static void main(String[] args) {
        // 接收对方传来的 MD5 加密后的密文
        String pwd = "4d7ad62f7d74f7defd845740661f53e2";

        // 根据对方传来的数据计算 MD5 加密的结果
        String encipher = MD5Util.md5Encipher("appkey=1234567890,secret=RSA,signt=1637109703238,secretKey=abcdefghijklmnopqrstuvwxyz");
        System.out.println("加密结果:" + encipher);
        if(pwd.equals(encipher)){
            // 两次结果一致,数据没有被篡改/签名有效
            System.out.println("数据相等");
        }else {
            // 两次结果不一致,数据被篡改了/签名无效
            System.out.println("数据不相等");
        }
    }

直接写在工具类里,运行,控制台输出:

MD5 加密前原始数据:appkey=1234567890,secret=RSA,signt=1637109703238,secretKey=abcdefghijklmnopqrstuvwxyz
MD5 加密结果:4d7ad62f7d74f7defd845740661f53e2
加密结果:4d7ad62f7d74f7defd845740661f53e2
==========数据相等==========

进程已结束,退出代码0

验证成功,没什么问题。

RSA

RSA是一种公钥加密算法,其加密和解密过程都需要用到两个密钥,一个是公钥,另一个是私钥。公钥可以自由发布,私钥则是保密的。

RSA的安全性基于大数分解难题,也就是说,对于一个足够大的整数,要找到它的所有质因数是非常困难的。RSA算法的核心是选择两个大质数p和q,然后计算它们的乘积N=p * q。然后根据一定的算法计算出一个公钥和一个私钥。

公钥包含两个参数N和e,私钥包含两个参数N和d。其中,N为大质数p和q的乘积,e是一个小于N的正整数,且e与(p-1) * (q-1)互质,d是e的模逆元,满足 (e * d) % ((p-1) * (q-1)) = 1。

RSA加密过程如下:

  1. 接收方生成一对密钥,其中一个是公钥,公开给发送方;另一个是私钥,保存在自己手中。
  2. 发送方获取接收方的公钥,将明文进行加密后发送。
  3. 接收方接收到密文后,使用自己的私钥进行解密,得到明文。

RSA算法的优点是安全性高,加密速度较快,可以用于数据的加密、数字签名等方面。缺点是加密和解密的速度较慢,且加密的数据长度有限制。

RSA 加密在网上找了找都是讲 RSA 算法的,或者应用起来比较复杂。所以搞个简单的工具类,万一以后还要用到就可以直接用了。

RSAUtil

也是放一个 RSAUtil,其中包括了生成公钥私钥、使用公钥加密、使用私钥解密的方法:

public class RSAUtil {

    //密钥长度
    private final static int KEY_SIZE = 1024;

    //保存产生的公钥与私钥
    private static Map<String, String> keyMap = new HashMap<String, String>();

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception
     */
    public static String encrypt(String str, String publicKey) throws Exception {
        // 将公钥解码为 base64
        byte[] decoded = decryptBASE64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        // RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = encryptBASE64(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 明文
     * @throws Exception
     */
    public static String decrypt(String str, String privateKey) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = decryptBASE64(str);
        //base64编码的私钥
        byte[] decoded = decryptBASE64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }

    // 编码返回字符串
    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    // 解码返回byte
    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    /**
     * 随机生成密钥对
     * @throws Exception
     */
    public static void generateKeyPair() throws Exception {
        // KeyPairGenerator 基于RSA算法,用于生成一对公钥和私钥
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();

        // 获取公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        // 获取公钥字符串
        String publicKeyString = encryptBASE64(publicKey.getEncoded());
        // 得到私钥字符串
        String privateKeyString = encryptBASE64(privateKey.getEncoded());

        // 保存公钥和私钥
        keyMap.put("pubKey", publicKeyString);
        keyMap.put("privateKey", privateKeyString);
    }

}

其中用到了 common-codec 的依赖:

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.15</version>
        </dependency>

测试

写一个 Main 方法测试一下获取密钥和加密解密的过程:

    public static void main(String[] args) {
        try {
            // 生成公钥和私钥
            generateKeyPair();
            String publicKey = keyMap.get("pubKey");
            System.out.println("公钥:" + publicKey);
            String privateKey = keyMap.get("privateKey");
            System.out.println("私钥:" + privateKey);

            String orgData = "然而也应当记住黑暗的时日";
            System.out.println("原始数据:" + orgData);

            String encryptStr = encrypt(orgData,publicKey);
            System.out.println("加密数据:" + encryptStr);

            String decryptStr = decrypt(encryptStr,privateKey);
            System.out.println("解密数据:" + decryptStr);

            if(orgData.equals(decryptStr)){
                System.out.println("==========原始数据与解密数据一致==========");
            }else {
                System.out.println("==========原始数据与解密数据不一致==========");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

也是直接在工具类里写的,运行,控制台输出:

公钥:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/+dP4fJbr4EjhLxjUMcI7zOpnOES5VBnM03Uf
w5yuPglwc2UVeV0B12AC3GIwTZ/94Ohf0+a3g9ecx6CVEzyYeuIfnOjXhM7waVZdR8LcbwsN8kgX
92lpc2qNSquMRU57Wt/cxSoarBXyh1DcCRxzQFACEc04voeVRHzuB6QKZwIDAQAB

私钥:MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL/50/h8luvgSOEvGNQxwjvM6mc4
RLlUGczTdR/DnK4+CXBzZRV5XQHXYALcYjBNn/3g6F/T5reD15zHoJUTPJh64h+c6NeEzvBpVl1H
wtxvCw3ySBf3aWlzao1Kq4xFTnta39zFKhqsFfKHUNwJHHNAUAIRzTi+h5VEfO4HpApnAgMBAAEC
gYBLBwt1yNN++hfhkfOFMrEzh+FwV8hcGec/asESmfOJEYvE3AR8gQL9bjwCwjjJofzOTvDiSsGX
pTpF9qrmuC7sxqKN+xVdAJzoTIzUSXd/Zd/RLhxDWHLSppyJ5c1n1pikDKrAkUqfWnp2gjMUT4Pd
pU4WH/W7IFV9H0o+AOCbcQJBAPV/KN871ydNkwQL8+zu2L3iXTHMWKmEv40AXI+vh9c5iS08fiJi
VL3TQjC/W8SQ4Emj2lF48XSIdTO7gPFF7I8CQQDIMHkrFjeWYyk6QuCC5mV9F95RyQDk0yvrcLtg
aEvA9wZVv/FcZOq7I2ejcYWt1wi99sXpFwdZ0oiI3GZFByCpAkEAo+RIfP+OG4cGZuUz6zFpMRs1
7FDnwAQHfTKImMQug9i9Y53G911+BVxMDA80TH4Lvh3NWibLy2huFiNPacOssQJAU2fmw+3gwRaV
ccG1WrR1alYMeZS+e5gD/3cbioJJtZ72E7oB7JXbOpb4sh81LAWgjc0IDiJbHLBb1HHHZlEe6QJB
ANzDR19vzmit+miwFkNbVfCPSFX0oKbWZaNfkJ+zmg4scCDppny4S2pxZQ+zyOGATXUlJH3YvHd2
lqDpIbXxong=

原始数据:然而也应当记住黑暗的时日
加密数据:fi//U40WewohsDAva8u0j71pzwOsIoCAxpR1aTvNr7HnU3pmU1MmePonePePab0cZBwIvl/3TQyV
GLduPS2XOdIIqKrT28bXLL02f7UXkFCICrD/JiotClG6gbKOtEKCpgOIKKoLEXA7RPDLzw7QFSjz
8m9z4s3/uHjrCcaaTwY=

解密数据:然而也应当记住黑暗的时日
==========原始数据与解密数据一致==========

进程已结束,退出代码0

没什么问题,加解密能够应用就行了,其中的原理不必深究了。。。

标签:公钥,加密,String,RSA,Java,私钥,MD5
From: https://www.cnblogs.com/qiyuanc/p/Back11.html

相关文章

  • Adversarial Robust Deep Reinforcement Learning Requires Redefining Robustness
    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! ......
  • Java Assert断言使用
    目录断言所谓的assertion,是jdk1.4后加入的新功能。作用它主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出。后续当软件正式发布后,可以取消断言部分的代码。java中使用assert作为断言的一个关键......
  • javamock生成对象
    `importjava.lang.reflect.Field;importjava.lang.reflect.ParameterizedType;importjava.util.ArrayList;importjava.util.Date;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importjava.util.Random;publicclassMock{privatesta......
  • JavaScript实现文件上传下载功能实例解析
    ​ 需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。 第一步:前端修改由于项目使用的是BJUI前端框架,并没有使用框架本身的文件上传控件,而使用的基于jQuery的Uploadify......
  • java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check th
    java.sql.SQLSyntaxErrorException:YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear''',b_5='17',b_6=''whereb_name='A'......
  • Java最佳实践
    计算机编程中,最佳实践是许多开发人员遵循的一组非正式规则,以提高软件质量、可读性和可维护性。在应用程序长时间保持使用的情况下,最佳实践尤其有益,这样它最初是由一个团队开发的,然后由不同的人组成的维护团队进行维护。本教程将提供Java最佳实践的概述,以及每个条目的解释,包括Jav......
  • Java 静态泛型方法为什么要在返回值之前使用泛型
    静态方法定义静态泛型方法要在返回值之前使用泛型声明此方法为泛型方法:/***泛型方法的基本介绍*@paramtClass传入的泛型实参*@returnT返回值为T类型*说明:*1)public与返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。......
  • jmeter中的java请求 用httpclient写的http请求 及参数化
    首先,jmeter中的sample的原理: jmeter 中的java 请求,sample 原理,java testjmeter自带的包,把包放在类路径下面,通过反射机制,通过反射机制扫出来。 先导入五个jar包  packagecom.young.testing91;importjava.io.IOException;importorg.apache.http.client.C......
  • java基础-泛型(七)
    泛型:jdk1.5版本以后出现的一个安全机制。表现格式:<>好处:1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。2:避免了强制转换的麻烦。 只要带有<>的类或者接口,都属于带有类型参数的类或者接口,在使用这些类或者接口时,必须给<>中传递一......
  • java基础-面向对象-多态(四)
    某一个事务不同的体现形态例如:人:男人、女人动物:猫、狗猫x=new猫();动物x=new猫();1、多态的体现父类的引用指向了自己的子类对象父类的引用也可以接受自己的子类对象2、多态的前提必须是类与类之间有关系,要么继承、要么实现,通常还有一个前提,存在覆盖3、多态的好处多态的出现大大......