golang生成公私钥java签名验签
最近在学习golang,决定用golang搭建一个支付中心的服务,客服端与服务器通信使用非对称rsa加签验签,来保证通信的可靠安全。客服端可以请求支付中心golang生成公私钥,支付中心提供sdk,sdk支持各主流语言。golang版本的rsa签名和验签都没问题,
在写java版本的sdk确出现了问题,先把golang版本的sdk贴上。
package sdk import ( "crypto" "crypto/md5" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/pem" "errors" "log" ) const ( privateBlockType = "RSA PRIVATE KEY" publicBlockType = "RSA PUBLIC KEY" ) // Md5 to encode string of src, // the return string length is 32 func Md5(src string) string { defer GetTimer("md5加密")() bs := md5.Sum([]byte(src)) endCodeStr := hex.EncodeToString(bs[:]) return endCodeStr } func GenRsaKeys(bits int) (priKey, pubKey, pkcs8Key string, err error) { defer GetTimer("生成rsa密钥对")() key, err := rsa.GenerateKey(rand.Reader, bits) if err != nil { return "", "", "", err } err = key.Validate() if err != nil { return "", "","", err } priBytes := x509.MarshalPKCS1PrivateKey(key) priKey = string(pem.EncodeToMemory(&pem.Block{Type: privateBlockType, Bytes: priBytes})) pubBytes, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { panic(err) } pubKey = string(pem.EncodeToMemory(&pem.Block{Type: publicBlockType, Bytes: pubBytes})) pkcs8Bytes, err := x509.MarshalPKCS8PrivateKey(key) if err != nil { return "", "", "", err } pkcs8Key = string(pem.EncodeToMemory(&pem.Block{Type: privateBlockType, Bytes: pkcs8Bytes})) return priKey, pubKey, pkcs8Key, err } func decodePrivateKey(privateKey string) (*rsa.PrivateKey, error) { block, _ := pem.Decode([]byte(privateKey)) if block == nil || block.Type != privateBlockType { return nil, errors.New("failed to decode PEM block containing private key") } return x509.ParsePKCS1PrivateKey(block.Bytes) } //私钥加签 func SignWithPrivateKey(privateKey string, src []byte) (string, error) { defer GetTimer("私钥加签")() key, err := decodePrivateKey(privateKey) if err != nil { return "", err } hashed := sha256.Sum256(src) log.Println("sha256:", base64.StdEncoding.EncodeToString(hashed[:])) signBytes, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hashed[:]) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(signBytes), nil } func decodePublicKey(publicKey string) (*rsa.PublicKey, error) { block, _ := pem.Decode([]byte(publicKey)) if block == nil { return nil, errors.New("failed to decode PEM block containing private key") } pub,err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } return pub.(*rsa.PublicKey), err } // VerifyWithPublicKey 公钥验签 func VerifyWithPublicKey(signData string, srcData []byte, publicKey string) error { defer GetTimer("公钥验签")() key, err := decodePublicKey(publicKey) if err != nil { return err } bytes, err := base64.StdEncoding.DecodeString(signData) if err != nil { return err } hashed := sha256.Sum256(srcData) err = rsa.VerifyPKCS1v15(key, crypto.SHA256, hashed[:], bytes) if err != nil { return err } return nil } // DecryptWithPrivateKey私钥解密 func DecryptWithPrivateKey(privateKey string, encryptData string) ([]byte, error) { defer GetTimer("私钥解密")() key, err := decodePrivateKey(privateKey) if err != nil { return nil, err } encryptBytes, err := base64.StdEncoding.DecodeString(encryptData) if err != nil { return nil, err } return rsa.DecryptPKCS1v15(rand.Reader, key, encryptBytes) } //EncryptWithPublicKey 公钥加密 func EncryptWithPublicKey(publicKey string, bs []byte) (string, error) { defer GetTimer("公钥加密")() key, err := decodePublicKey(publicKey) if err != nil { return "", err } encryptBytes, err := rsa.EncryptPKCS1v15(rand.Reader, key, bs) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(encryptBytes), nil }
GenRsaKeys(int)参数是生成私钥字节大小,一般是1024或者2048,第一个返回值是cs1版本的私钥,第二个参数是公钥,第三个参数是pkcs8的私钥,java语言只能使用
pkcs8的私钥,cs1的会报错,另外java使用公钥私钥都要去掉开头和结尾的block type,不然会报错。这个函数生成的公私钥下面命令生成的等价
//生成cs1私钥 openssl genrsa -out rsa_private_key.pem 2048 //生成公钥 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem //生成pkcs8私钥 openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform pem -nocrypt -out private_pkcs8.pem
java版本的有公钥加密,pkcs8私钥解密,私钥加签,公钥验签
package com.zhqn.rsa.util; import javax.crypto.Cipher; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import java.util.Base64; /** * Date: 2022/1/27 18:10 * Description: * * @author zhouquan3 */ public class CryptoUtils { public static final String RSA_ALGORITHM = "rsa"; /** * 非对称加密 * * @param src 待加密字符串 * @param publicKey 公钥 * @return 解密后的字符串 */ public static String encryptWithPublicKey(String src, String publicKey) { try { byte[] publicKeyBytes = parseKey(publicKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); PublicKey key = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes)); Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encodeBytes = cipher.doFinal(src.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encodeBytes); } catch (Exception e) { throw new RuntimeException(e); } } /** * 去掉公钥或者私钥blockType * @param key 私钥或者公钥 * @return bytes */ private static byte[] parseKey(String key) { String newKey = Arrays.stream(key.split("\n")).filter(line -> line.length() > 0 && line.charAt(0) != '-').reduce(String::concat).orElse(""); return Base64.getDecoder().decode(newKey.getBytes()); } /** * 非对称解密 * * @param src 待解密字符串 * @param privateKey 符合pkcs8私钥 * @return 解密后的字符串 */ public static String decryptWithPkcs8(String src, String privateKey) { try { byte[] srcBytes = Base64.getDecoder().decode(src); byte[] privateKeyBytes = parseKey(privateKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decryptBytes = cipher.doFinal(srcBytes); return new String(decryptBytes); } catch (Exception e) { throw new RuntimeException(e); } } private static PublicKey getPublicKey(String publicKey) { byte[] keyBytes = parseKey(publicKey); try { KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); return keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes)); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException(e); } } /** * 用公钥验签 * @param sign 签名 * @param publicKey 公钥 * @param plain 原文 * @return true 通过 false 未通过 */ public static boolean verifySignWithPublicKey(String publicKey, String sign, String plain) { try { Signature signature = Signature.getInstance("SHA256WithRSA"); signature.initVerify(getPublicKey(publicKey)); signature.update(plain.getBytes(StandardCharsets.UTF_8)); return signature.verify(Base64.getDecoder().decode(sign)); } catch (Exception e ) { e.printStackTrace(); return false; } } /** * 用pkcs8格式的私钥加签名 * @param privateKey 私钥 * @param plain 原文 * @return base64 */ public static String signWithPkcs8(String privateKey, String plain) { try { Signature signature = Signature.getInstance("SHA256WithRSA"); signature.initSign(getPrivateKey(privateKey)); signature.update(plain.getBytes()); byte[] bytes = signature.sign(); return Base64.getEncoder().encodeToString(bytes); } catch (Exception e) { throw new RuntimeException(e); } } private static PrivateKey getPrivateKey(String privateKey) { byte[] privateKeyBytes = parseKey(privateKey); KeyFactory keyFactory = null; try { keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); } catch (Exception e) { throw new RuntimeException(e); } } }
java加签流程:signWithPkcs8(pkcs8私钥,明文)得到签名sign,golang调用VerifyWithPublicKey(签名,原文,公钥),验证通过
golang加签流程: SignWithPrivateKey(cs1版本的私钥,明文)得到签名sign,java调用verifySignWithPublicKey(公钥,签名,原文)
java加密流程: encryptWithPublicKey(公钥,原文)得到密文,golang调用DecryptWithPrivateKey(cs1版本的私钥,密文)得到原文
golang加密流程:EncryptWithPublicKey(公钥,原文)得到密文, java调用decryptWithPkcs8(密文,pkcs8密钥)得到原文
以上流程都充分测试,未发现问题,可以放心使用