Jerry hace 6 años
padre
commit
ac95c5d793

+ 7 - 5
README.md

@@ -105,7 +105,8 @@ client := alipay.NewClient()
 
 ### 支付宝公共API
 
-* alipay.GetCertSN() => 获取证书SN号(app_cert_sn、alipay_cert_sn、alipay_root_cert_sn>
+* alipay.GetCertSN() => 获取证书SN号(app_cert_sn、alipay_cert_sn)
+* alipay.GetRootCertSN() => 获取证书SN号(alipay_root_cert_sn)
 * alipay.SystemOauthToken() => 换取授权访问令牌(得到access_token,user_id等信息)
 * alipay.FormatPrivateKey() => 格式化应用私钥
 * alipay.FormatPublicKey() => 格式化支付宝公钥
@@ -201,16 +202,17 @@ import (
 	"github.com/iGoogle-ink/gopay/alipay"
 )
 
-//初始化支付宝客户端
+// 初始化支付宝客户端
 //    appId:应用ID
 //    privateKey:应用秘钥
 //    isProd:是否是正式环境
 client := alipay.NewClient("2016091200494382", privateKey, false)
 
-//设置支付宝请求 公共参数
+// 设置支付宝请求 公共参数
 //    注意:具体设置哪些参数,根据不同的方法而不同,此处列举出所以设置参数
-client.SetAliPayRootCertSN().               //设置支付宝根证书SN,通过 gopay.GetCertSN() 获取
-    SetAppCertSN().                         //设置应用公钥证书SN,通过 gopay.GetCertSN() 获取
+client.SetAliPayRootCertSN().               //设置支付宝根证书SN,通过 alipay.GetRootCertSN() 获取
+    SetAppCertSN().                         //设置应用公钥证书SN,通过 alipay.GetCertSN() 获取
+    SetAliPayPublicCertSN().                //设置支付宝公钥证书SN,通过 alipay.GetCertSN() 获取
     SetCharset("utf-8").                    //设置字符编码,不设置默认 utf-8
     SetSignType("RSA2").                    //设置签名类型,不设置默认 RSA2
     SetReturnUrl("https://www.gopay.ink").  //设置返回URL

+ 9 - 9
alipay/alipay_client_test.go

@@ -126,15 +126,15 @@ func TestGetCertSN(t *testing.T) {
 	}
 	fmt.Println("sn:", sn)
 
-	sn, err = GetCertSN("../examples/alipay_cert/alipayRootCert.crt")
-	if err != nil {
-		fmt.Println("err:", err)
-		return
-	}
-	if sn != "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" {
-		t.Fatal("get cert chain sn error")
-	}
-	fmt.Println("sn:", sn)
+	//sn, err := GetRootCertSN("../examples/alipay_cert/alipayRootCert.crt")
+	//if err != nil {
+	//	fmt.Println("err:", err)
+	//	return
+	//}
+	//if sn != "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6" {
+	//	t.Fatal("get cert chain sn error")
+	//}
+	//fmt.Println("sn:", sn)
 }
 
 func TestDecryptOpenDataToBodyMap(t *testing.T) {

+ 4 - 4
alipay/alipay_params.go

@@ -40,7 +40,7 @@ type OpenApiRoyaltyDetailInfoPojo struct {
 }
 
 // 设置 应用公钥证书SN
-//    appCertSN:应用公钥证书SN,通过 gopay.GetCertSN() 获取
+//    appCertSN:应用公钥证书SN,通过 alipay.GetCertSN() 获取
 func (a *Client) SetAppCertSN(appCertSN string) (client *Client) {
 	a.mu.Lock()
 	a.AppCertSN = appCertSN
@@ -49,7 +49,7 @@ func (a *Client) SetAppCertSN(appCertSN string) (client *Client) {
 }
 
 // 设置 支付宝公钥证书SN
-//    aliPayPublicCertSN:支付宝公钥证书SN,通过 gopay.GetCertSN() 获取
+//    aliPayPublicCertSN:支付宝公钥证书SN,通过 alipay.GetCertSN() 获取
 func (a *Client) SetAliPayPublicCertSN(aliPayPublicCertSN string) (client *Client) {
 	a.mu.Lock()
 	a.AliPayPublicCertSN = aliPayPublicCertSN
@@ -58,7 +58,7 @@ func (a *Client) SetAliPayPublicCertSN(aliPayPublicCertSN string) (client *Clien
 }
 
 // 设置 支付宝CA根证书SN
-//    aliPayRootCertSN:支付宝CA根证书SN,通过 gopay.GetCertSN() 获取
+//    aliPayRootCertSN:支付宝CA根证书SN,通过 alipay.GetRootCertSN() 获取
 func (a *Client) SetAliPayRootCertSN(aliPayRootCertSN string) (client *Client) {
 	a.mu.Lock()
 	a.AliPayRootCertSN = aliPayRootCertSN
@@ -95,7 +95,7 @@ func (a *Client) SetAliPayPublicCertSnByPath(aliPayPublicCertPath string) (clien
 // 设置 alipay_root_cert_sn 通过支付宝CA根证书文件路径
 //    aliPayRootCertPath:支付宝CA根证书文件路径
 func (a *Client) SetAliPayRootCertSnByPath(aliPayRootCertPath string) (client *Client, err error) {
-	sn, err := GetCertSN(aliPayRootCertPath)
+	sn, err := GetRootCertSN(aliPayRootCertPath)
 	if err != nil {
 		return a, fmt.Errorf("get alipay_root_cert_sn return err, but alse return alipay client. err: %v", err)
 	}

+ 43 - 6
alipay/alipay_service_api.go

@@ -289,8 +289,8 @@ func FormatPublicKey(publicKey string) (pKey string) {
 }
 
 // GetCertSN 获取证书序列号SN
-//    certPath:X.509证书文件路径(appCertPublicKey.crt、alipayRootCert.crt、alipayCertPublicKey_RSA2)
-//    返回 sn:证书序列号(app_cert_sn、alipay_root_cert_sn、alipay_cert_sn)
+//    certPath:X.509证书文件路径(appCertPublicKey.crt、alipayCertPublicKey_RSA2.crt)
+//    返回 sn:证书序列号(app_cert_sn、alipay_cert_sn)
 //    返回 err:error 信息
 func GetCertSN(certPath string) (sn string, err error) {
 	var (
@@ -300,15 +300,52 @@ func GetCertSN(certPath string) (sn string, err error) {
 		h                  hash.Hash
 	)
 	certData, err = ioutil.ReadFile(certPath)
+	if err != nil {
+		return gopay.NULL, err
+	}
+	if block, _ := pem.Decode(certData); block != nil {
+		if certs, err = x509.ParseCertificates(block.Bytes); err != nil {
+			return gopay.NULL, err
+		}
+		name = certs[0].Issuer.String()
+		serialNumber = certs[0].SerialNumber.String()
+		h = md5.New()
+		h.Write([]byte(name))
+		h.Write([]byte(serialNumber))
+		if sn == "" {
+			sn += hex.EncodeToString(h.Sum(nil))
+		} else {
+			sn += "_"
+			sn += hex.EncodeToString(h.Sum(nil))
+		}
+	}
+	if sn == "" {
+		return "", errors.New("failed to get sn,please check your cert")
+	}
+	return sn, nil
+}
+
+// GetRootCertSN 获取root证书序列号SN
+//    rootCertPath:X.509证书文件路径(alipayRootCert.crt)
+//    返回 sn:证书序列号(alipay_root_cert_sn)
+//    返回 err:error 信息
+func GetRootCertSN(rootCertPath string) (sn string, err error) {
+	var (
+		certData           []byte
+		certs              []*x509.Certificate
+		name, serialNumber string
+		h                  hash.Hash
+	)
+	certData, err = ioutil.ReadFile(rootCertPath)
 	if err != nil {
 		return "", err
 	}
-	strs := strings.Split(string(certData), "-----END CERTIFICATE-----")
+	strs := strings.Split(string(certData), "\n\n")
 	for i := 0; i < len(strs); i++ {
-		if strs[i] == "" {
+		if strs[i] == gopay.NULL {
 			continue
 		}
-		if block, _ := pem.Decode([]byte(strs[i] + "-----END CERTIFICATE-----")); block != nil {
+		if block, _ := pem.Decode([]byte(strs[i])); block != nil {
 			if certs, err = x509.ParseCertificates(block.Bytes); err != nil {
 				continue
 			}
@@ -320,7 +357,7 @@ func GetCertSN(certPath string) (sn string, err error) {
 			h = md5.New()
 			h.Write([]byte(name))
 			h.Write([]byte(serialNumber))
-			if sn == "" {
+			if sn == gopay.NULL {
 				sn += hex.EncodeToString(h.Sum(nil))
 			} else {
 				sn += "_"

+ 75 - 0
examples/alipay_cert/java_sdk.txt

@@ -0,0 +1,75 @@
+
+// Java SDK 获取Root证书SN  AntCertificationUtil.getRootCertSN();
+/**
+ * 从公钥证书中提取公钥序列号
+ *
+ * @param certPath 公钥证书存放路径,例如:/home/admin/cert.crt
+ * @return 公钥证书序列号
+ * @throws AlipayApiException
+ */
+public static String getCertSN(String certPath) throws AlipayApiException {
+    InputStream inputStream = null;
+    try {
+        inputStream = new FileInputStream(certPath);
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        md.update((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes());
+        String certSN = new BigInteger(1, md.digest()).toString(16);
+        //BigInteger会把0省略掉,需补全至32位
+        certSN = fillMD5(certSN);
+        return certSN;
+
+    } catch (NoSuchAlgorithmException e) {
+        throw new AlipayApiException(e);
+    } catch (IOException e) {
+        throw new AlipayApiException(e);
+    } catch (CertificateException e) {
+        throw new AlipayApiException(e);
+    } finally {
+        try {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        } catch (IOException e) {
+            throw new AlipayApiException(e);
+        }
+    }
+}
+
+// Java SDK 获取证书SN  AlipaySignature.getCertSN();
+/**
+ * 获取根证书序列号
+ *
+ * @param rootCertContent
+ * @return
+ */
+public static String getRootCertSN(String rootCertContent) {
+    String rootCertSN = null;
+    try {
+        X509Certificate[] x509Certificates = readPemCertChain(rootCertContent);
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        for (X509Certificate c : x509Certificates) {
+            if (c.getSigAlgOID().startsWith("1.2.840.113549.1.1")) {
+                md.update((c.getIssuerX500Principal().getName() + c.getSerialNumber()).getBytes());
+                String certSN = new BigInteger(1, md.digest()).toString(16);
+                //BigInteger会把0省略掉,需补全至32位
+                certSN = fillMD5(certSN);
+                if (StringUtils.isEmpty(rootCertSN)) {
+                    rootCertSN = certSN;
+                } else {
+                    rootCertSN = rootCertSN + "_" + certSN;
+                }
+            }
+
+        }
+    } catch (Exception e) {
+        AlipayLogger.logBizError(("提取根证书失败"));
+    }
+    return rootCertSN;
+
+}
+
+private static String fillMD5(String md5) {
+    return md5.length() == 32 ? md5 : fillMD5("0" + md5);
+}

+ 1 - 1
model.go

@@ -7,7 +7,7 @@ const (
 	SUCCESS    = "SUCCESS"
 	FAIL       = "FAIL"
 	OK         = "OK"
-	Version    = "1.4.6"
+	Version    = "1.4.7"
 )
 
 type ReturnMessage struct {

+ 3 - 1
release_note.txt

@@ -1,7 +1,9 @@
 版本号:Release 1.4.7
-发布时间:2019/12/10 18:37
+发布时间:2019/12/10 19:37
 修改记录:
    (1) 模块分包大调整
+   (2) 支付宝:修改公共API方法:alipay.GetCertSN(),不再支持支付宝根证书的SN获取
+   (3) 支付宝:新增公共API方法:alipay.GetRootCertSN(),获取root证书序列号SN
 
 版本号:Release 1.4.6
 发布时间:2019/12/09 18:37