Skip to content

1.2 签名示例代码

1.2.1 JAVA版本

Java
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.Base64;

public class Demo {
    private static final String AES_KEY = "1234567812345678";

    private static final String PRIVATE_KEY_PEM =
            "-----BEGIN PRIVATE KEY-----\n" +
            "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOXcXDlUfioHYK8x\n" +
            "+PTip8W9bwh9fWmTT9gXQGVo+91UQ0V/uZe1eR/PZrsW3GgZEqA3qavj2xSg/q+i\n" +
            "o8isZ2zm7qrzY0zvuCJ/ZTnqi1aGzlB2oH4CIq0nUtck/LeWS3Z5OQVQLB0FglRq\n" +
            "atZ/feqdBsqgqqwfggMVZI6A6w2PAgMBAAECgYACT7Pxs6LNI2PafyMkuz66o0wB\n" +
            "htmC/UM5RgqUuniF4joG8eDY4K9GHZzH1tQCi/X+/HBNVS43x+Knu3fwJaOolMzc\n" +
            "WagAW8csDXrT9M1XxfXiw/X/rKoit6Z60z+2OPO/XozR2MuP9Z6MYrLHRvD3RySD\n" +
            "rPK7ccogj3IJZ/4iUQJBAPYSb6Th2sb24flS8pxTgHMaBUeoRklnUMFW3UzpjsQV\n" +
            "uVGfr+P3RcqpWGh+9IqhFdMoVDmnUnr4Om712GIOJvcCQQDvInxeS9TqZNzSpVaZ\n" +
            "du7SL4tXEsZrgCpehtzQfEBCfReNTdEa25aseKSBqdpLyO8pgz5IjeFnU6ahifY0\n" +
            "y7ApAkEA2A6R9nzUglQtT6QUH0x2ARo8vpEyvaq4Tjn971U3JFZKtC942bm4jtwo\n" +
            "IwAtqTcNGa1UXpbapdwkOl8kEdyJOwJAQ0cKgmUHQ+KYldLaaFajnhKuOxMXK8tl\n" +
            "IC8FFMrAMXSMGb8Y41uAKonOjoRA3C1ty9oWvcbc8XsBWFU+JWBg8QJBAJ1xeRPh\n" +
            "SdYwi7GHNPQ4JruaYvrnBRK7XqYqHRBSMb8IFpDjWEoKJhr/rDoE/Z9x+pwgLsSM\n" +
            "OsfW8fVMcwILSqg=\n" +
            "-----END PRIVATE KEY-----\n";

    private static final String PUBLIC_KEY_PEM =
            "-----BEGIN PUBLIC KEY-----\n" +
            "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDl3Fw5VH4qB2CvMfj04qfFvW8I\n" +
            "fX1pk0/YF0BlaPvdVENFf7mXtXkfz2a7FtxoGRKgN6mr49sUoP6voqPIrGds5u6q\n" +
            "82NM77gif2U56otWhs5QdqB+AiKtJ1LXJPy3lkt2eTkFUCwdBYJUamrWf33qnQbK\n" +
            "oKqsH4IDFWSOgOsNjwIDAQAB\n" +
            "-----END PUBLIC KEY-----\n";

    public static void main(String[] args) {
        Map<String, Object> params = new LinkedHashMap<String, Object>();
        params.put("username", "momo");
        params.put("orderNo", "SH20220113111929");

        String channelCode = "kld8956spceq";
        String method = "userVerify";
        String nonce = "5K8264ILTKCH16CQ2502SI8ZNMTM67VS";
        String timestamp = "1568945678";

        try {
            String bizData = buildBizData(params, AES_KEY);
            String signature = sign(bizData, channelCode, method, nonce, timestamp);
            System.out.println(signature);
            if (check(bizData, channelCode, method, nonce, timestamp, signature)) {
                System.out.println("验签成功");
            } else {
                System.out.println("验签失败");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // BuildBizData 加密入参
    static String buildBizData(Map<String, Object> params, String aesKey) throws Exception {
        String rawJson = toJsonSorted(params);
        return aesEcbEncryptBase64(rawJson.getBytes(StandardCharsets.UTF_8), aesKey);
    }

    // Sign 返回签名
    static String sign(String bizData, String channelCode, String method, String nonce, String timestamp) {
        try {
            String paramsStr = String.format("biz_data=%s&channel_code=%s&method=%s&nonce=%s&timestamp=%s", bizData, channelCode, method, nonce, timestamp);
            RSAPrivateKey privateKey = loadPrivateKeyFromPem(PRIVATE_KEY_PEM);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(paramsStr.getBytes(StandardCharsets.UTF_8));
            byte[] sig = signature.sign();
            return Base64.getEncoder().encodeToString(sig);
        } catch (Exception e) {
            return "";
        }
    }

    // Check 校验签名
    static boolean check(String bizData, String channelCode, String method, String nonce, String timestamp, String signatureBase64) {
        try {
            String paramsStr = String.format("biz_data=%s&channel_code=%s&method=%s&nonce=%s&timestamp=%s", bizData, channelCode, method, nonce, timestamp);
            RSAPublicKey publicKey = loadPublicKeyFromPem(PUBLIC_KEY_PEM);
            Signature verifier = Signature.getInstance("SHA256withRSA");
            verifier.initVerify(publicKey);
            verifier.update(paramsStr.getBytes(StandardCharsets.UTF_8));
            byte[] sig = Base64.getDecoder().decode(signatureBase64);
            return verifier.verify(sig);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return false;
        }
    }

    static String aesEcbEncryptBase64(byte[] rawData, String key) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] encryptData = cipher.doFinal(rawData);
        return Base64.getEncoder().encodeToString(encryptData);
    }

    private static RSAPrivateKey loadPrivateKeyFromPem(String pem) throws Exception {
        String normalized = pem.replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s", "");
        byte[] keyBytes = Base64.getDecoder().decode(normalized);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    }

    private static RSAPublicKey loadPublicKeyFromPem(String pem) throws Exception {
        String normalized = pem.replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", "");
        byte[] keyBytes = Base64.getDecoder().decode(normalized);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }

    // Minimal stable JSON: sort keys lexicographically like Go's encoding/json for maps
    private static String toJsonSorted(Map<String, Object> map) {
        List<String> keys = new ArrayList<String>(map.keySet());
        Collections.sort(keys);
        StringBuilder sb = new StringBuilder();
        sb.append('{');
        boolean first = true;
        for (String k : keys) {
            if (!first) sb.append(',');
            first = false;
            sb.append('"').append(escapeJson(k)).append('"').append(':');
            Object v = map.get(k);
            if (v == null) {
                sb.append("null");
            } else {
                sb.append('"').append(escapeJson(String.valueOf(v))).append('"');
            }
        }
        sb.append('}');
        return sb.toString();
    }

    private static String escapeJson(String s) {
        StringBuilder out = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '"': out.append("\\\""); break;
                case '\\': out.append("\\\\"); break;
                case '\b': out.append("\\b"); break;
                case '\f': out.append("\\f"); break;
                case '\n': out.append("\\n"); break;
                case '\r': out.append("\\r"); break;
                case '\t': out.append("\\t"); break;
                default:
                    if (c < 0x20) {
                        out.append(String.format("\\u%04x", (int) c));
                    } else {
                        out.append(c);
                    }
            }
        }
        return out.toString();
    }
}

1.2.2 GO版本

go
package main

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"

	"github.com/forgoer/openssl"
)

var aesKey = "1234567812345678"

var privateKey = []byte(`
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOXcXDlUfioHYK8x
+PTip8W9bwh9fWmTT9gXQGVo+91UQ0V/uZe1eR/PZrsW3GgZEqA3qavj2xSg/q+i
o8isZ2zm7qrzY0zvuCJ/ZTnqi1aGzlB2oH4CIq0nUtck/LeWS3Z5OQVQLB0FglRq
atZ/feqdBsqgqqwfggMVZI6A6w2PAgMBAAECgYACT7Pxs6LNI2PafyMkuz66o0wB
htmC/UM5RgqUuniF4joG8eDY4K9GHZzH1tQCi/X+/HBNVS43x+Knu3fwJaOolMzc
WagAW8csDXrT9M1XxfXiw/X/rKoit6Z60z+2OPO/XozR2MuP9Z6MYrLHRvD3RySD
rPK7ccogj3IJZ/4iUQJBAPYSb6Th2sb24flS8pxTgHMaBUeoRklnUMFW3UzpjsQV
uVGfr+P3RcqpWGh+9IqhFdMoVDmnUnr4Om712GIOJvcCQQDvInxeS9TqZNzSpVaZ
du7SL4tXEsZrgCpehtzQfEBCfReNTdEa25aseKSBqdpLyO8pgz5IjeFnU6ahifY0
y7ApAkEA2A6R9nzUglQtT6QUH0x2ARo8vpEyvaq4Tjn971U3JFZKtC942bm4jtwo
IwAtqTcNGa1UXpbapdwkOl8kEdyJOwJAQ0cKgmUHQ+KYldLaaFajnhKuOxMXK8tl
IC8FFMrAMXSMGb8Y41uAKonOjoRA3C1ty9oWvcbc8XsBWFU+JWBg8QJBAJ1xeRPh
SdYwi7GHNPQ4JruaYvrnBRK7XqYqHRBSMb8IFpDjWEoKJhr/rDoE/Z9x+pwgLsSM
OsfW8fVMcwILSqg=
-----END PRIVATE KEY-----
`)

var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDl3Fw5VH4qB2CvMfj04qfFvW8I
fX1pk0/YF0BlaPvdVENFf7mXtXkfz2a7FtxoGRKgN6mr49sUoP6voqPIrGds5u6q
82NM77gif2U56otWhs5QdqB+AiKtJ1LXJPy3lkt2eTkFUCwdBYJUamrWf33qnQbK
oKqsH4IDFWSOgOsNjwIDAQAB
-----END PUBLIC KEY-----
`)

func main() {
	params := map[string]interface{}{
		"username": "momo",
		"orderNo":  "SH20220113111929",
	}
	channelCode := "kld8956spceq"
	method := "userVerify"
	nonce := "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"
	timestamp := "1568945678"

	bizData, err := BuildBizData(params, aesKey)
	if err != nil {
		panic(err)
	}

	signature := Sign(bizData, channelCode, method, nonce, timestamp)
	fmt.Println(signature)
	if Check(bizData, channelCode, method, nonce, timestamp, signature) {
		fmt.Println("验签成功")
	} else {
		fmt.Println("验签失败")
	}
}

// BuildBizData 加密入参
func BuildBizData(params map[string]interface{}, aesKey string) (string, error) {
	rawData, err := json.Marshal(params)
	if err != nil {
		return "", err
	}
	return AesEcbEncryptBase64(rawData, aesKey)
}

// Sign 返回签名
func Sign(bizData, channelCode, method, nonce, timestamp string) string {
	paramsStr := fmt.Sprintf("biz_data=%s&channel_code=%s&method=%s&nonce=%s&timestamp=%s", bizData, channelCode, method, nonce, timestamp)
	messageBuffer := bytes.NewBufferString(paramsStr)
	hash := sha256.New()
	_, _ = hash.Write(messageBuffer.Bytes())
	digest := hash.Sum(nil)
	// pem解码
	block, _ := pem.Decode(privateKey)
	var rsaPrivateKey interface{}
	// x509解码
	rsaPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return ""
	}
	signature, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey.(*rsa.PrivateKey), crypto.SHA256, digest)
	if err != nil {
		return ""
	}
	return base64.StdEncoding.EncodeToString(signature)
}

// Check 校验签名
func Check(bizData, channelCode, method, nonce, timestamp, signature string) bool {
	paramsStr := fmt.Sprintf("biz_data=%s&channel_code=%s&method=%s&nonce=%s&timestamp=%s", bizData, channelCode, method, nonce, timestamp)
	messageBuffer := bytes.NewBufferString(paramsStr)
	hash := sha256.New()
	_, _ = hash.Write(messageBuffer.Bytes())
	digest := hash.Sum(nil)
	// pem解码
	block, _ := pem.Decode(publicKey)
	// x509解码
	publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Println(err)
		return false
	}
	sig, err := base64.StdEncoding.DecodeString(signature)
	if err != nil {
		fmt.Println(err)
		return false
	}
	// 类型断言
	pk := publicKeyInterface.(*rsa.PublicKey)
	if err := rsa.VerifyPKCS1v15(pk, crypto.SHA256, digest, sig); err != nil {
		fmt.Println(err)
		return false
	}
	return true
}

func AesEcbEncryptBase64(rawData []byte, key string) (string, error) {
	encryptData, err := openssl.AesECBEncrypt(rawData, []byte(key), openssl.PKCS5_PADDING)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(encryptData), nil
}