Rsa.java

在某些场景下,你可能想自己接入api,下面提供了加密相关的逻辑代码。

在此提醒 : openssl 生成的私钥默认为pkcs1格式,java提供开箱即用的pkcs8格式,所以需要做转换,请参考“准备工作“一节。

package io.dabank.sdk;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class Rsa {
    public static final String PKCS8_PRIVATE_PEM_START = "-----BEGIN PRIVATE KEY-----";
    public static final String PKCS8_PRIVATE_PEM_END = "-----END PRIVATE KEY-----";
    public static final String PUBLIC_KEY_BEGIN = "-----BEGIN PUBLIC KEY-----";
    public static final String PUBLIC_KEY_END = "-----END PUBLIC KEY-----";

    private static final String CHARSET = "utf-8";
    private static final String RSA = "RSA";
    private static final String HASH_ENCRYPTION_ALGORITHM = "SHA256withRSA";

    /**
     * 用公钥验证RSA签名是否正确
     * @param message 原始明文
     * @param publicKey 公钥
     * @param sign 签名
     */
    public static boolean verify(String message, String sign, PublicKey publicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
        final Signature sig = Signature.getInstance(HASH_ENCRYPTION_ALGORITHM);
        sig.initVerify(publicKey);
        sig.update(message.getBytes(CHARSET));
        final byte[] bytes = Base64.getDecoder().decode(sign);
        return sig.verify(bytes);
    }

    /**
     * 从pem格式解析RSA公钥
     * @param key pem公钥
     */
    public static RSAPublicKey getPublicKeyFromPEM(String key) throws IOException, GeneralSecurityException {
        String publicKeyPEM = key.replace(PUBLIC_KEY_BEGIN, "")
                .replace(PUBLIC_KEY_END, "")
                .replaceAll("\n", "");
        byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        return (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
    }

    /**
     * 从PKCS8PEM 解析私钥
     * @param pkcs8PEM pkcs8_pem 私钥
     */
    public static RSAPrivateKey getPrivateKeyFromPKCS8PEM(String pkcs8PEM) throws Exception {
        if (pkcs8PEM == null || pkcs8PEM.isEmpty()) {
            throw new RuntimeException("Private key is null or empty");
        }

        if (pkcs8PEM.contains("RSA PRIVATE KEY")) {
            throw new RuntimeException("Private key is PKCS1 format, not PKCS8, try this: \n openssl pkcs8 -topk8 -inform PEM -outform PEM -in app_private_key.pem -out priv8.pem -nocrypt");
        }

        pkcs8PEM = pkcs8PEM
                .replaceAll(PKCS8_PRIVATE_PEM_START, "")
                .replaceAll(PKCS8_PRIVATE_PEM_END, "")
                .replaceAll("\n", "");
        byte[] keyBytes = Base64.getDecoder().decode(pkcs8PEM);
        return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
    }

    /**
     * sha256 rsa 然后用base64编码
     * @param message 需要签名的数据,字符串使用 "".getBytes("UTF-8")
     * @param privateKey 私钥
     */
    public static String sha256withRSASign2base64(RSAPrivateKey privateKey, byte[] message) throws SignatureException, InvalidKeyException {
//        System.out.println("[debug]message:" + Arrays.toString(message));
        if (privateKey == null || message == null || message.length == 0) {
            throw new RuntimeException("private key is null or message is empty");
        }
        Signature signer = null;
        try {
            signer = Signature.getInstance(HASH_ENCRYPTION_ALGORITHM);
        } catch (NoSuchAlgorithmException e) { // this will not happen forever
            e.printStackTrace();
        }
        signer.initSign(privateKey);
        signer.update(message);
        return Base64.getEncoder().encodeToString(signer.sign());
    }
}

Last updated

Was this helpful?