java实现RSA加密、解密、签名

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: java实现RSA加密、解密、签名

一、RSA简介


 RSA加密是一种非对称加密。可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对一极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。


二、加密和签名的区别


加密和签名都是为 了安全性考虑的,简单的说,加密是为了防止消息泄露,而签名是为了防止消息被篡改。



20190504213734218.png


如上图所示,A系统需要发送信息到B系统,这个过程需要采用RSA非对称加密算法防止信息泄漏和 篡改。首先A、B首先都需要获得各自的一对秘钥(公钥和私钥),然后具体的操作过程如上图文字。


如果我们在实际应用中如果只使用加密和签名中的一种可能带来不同的问题。如果我们在消息传递的过程中只对数据加密不进行签名,这样虽然可保证被截获的消息 不会泄露,但是可以利用截获的公钥,将假信息进行加密,然后传递到B。如果只对信息签名,这样虽然可以防止消息被篡改,但是不能防止消息泄露,所以我们在实际项目中需要根据实际需求进行使用。


总结:公钥加密、私钥解密、私钥签名、公钥验签。

 

三、java代码工具类


3.1 RSA工具类RSAUtil


package com.jack.common.utils;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
/**
 * <p>
 * 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密,<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据的安全
 * </p>
 * 
 */
public class RSAUtil {
  /**
   * 加密算法RSA
   */
  public static final String KEY_ALGORITHM = "RSA";
  private static final int MAX_ENCRYPT_BLOCK = 117;
  private static final int MAX_DECRYPT_BLOCK = 128;
  /**
   * method will close inputSteam
   * 
   * @param pemFileInputStream
   * @return
   */
  public static PublicKey loadPublicKey(InputStream pemFileInputStream) {
    return readPublicKey(readPEMFile(pemFileInputStream));
  }
  /**
   * method will close inputSteam
   * 
   * @param pkcs8PemFileInputStream
   * @return
   */
  public static PrivateKey loadPrivateKey(InputStream pkcs8PemFileInputStream) {
    return readPrivateKey(readPEMFile(pkcs8PemFileInputStream));
  }
  /**
   * 
   * @param pemFile
   * @return
   */
  public static PublicKey loadPublicKey(String pemFile) {
    return readPublicKey(readPEMFile(pemFile));
  }
  /**
   * 
   * @param pkcs8PemFile
   * @return
   */
  public static PrivateKey loadPrivateKey(String pkcs8PemFile) {
    return readPrivateKey(readPEMFile(pkcs8PemFile));
  }
  /**
   * read pem file, delete first and last line, sth. like:<br />
   * <p>
   * -----BEGIN PUBLIC KEY----- -----END PUBLIC KEY-----
   * </p>
   * 
   * @param filename
   * @return
   */
  public static String readPEMFile(String filename) {
    try {
      return readPEMFile(new FileInputStream(filename));
    } catch (FileNotFoundException e) {
      throw new RuntimeException(e);
    }
  }
  /**
   * method will close inputSteam
   * 
   * @param stream
   *            pem file inputstream
   * @return
   */
  public static String readPEMFile(InputStream stream) {
    if (null != stream) {
      BufferedReader in = null;
      StringBuilder ret = new StringBuilder();
      String line;
      try {
        in = new BufferedReader(new InputStreamReader(stream, "ASCII"));
        line = in.readLine();
        while (null != line) {
          if (!(line.startsWith("-----BEGIN ") || line.startsWith("-----END "))) {
            ret.append(line);
            ret.append("\n");
          }
          line = in.readLine();
        }
        return ret.toString();
      } catch (Exception ex) {
        throw new RuntimeException(ex);
      } finally {
        try {
          stream.close();
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        if (null != in) {
          try {
            in.close();
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        }
      }
    }
    return null;
  }
  /**
   * 
   * @param pkcs8Base64String
   *            <p>
   *            delete the first and last line, sth. like below: -----BEGIN
   *            PRIVATE KEY----- -----END PRIVATE KEY-----
   *            </p>
   * @return
   */
  public static PrivateKey readPrivateKey(String pkcs8Base64String) {
    byte[] keyByte = Base64Util.decode(pkcs8Base64String);
    try {
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyByte);
      RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
      return privateKey;
    } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
      throw new RuntimeException(ex);
    }
  }
  public static PublicKey readPublicKey(String pkcs8Base64String) {
    byte[] keyByte = Base64Util.decode(pkcs8Base64String);
    try {
      X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyByte);
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      PublicKey publicKey = (PublicKey) keyFactory.generatePublic(x509KeySpec);
      return publicKey;
    } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
      throw new RuntimeException(ex);
    }
  }
  /**
   * <P>
   * 私钥解密
   * </p>
   * 
   * @param encryptedData
   *            已加密数据
   * @param privateKey
   * @return
   */
  public static byte[] decryptByPrivateKey(byte[] encryptedData, PrivateKey privateKey) {
    try {
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      return cipher.doFinal(encryptedData);
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
  }
  /**
   * 公钥加密
   * 
   * @param content
   *            待加密内容
   * @param publicKey
   *            公钥
   * @param charset
   *            content的字符集,如UTF-8, GBK, GB2312
   * @return 密文内容 base64 ASCII
   */
  public static String encryptByPublicKey(String content, PublicKey publicKey, String charset) {
    try {
      Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
      cipher.init(Cipher.ENCRYPT_MODE, publicKey);
      byte[] data = (charset == null || charset.isEmpty()) ? content.getBytes() : content.getBytes(charset);
      int inputLen = data.length;
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      int offSet = 0;
      byte[] cache;
      int i = 0;
      // 对数据分段加密
      while (inputLen - offSet > 0) {
        if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
          cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
        } else {
          cache = cipher.doFinal(data, offSet, inputLen - offSet);
        }
        out.write(cache, 0, cache.length);
        i++;
        offSet = i * MAX_ENCRYPT_BLOCK;
      }
      byte[] encryptedData = Base64Util.encode(out.toByteArray());
      out.close();
      return new String(encryptedData, "ASCII");
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  /**
   * 私钥加密
   * 
   * @param content
   *            待加密内容
   * @param privateKey
   *            私钥
   * @param charset
   *            content的字符集,如UTF-8, GBK, GB2312
   * @return 密文内容 base64 ASCII
   */
  public static String encryptByPrivateKey(String content, PrivateKey privateKey, String charset) {
    try {
      Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
      cipher.init(Cipher.ENCRYPT_MODE, privateKey);
      byte[] data = (charset == null || charset.isEmpty()) ? content.getBytes() : content.getBytes(charset);
      int inputLen = data.length;
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      int offSet = 0;
      byte[] cache;
      int i = 0;
      // 对数据分段加密
      while (inputLen - offSet > 0) {
        if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
          cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
        } else {
          cache = cipher.doFinal(data, offSet, inputLen - offSet);
        }
        out.write(cache, 0, cache.length);
        i++;
        offSet = i * MAX_ENCRYPT_BLOCK;
      }
      byte[] encryptedData = Base64Util.encode(out.toByteArray());
      out.close();
      return new String(encryptedData, "ASCII");
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  /**
   * 私钥解密
   * 
   * @param content
   *            待解密内容(base64, ASCII)
   * @param privateKey
   *            私钥
   * @param charset
   *            加密前字符的字符集,如UTF-8, GBK, GB2312
   * @return 明文内容
   * @return
   */
  public static String decryptByPrivateKey(String content, PrivateKey privateKey, String charset) {
    try {
      Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      byte[] encryptedData = Base64Util.decode(content);
      int inputLen = encryptedData.length;
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      int offSet = 0;
      byte[] cache;
      int i = 0;
      // 对数据分段解密
      while (inputLen - offSet > 0) {
        if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
          cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
        } else {
          cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
        }
        out.write(cache, 0, cache.length);
        i++;
        offSet = i * MAX_DECRYPT_BLOCK;
      }
      byte[] decryptedData = out.toByteArray();
      out.close();
      return (charset == null || charset.isEmpty()) ? new String(decryptedData)
          : new String(decryptedData, charset);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  /**
   * 公钥解密
   * 
   * @param content
   *            待解密内容(base64, ASCII)
   * @param publicKey
   *            公钥
   * @param charset
   *            加密前字符的字符集,如UTF-8, GBK, GB2312
   * @return 明文内容
   * @return
   */
  public static String decryptByPublicKey(String content, PublicKey publicKey, String charset) {
    try {
      Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
      cipher.init(Cipher.DECRYPT_MODE, publicKey);
      byte[] encryptedData = Base64Util.decode(content);
      int inputLen = encryptedData.length;
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      int offSet = 0;
      byte[] cache;
      int i = 0;
      // 对数据分段解密
      while (inputLen - offSet > 0) {
        if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
          cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
        } else {
          cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
        }
        out.write(cache, 0, cache.length);
        i++;
        offSet = i * MAX_DECRYPT_BLOCK;
      }
      byte[] decryptedData = out.toByteArray();
      out.close();
      return (charset == null || charset.isEmpty()) ? new String(decryptedData)
          : new String(decryptedData, charset);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  /**
   * 
   * @param signType 如:SHA1withRSA
   * @param data
   * @param publicKey
   * @param sign base64字符串
   * @return
   * @throws Exception
   */
  public static boolean verifySign(String signType, byte[] data, PublicKey publicKey, String sign) throws Exception {
    Signature signature = Signature.getInstance(signType);
    signature.initVerify(publicKey);
    signature.update(data);
    return signature.verify(Base64Util.decode(sign));
  }
  /**
   * 
   * @param signType 如:SHA1withRSA
   * @param data
   * @param privateKey
   * @return base64字符串
   * @throws Exception
   */
  public static String sign(String signType, byte[] data, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(signType);
        signature.initSign(privateKey);
        signature.update(data);
        return new String(Base64Util.encode(signature.sign()));
    }
}

3.2 获得公钥和私钥的工具类ClientUtil

package com.jack.common.utils;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
public class ClientUtil {
    public static final Map<String,String> keyMap = new HashMap<String,String>();
    public static PrivateKey getPrivateKay(String fileTwo){
        String privateKey = keyMap.get("privateKay");
        if(privateKey == null || "".equals(privateKey)){
            privateKey = RSAUtil.readPEMFile(fileTwo);
            keyMap.put("privateKay",privateKey);
        }
        PrivateKey privateKeyTwo = RSAUtil.readPrivateKey(privateKey);
        return privateKeyTwo;
    }
    public static PublicKey getPublicKey(String fileTwo,String orgCode){
        String publicKey = keyMap.get(orgCode);
        if(publicKey == null || "".equals(publicKey)){
            publicKey = RSAUtil.readPEMFile(fileTwo);
            keyMap.put(orgCode,publicKey);
        }
        PublicKey publicKeyTwo = RSAUtil.readPublicKey(publicKey);
        return publicKeyTwo;
    }
}


3.3 添加签名方法

package com.jack.urgerobot.rsa.service;
import com.jack.common.utils.ClientUtil;
import com.jack.common.utils.RSAUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.security.PrivateKey;
import java.security.PublicKey;
@Service
public class SignCommonService {
    private String signType = "SHA1WithRSA";
    public String sign( String content) throws Exception{
        PrivateKey privateKeyTwo = null;
        String fileTwo = this.getClass().getClassLoader().getResource("rsa/UserPri.key").getPath();//我们的私钥文件
        privateKeyTwo = ClientUtil.getPrivateKay(fileTwo);
        String sign = RSAUtil.sign(signType, content.getBytes("utf-8"), privateKeyTwo);
        return sign;
    }
}

注释:秘钥存放位置为项目的resources/rsa/下面


20190505101049244.png


3.4 客户端测试发送数据 加密 签名


package com.jack.urgerobot.rsa.service;
import com.aliyun.oss.common.utils.HttpUtil;
import com.jack.common.utils.ClientUtil;
import com.jack.common.utils.HttpUtils;
import com.jack.common.utils.RSAUtil;
import org.apache.ibatis.javassist.bytecode.stackmap.BasicBlock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.security.PublicKey;
import java.util.HashMap;
/**
 * @author zhenghao
 * @description
 * @date 2019/4/30 11:41
 */
@Service
public class testService {
    @Autowired
    private SignCommonService signCommonService;
    public void testScreit(){
        try {
            String reqData = "{\n" +
                    "\"caseCode\":\""+"12345"+"\",\n" +
                    "\"billNo\":\""+"7687878"+"\",\n" +
                    "}";
            HashMap<String, String> pars = new HashMap<String, String>();
            String fileTwo = this.getClass().getClassLoader().getResource("rsa/userPublic.key").getPath();//客户公钥
            PublicKey publicKeyTwo = ClientUtil.getPublicKey(fileTwo, "mbt");
            String entryContent = RSAUtil.encryptByPublicKey(reqData, publicKeyTwo, "UTF-8");
            pars.put("serviceCode", "mbt");
            pars.put("reqData", entryContent);
            pars.put("sign", signCommonService.sign(reqData));
            HashMap<String,String> headerMap = new HashMap<>();
            headerMap.put("Content-uid", "1");
            headerMap.put("Content-pwkey", "1");
            String r = HttpUtils.postWithHeaders("http://localhost:8090/sign/testSign", headerMap, pars);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.5 接收端 解密 验签

package com.jack.marketing.rsa.controller;
import com.jack.common.utils.ServletUtils;
import com.jack.marketing.rsa.service.RsaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
 * @author zhenghao
 * @description
 * @date 2019/4/30 16:38
 */
@Controller
@RequestMapping("/sign")
public class RsaController {
    @Autowired
    private RsaService rsaService;
    @RequestMapping("/testSign")
    public void addPartner(HttpServletRequest req, HttpServletResponse res,String orgCode, String sign, String reqData){
        Map map = new HashMap<>();
        map.put("orgCode", orgCode);
        map.put("sign", sign);
        map.put("reqData", reqData);
        String reqJson = rsaService.testSign(orgCode, sign, reqData); //获取传入的签名
        ServletUtils.toJson(reqJson,req,res);
    }
}
package com.jack.marketing.rsa.service;
import com.jack.common.exception.ToUserException;
import com.jack.common.utils.ClientUtil;
import com.jack.common.utils.RSAUtil;
import org.springframework.stereotype.Service;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
/**
 * @author zhenghao
 * @description
 * @date 2019/4/30 15:44
 */
@Service
public class RsaService {
    private static String signType = "SHA1WithRSA";
    private static PrivateKey privateKeyTwo = null;
    private static HashMap<String, PublicKey> publicKeyHashMap = new HashMap<String, PublicKey>();
    public String testSign(String orgCode, String sign, String reqData){
        String reqJson = null;
        System.out.println(orgCode + sign + reqData);
        boolean signOK = false;
        //自己私钥解密
        if (privateKeyTwo == null) {
            String fileTwo = this.getClass().getClassLoader().getResource("rsa/userPri.key").getPath();//我们的私钥文件
            privateKeyTwo = ClientUtil.getPrivateKay(fileTwo);
        }
        reqJson = RSAUtil.decryptByPrivateKey(reqData, privateKeyTwo, "utf-8");
        PublicKey publicKeyTwo = publicKeyHashMap.get(orgCode);
        if (publicKeyTwo == null) {
            try {
                String pubfileTwo = this.getClass().getClassLoader().getResource("rsa/userPublic.key").getPath();//服务方的公钥文件
                publicKeyTwo = ClientUtil.getPublicKey(pubfileTwo, orgCode);
                publicKeyHashMap.put(orgCode, publicKeyTwo);
            } catch (Exception e) {
                throw new ToUserException("上传公钥文件");
            }
        }
        //对方公钥验签
        try {
            signOK = RSAUtil.verifySign(signType, reqJson.getBytes("utf-8"), publicKeyTwo, sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (signOK) {
            return reqJson;
        } else {
            throw new ToUserException("验证签名失败");
        }
    }
}

上面的代码已经通过测试,可以直接使用


四 小结


关于RSA的使用,之前在软考的时候了解过,后来在项目中也没有负责写过相关的模块,最近刚好项目中需要调用第三方提供的黑名单查询接口,看了下对方的文档需要使用RSA进行加密和签名,所以查了查相关的资料,将这块的东西写完了,在这总结下,在以后的项目中可以直接使用。

目录
相关文章
|
2月前
|
安全 算法 网络安全
浅谈非对称加密(RSA)
浅谈非对称加密(RSA)
|
27天前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
41 2
|
1月前
|
算法 安全 Go
RSA加密算法详解与Python和Go实现
RSA加密算法详解与Python和Go实现
94 1
|
1月前
|
算法 安全 网络安全
使用 Python 实现 RSA 加密
使用 Python 实现 RSA 加密
52 2
|
1月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
126 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
2月前
|
存储 安全 算法
RSA在手,安全我有!Python加密解密技术,让你的数据密码坚不可摧
【9月更文挑战第11天】在数字化时代,信息安全至关重要。传统的加密方法已难以应对日益复杂的网络攻击。RSA加密算法凭借其强大的安全性和广泛的应用场景,成为保护敏感数据的首选。本文介绍RSA的基本原理及在Python中的实现方法,并探讨其优势与挑战。通过使用PyCryptodome库,我们展示了RSA加密解密的完整流程,帮助读者理解如何利用RSA为数据提供安全保障。
121 5
|
2月前
|
安全 算法 数据安全/隐私保护
深度揭秘!Python加密技术的背后,AES与RSA如何守护你的数据安全
【9月更文挑战第10天】随着数字化时代的到来,数据安全成为企业和个人面临的重大挑战。Python 作为功能强大的编程语言,在数据加密领域扮演着重要角色。AES 和 RSA 是两种主流加密算法,分别以对称和非对称加密方式保障数据安全。AES(Advanced Encryption Standard)因其高效性和安全性,在数据加密中广泛应用;而 RSA 则利用公钥和私钥机制,在密钥交换和数字签名方面表现卓越。
81 3
|
2月前
|
存储 安全 数据库
双重防护,无懈可击!Python AES+RSA加密方案,构建最强数据安全堡垒
【9月更文挑战第11天】在数字时代,数据安全至关重要。AES与RSA加密技术相结合,构成了一道坚固防线。AES以其高效性保障数据加密,而RSA则确保密钥安全传输,二者相辅相成,提供双重保护。本文通过Python代码示例展示了这一加密方案的魅力,强调了其在实际应用中的重要性和安全性。使用HTTPS等安全协议传输加密密钥和密文,确保数据在数字世界中自由流通而无忧。
60 1
|
2月前
|
安全 Java 数据安全/隐私保护
- 代码加密混淆工具-Java 编程安全性
在Java编程领域,保护代码安全与知识产权至关重要。本文探讨了代码加密混淆工具的重要性,并介绍了五款流行工具:ProGuard、DexGuard、Jscrambler、DashO 和 Ipa Guard。这些工具通过压缩、优化、混淆和加密等手段,提升代码安全性,保护知识产权。ProGuard 是开源工具,用于压缩和混淆Java代码;DexGuard 专为Android应用程序设计,提供强大加密功能;Jscrambler 基于云,保护Web和移动应用的JavaScript及HTML5代码;DashO 支持多种Java平台和
204 1
|
2月前
|
算法 安全 网络安全
概念区分:对称加密、非对称加密、公钥、私钥、签名、证书
概念区分:对称加密、非对称加密、公钥、私钥、签名、证书
106 0