Java实现基于国密SM2、SM4生成证书密钥进行字串的加解密

流程说明:

在这里插入图片描述

DTO层

生成证书密钥所需参数封装类


/**
 * 安全证书生成DTO
 *
 * @author luce
 * @date 2021年08月30日 15:17
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CertificateCreate {

    /**
     * 国家代码
     */
    private String countryCode;

    /**
     * 省份或州
     */
    private String province;

    /**
     * 地区
     */
    private String district;

    /**
     * 组织
     */
    private String organization;

    /**
     * 通用名称
     */
    private String commonName;

    /**
     * 证书存储路径
     */
    private String certificateStoragePath;

    /**
     * 密钥存储路径
     */
    private String secretKeyStoragePath;
}

最终密文封装类

/**
 * 密文文件
 *
 * @author luce
 * @date 2021年08月31日 14:20
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Ciphertext {
    /**
     * 签文
     */
    private String sigFile;
    /**
     * 加密密钥
     */
    private String encryptionKey;
    /**
     * 加密密文
     */
    private String encryptionFile;
    /**
     * 发送时间
     */
    private String time;
}

controller层

@RestController
public class CipherController {

    @Autowired
    private CipherService cipherService;

     /**
     * 生成证书和密钥文件
     *
     * @param create 参数类
     * @return com.hb56.common.result.RestInfo<?>
     * @author luce
     * @date 2021/9/1 10:12
     */
    @PostMapping("/cipher/certificatepath")
    public RestInfo<?> createCertificate(@RequestBody CertificateCreate create) throws Exception {
        return RestUtil.setSuccessMsg("生成成功!", cipherService.createCertificate(create));
    }

    /**
     * 读取证书
     *
     * @return com.hb56.common.result.RestInfo<?>
     * @author luce
     * @date 2021/9/1 10:12
     */
    @GetMapping("/cipher/certificate")
    public RestInfo<?> getCert() throws Exception {
        return RestUtil.setSuccessMsg("读取成功!", cipherService.getCert());
    }

    /**
     * 生成密文:path
     *
     * @param str                报文
     * @param certFilePath       接收方证书文件路径
     * @param privateKeyFilePath 发送方密钥文件路径
     * @return com.hb56.common.result.RestInfo<?>
     * @author luce
     * @date 2021/9/1 10:17
     */
    @PostMapping("/cipher/encryptedpath")
    public RestInfo<?> encrypted(String str, @RequestHeader("certFilePath") String certFilePath, @RequestHeader("privateKeyFilePath") String privateKeyFilePath) throws Exception {
        BCECPublicKey receivePublicKey = getPublicKeyByPath(certFilePath);
        BCECPrivateKey sendPrivateKey = getPrivateKeyByPath(privateKeyFilePath);
        return RestUtil.setSuccessMsg("生成成功!", cipherService.createCiphertext(str, receivePublicKey, sendPrivateKey));
    }


    /**
     * 生成密文
     * @author luce
     * @date 2021/9/3 10:23
     * @param map str:报文/cert:接收方证书
     * @return com.hb56.common.result.RestInfo<?>
     */
    @PostMapping("/cipher/encrypted")
    public RestInfo<?> encrypted(@RequestBody Map<String, String> map) throws Exception {
        BCECPublicKey receivePublicKey = getPublicKey(map.get("cert"));
        BCECPrivateKey sendPrivateKey = cipherService.getPrivateKey();
        return RestUtil.setSuccessMsg("生成成功!", cipherService.createCiphertext(map.get("str"), receivePublicKey, sendPrivateKey));
    }

    /**
     * 解读密文:path
     *
     * @param ciphertext         密文
     * @param certFilePath       发送方证书文件路径
     * @param privateKeyFilePath 接收方密钥文件路径
     * @return com.hb56.common.result.RestInfo<?>
     * @author luce
     * @date 2021/9/1 10:18
     */
    @PostMapping("/cipher/unscrambledpath")
    public RestInfo<?> unscrambled(@RequestBody Ciphertext ciphertext, @RequestHeader("certFilePath") String certFilePath, @RequestHeader("privateKeyFilePath") String privateKeyFilePath) throws Exception {
        BCECPublicKey sendPublicKey = getPublicKeyByPath(certFilePath);
        BCECPrivateKey receivePrivateKey = getPrivateKeyByPath(privateKeyFilePath);
        return RestUtil.setSuccessMsg("读取成功!", cipherService.unscrambledCiphertext(ciphertext, sendPublicKey, receivePrivateKey));
    }

    /**
     * 解读密文
     *
     * @param ciphertext 密文
     * @return com.hb56.common.result.RestInfo<?>
     * @author luce
     * @date 2021/9/1 10:18
     */
    @PostMapping("/cipher/unscrambled")
    public RestInfo<?> unscrambled(@RequestBody Ciphertext ciphertext) throws Exception {
        BCECPublicKey sendPublicKey = getPublicKey(ciphertext.getCert());
        BCECPrivateKey receivePrivateKey = cipherService.getPrivateKey();
        return RestUtil.setSuccessMsg("读取成功!", cipherService.unscrambledCiphertext(ciphertext, sendPublicKey, receivePrivateKey));
    }

}

service层

public interface CipherService {

    /**
     * 生成证书和密钥
     *
     * @param create
     * @return 保存路径 -String
     * @author luce
     * @date 2021/9/1 10:00
     */
    String createCertificate(CertificateCreate create) throws Exception;

    /**
     * 生成加密文件
     *
     * @param str        报文
     * @param publicKey  接收方公钥
     * @param privateKey 发送方私钥
     * @return com.hb56.cipherclient.dto.Ciphertext
     * @author luce
     * @date 2021/9/3 8:45
     */
    Ciphertext createCiphertext(String str, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception;

    /**
     * 解读密文
     * @param ciphertext 密文
     * @param publicKey  发送方公钥
     * @param privateKey 接收方私钥
     * @return 报文 -String
     * @author luce
     * @date 2021/9/3 9:53
     */
    String unscrambledCiphertext(Ciphertext ciphertext, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception;

    /**
     * 读取证书
     *
     * @return java.lang.String
     * @author luce
     * @date 2021/9/3 10:09
     */
    Object getCert() throws Exception;

    /**
     * 读取私钥
     *
     * @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
     * @author luce
     * @date 2021/9/3 13:23
     */
    BCECPrivateKey getPrivateKey() throws Exception;

serviceImp层

这层代码会比较多,不过都有注解

@Slf4j
@Service
public class CipherServiceImp implements CipherService {


    /**
     * 生成证书和密钥
     *
     * @param create
     * @return 保存路径 -String
     * @author luce
     * @date 2021/8/31 14:56
     */
    @Override
    public String createCertificate(CertificateCreate create) throws Exception {
        //创建密钥
        KeyPair keyPair = SM2Util.generateKeyPair();
        Map<String, String> names = new HashMap<>();
        // 国家代码
        names.put(BCStyle.C.getId(), create.getCountryCode());
        // 省份或州
        names.put(BCStyle.ST.getId(), create.getProvince());
        // 地区
        names.put(BCStyle.L.getId(), create.getDistrict());
        // 组织
        names.put(BCStyle.O.getId(), create.getOrganization());
        // 通用名称
        names.put(BCStyle.CN.getId(), create.getCommonName());
        X500Name x500Name = CommonUtil.buildX500Name(names);

        SM2X509CertMaker sm2X509CertMaker = new SM2X509CertMaker(keyPair, 525600000, x500Name, new RandomSNAllocator());
        PKCS10CertificationRequest pkcs10CertificationRequest = CommonUtil.createCSR(x500Name, new SM2PublicKey((BCECPublicKey) keyPair.getPublic()), keyPair.getPrivate(), SIGN_ALGO_SM3WITHSM2);
        X509Certificate x509Certificate = sm2X509CertMaker.makeRootCACert(pkcs10CertificationRequest.getEncoded());
        String certStoragePath = "";
        String secretKeyStoragePath = "";

        if (StringUtils.hasText(create.getCertificateStoragePath())) {
            certStoragePath = create.getCertificateStoragePath() + "/safe/certificate.pem";
        } else {
            certStoragePath = acquireJarPath() + "safe/certificate.pem";
        }
        if (StringUtils.hasText(create.getSecretKeyStoragePath())) {
            secretKeyStoragePath = create.getCertificateStoragePath() + "/safe/private.key";
        } else {
            secretKeyStoragePath = acquireJarPath() + "safe/private.key";
        }
        //保存证书文件
        createCert(x509Certificate, certStoragePath);
        //保存密钥文件
        createKeyPair(keyPair, secretKeyStoragePath);
        return certStoragePath + " and " + secretKeyStoragePath;
    }

    /**
     * 生成密文文件
     *
     * @param str        明文
     * @param publicKey  接收方公钥
     * @param privateKey 发送方私钥
     * @return com.hb56.cipherclient.dto.Ciphertext
     * @author luce
     * @date 2021/9/2 15:46
     */
    @Override
    public Ciphertext createCiphertext(String str, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        String localDateTime = LocalDateTime.now().toString();

        //将报文转码 Base64
        String encodeStr = Base64Encoder.encode(str.getBytes(StandardCharsets.UTF_8));
        //将时间转码 Base64
        String encodeTime = Base64Encoder.encode(localDateTime.getBytes(StandardCharsets.UTF_8));
        //生成随机密钥
        byte[] key = SM4Util.generateKey();
        //加签:用发送方的私钥
        String sigFile = sign(encodeStr, privateKey, encodeTime);
        //加密:用随机 SM4key
        String encodeCiphertext = encryptedDocument(key, encodeStr);
        //加密随机密钥:用接收方的公钥
        byte[] encryptsKey = SM2Util.encrypt(publicKey, key);
        String encodeKey = Base64Encoder.encode(encryptsKey);
        return new Ciphertext(sigFile, encodeKey, encodeCiphertext, localDateTime, null);
    }

    /**
     * 解读密文
     *
     * @param ciphertext 密文
     * @param publicKey  发送方公钥
     * @param privateKey 接收方私钥
     * @return 报文 -String
     * @author luce
     * @date 2021/9/3 9:53
     */
    @Override
    public String unscrambledCiphertext(Ciphertext ciphertext, BCECPublicKey publicKey, BCECPrivateKey privateKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        //解密
        String encodeStr = decodeDocument(ciphertext, privateKey);
        System.out.println("解密后得到的报文:" + encodeStr);
        //在报文中加入发送时间一起验签
        String signStr = encodeStr + Base64Encoder.encode(ciphertext.getTime().getBytes(StandardCharsets.UTF_8));
        System.out.println("验签内容:" + signStr);
        //验签
        boolean result = SM2Util.verify(publicKey, signStr.getBytes(StandardCharsets.UTF_8), Base64Decoder.decode(ciphertext.getSignText()));
        if (!result) {
            throw new RuntimeException("验签失败!");
        }
        return new String(Base64Decoder.decode(encodeStr), StandardCharsets.UTF_8);
    }

    /**
     * 读取证书
     *
     * @return java.lang.String
     * @author luce
     * @date 2021/9/3 10:09
     */
    @Override
    public String getCert() {
        File file = new File(acquireJarPath() + "safe/certificate.pem");
        long filelength = file.length();
        byte[] filecontent = new byte[(int) filelength];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(filecontent);
    }

    /**
     * 读取私钥
     *
     * @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
     * @author luce
     * @date 2021/9/3 13:23
     */
    @Override
    public BCECPrivateKey getPrivateKey() throws Exception {

        String privateKey = FileUtil.readString(acquireJarPath() + "safe/private.key", CharsetUtil.CHARSET_UTF_8);
        return BCECUtil.convertPKCS8ToECPrivateKey(BCECUtil.convertECPrivateKeyPEMToPKCS8(privateKey));
    }


    /**
     * 获取jar包中的绝对路径
     * @author luce
     * @date 2021/9/7 15:24 
     * @return java.lang.String
     */
    private String acquireJarPath() {
        String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
        try {
            path = java.net.URLDecoder.decode(path, "UTF-8");
        } catch (java.io.UnsupportedEncodingException ex) {
            log.error(ex.getLocalizedMessage());
        }
        java.io.File jarFile = new java.io.File(path);
        String jarFilepath = jarFile.getAbsolutePath();
        int end = jarFilepath.indexOf("target\\");
        String result = jarFilepath.substring(0, end + 7);
        return result;
    }
}

加密工具类

/**
 * 工具类
 *
 * @author luce
 * @date 2021年09月01日 13:40
 */
@Configuration
public class CipherTool {
    public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
    public static final String END_CERT = "-----END CERTIFICATE-----";


    /**
     * 生成证书文件
     *
     * @param x509Certificate 证书
     * @param certStoragePath 存储路径
     * @author luce
     * @date 2021/8/31 15:27
     */
    public static void createCert(X509Certificate x509Certificate, String certStoragePath) throws CertificateEncodingException {
        FileUtil.del(certStoragePath);
        //在指定目录生成证书文件
        FileUtil.newFile(certStoragePath);
        //证书标题
        FileUtil.appendUtf8String(BEGIN_CERT, certStoragePath);
        //分割线
        FileUtil.appendUtf8String(System.getProperty("line.separator"), certStoragePath);
        //转Base64码的证书
        FileUtil.appendUtf8String(Base64Encoder.encode(x509Certificate.getEncoded()), certStoragePath);
        //分割线
        FileUtil.appendUtf8String(System.getProperty("line.separator"), certStoragePath);
        //尾文
        FileUtil.appendUtf8String(END_CERT, certStoragePath);
    }

    /**
     * 生成密钥文件
     *
     * @param keyPair              密钥对
     * @param secretKeyStoragePath 存储路径
     * @author luce
     * @date 2021/8/31 15:30
     */
    public static void createKeyPair(KeyPair keyPair, String secretKeyStoragePath) throws InvalidKeyException, IOException {
        FileUtil.del(secretKeyStoragePath);
        //生成私钥
        ECPrivateKeyParameters bcecPrivateKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(keyPair.getPrivate());
        //生成公钥
        ECPublicKeyParameters bcecPublicKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(keyPair.getPublic());
        //在指定目录生成密钥文件
        FileUtil.newFile(secretKeyStoragePath);
        //转PEM格式密钥
        FileUtil.appendUtf8String(BCECUtil.convertECPrivateKeyPKCS8ToPEM(BCECUtil.convertECPrivateKeyToPKCS8(bcecPrivateKey, bcecPublicKey)), secretKeyStoragePath);
    }

    /**
     * 从证书 Path 读取公钥
     *
     * @param certFilePath 证书文件路径
     * @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
     * @author luce
     * @date 2021/9/1 14:53
     */
    public static BCECPublicKey getPublicKeyByPath(String certFilePath) throws Exception {
        X509Certificate sendCert = SM2CertUtil.getX509Certificate(certFilePath);
        return SM2CertUtil.getBCECPublicKey(sendCert);
    }

    /**
     * 读取公钥
     *
     * @param receiveCert 证书
     * @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
     * @author luce
     * @date 2021/9/1 14:53
     */
    public static BCECPublicKey getPublicKey(String receiveCert) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        X509Certificate sendCert = SM2CertUtil.getX509Certificate(new ByteArrayInputStream(receiveCert.getBytes(StandardCharsets.UTF_8)));
        return SM2CertUtil.getBCECPublicKey(sendCert);
    }

    /**
     * 从 Path 读取私钥
     *
     * @return org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
     * @author luce
     * @date 2021/9/1 14:53
     */
    public static BCECPrivateKey getPrivateKeyByPath(String privateKeyFilePath) throws Exception {
        String privateKey = FileUtil.readString(privateKeyFilePath, CharsetUtil.CHARSET_UTF_8);
        return BCECUtil.convertPKCS8ToECPrivateKey(BCECUtil.convertECPrivateKeyPEMToPKCS8(privateKey));
    }

    /**
     * 加签:用发送方的私钥进行加签
     *
     * @param encodeStr      Base64码报文
     * @param sendPrivateKey 发送方私钥
     * @param encodeTime     发送时间
     * @return Base64格式签文 -String
     * @author luce
     * @date 2021/8/31 14:37
     */
    public static String sign(String encodeStr, BCECPrivateKey sendPrivateKey, String encodeTime) throws CryptoException {
        String signStr = encodeStr + encodeTime;
        byte[] encodeSigFile = SM2Util.sign(sendPrivateKey, signStr.getBytes(StandardCharsets.UTF_8));
        return Base64Encoder.encode(encodeSigFile);
    }

    /**
     * 加密报文
     *
     * @param key       SM4随机密钥
     * @param encodeStr Base64格式 报文
     * @return 密文 -String
     * @author luce
     * @date 2021/8/31 16:00
     */
    public static String encryptedDocument(byte[] key, String encodeStr) throws Exception {
        byte[] ciphertext = SM4Util.encrypt_ECB_Padding(key, encodeStr.getBytes(StandardCharsets.UTF_8));
        return Base64Encoder.encode(ciphertext);
    }

    /**
     * 解密报文
     *
     * @param ciphertext     加密文件
     * @param bcecPrivateKey 接收方密钥
     * @return 报文 -byte[]
     * @author luce
     * @date 2021/8/31 16:48
     */
    public static String decodeDocument(Ciphertext ciphertext, BCECPrivateKey bcecPrivateKey) throws Exception {
        //解码密文
        byte[] decodeCiphertext = Base64Decoder.decode(ciphertext.getEncryptionFile());
        //解密随机密钥
        byte[] key = SM2Util.decrypt(SM2Engine.Mode.C1C3C2, bcecPrivateKey, Base64Decoder.decode(ciphertext.getEncryptionKey()));
        //解密密文
        byte[] encodeStr = SM4Util.decrypt_ECB_Padding(key, decodeCiphertext);
        return new String(encodeStr, StandardCharsets.UTF_8);
    }
}

测试

发送生成证书请求

在这里插入图片描述
结果
在这里插入图片描述

发送生成密文请求

在这里插入图片描述

发送解读密文请求

在这里插入图片描述

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值