天笑2001个人页面-阿里云开发者社区

个人头像照片 天笑2001 TA的个人档案

个人介绍

暂无个人介绍

擅长的技术

获得更多能力
通用技术能力:

暂时未有相关通用技术能力~

云产品技术能力:

暂时未有相关云产品技术能力~

阿里云技能认证

详细说明
  • 高分内容
  • 最新动态
  • 文章
  • 问答
正在加载, 请稍后...
暂无更多信息

2019年08月

  • 08.30 11:12:56
    回答了问题 2019-08-30 11:12:56

    官方的php sdk 里面并没有公钥证书签名的方法

    === 2018/8/30 16:00 终于搞定php对公钥证书签名 根据java SDK源码,用php取公钥证书SN如下:

    $p = [];
    
    $str = file_get_contents('./alipayRootCert.crt');
    $p["alipay_root_cert_sn"] = getRootCertSN($str);
    
    $str = file_get_contents('./alipayCertPublicKey_RSA2.crt');
    $p["alipay_cert_sn"] = getCertSN($str);
    
    $str = file_get_contents('./appCertPublicKey.crt');
    $p["app_cert_sn"] = getCertSN($str);
    
    var_export($p);
    
    function getRootCertSN($str)
    {
    	$arr = preg_split('/(?=-----BEGIN)/', $str, -1, PREG_SPLIT_NO_EMPTY);
    	$str = null;
    	foreach ($arr as $e) {
    		$sn = getCertSN($e, true);
    		if (!$sn)
    			continue;
    		if ($str === null)
    			$str = $sn;
    		else
    			$str .= "_" . $sn;
    	}
    	return $str;
    }
    
    function getCertSN($str, $matchAlgo=false)
    {
    /* 
    根据java SDK源码:AntCertificationUtil::getRootCertSN
    对证书链中RSA的项目进行过滤(猜测是gm国密算法java抛错搞不定,故意略去)
    java源码为:
    
    	if(c.getSigAlgOID().startsWith("1.2.840.113549.1.1"))
    
    根据 https://www.alvestrand.no/objectid/1.2.840.113549.1.1.html
    该OID为RSA算法系。
    */ 
    	if ($matchAlgo) {
    		openssl_x509_export($str, $out, false);
    		if (!preg_match('/Signature Algorithm:.*?RSA/im', $out, $m))
    			return;
    	}
    	$a = openssl_x509_parse($str);
    	$issuer = null;
    	// 注意:根据java代码输出,需要倒着排列 CN,OU,O
    	foreach ($a["issuer"] as $k=>$v) {
    		if ($issuer === null) {
    			$issuer = "$k=$v";
    		}
    		else {
    			$issuer = "$k=$v," . $issuer;
    		}
    	}
    #	echo($issuer . $a["serialNumber"] . "\n");
    	$sn = md5($issuer . $a["serialNumber"]);
    	return $sn;
    }
    
    
    
    

    做为首批踩坑者,只能长叹一声

    === 2018/8/30 12:15 更新:

    名字和序列化取出来像这样:

    CN=Ant Financial Certification Authority Class 2 R1,OU=Certification Authority,O=Ant Financial,C=CN
    42665268812499181166312682537244063920
    
    • 名字各段之间用逗号分隔,没有额外的空格
    • 序列号是10进制数,不是16进制,也没有任何分隔符
    • 取根证书sn需要遍历其中所有证书链,分别对name+serial进行md5编码,再用"_"连接起来。

    看了半天java源码,终于运行起来,把sn值打出来了,拷贝到php中,终于看到手机上支付页面出来了。结果大致像这样:

    "app_cert_sn" => "06de145e15a73a8ce87f3eefeddce8b2",
    //"alipay_cert_sn" => "b1a855128d973b1ff1b1609bbce77fe0",
    "alipay_root_cert_sn" => "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6"
    

    支付宝根证书要用特殊方法来取(源码里用AntCertificationUtil.getRootCertSN),怪不得之前写的不行。

    定此接口的人需要深刻检讨,竟然直接以java语言的某个输出来定接口规范。难怪php或其它语言的相关sdk没有出来,我感觉也不好写。

    ============ 旧消息

    新申请的开放平台帐号,被强制使用了公钥证书签名。 !!!下面开始吐槽:

    首先是接口文档比如 https://docs.open.alipay.com/api_1/alipay.trade.app.pay 中完全没提到有公钥证书签名这回事。

    调用支付出问题后(报错总是报“支付取消”??),四处查文档,才找到php SDK没有提供此功能,于是决定按接口文档来写。

    签名算法的文档中介绍是这样介绍算法的: “SN值是通过解析X.509证书文件中签发机构名称(name)以及内置序列号(serialNumber),将二者拼接后的字符串计算MD5值获取”

    尼码,文档完全没有介绍name和serialNumber分别是怎样的文本格式?也没有例子。 我试了name是 “ROOTCA”,“/C=CN/O=NRCAC/CN=ROOTCA”,“C=CN, O=NRCAC, CN=ROOTCA”各种格式,而serialNumber格式我试了 “69e2fec0170ac67b”以及加"69 e2..."、加“69:e2..."等,以及各种组合,都没有验签成功。

    于是走第二条路,找它说的java SDK源码来参考。打开java sdk链接,是在maven上的,找了好久才搜到了新版本java源码。

    终于找到了介绍的getCertSN函数,其核心是这样实现的:

        public static String getCertSN(String certPath)throws AlipayApiException{
            InputStream inputStream = null;
                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;
    
    

    于是找这cert.getIssuerX500Principal().getName()+cert.getSerialNumber()文档 实在看不懂,算了,还是直接运行这段java代码吧,把结果复制到php中填参数也可以,眼看就要成功了,结果运行java是这样的:

    java.security.cert.CertificateParsingException: java.io.IOException: Unknown named curve: 1.2.156.10197.1.301
            at sun.security.x509.X509CertInfo.<init>(Unknown Source)
            at sun.security.x509.X509CertImpl.parse(Unknown Source)
            at sun.security.x509.X509CertImpl.<init>(Unknown Source)
            at sun.security.provider.X509Factory.engineGenerateCertificate(Unknown Source)
            at java.security.cert.CertificateFactory.generateCertificate(Unknown Source)
            at j1.getCertSN(j1.java:33)
            at j1.main(j1.java:24)
    

    搜索发现,是java不支持国密算法。。。(https://cloud.tencent.com/info/fcfae2f8bd49fcce8737c7485afcdf29.html)

    踩0 评论0
正在加载, 请稍后...
滑动查看更多
正在加载, 请稍后...
暂无更多信息
  • 回答了问题 2019-08-30

    官方的php sdk 里面并没有公钥证书签名的方法

    === 2018/8/30 16:00 终于搞定php对公钥证书签名 根据java SDK源码,用php取公钥证书SN如下:

    $p = [];
    
    $str = file_get_contents('./alipayRootCert.crt');
    $p["alipay_root_cert_sn"] = getRootCertSN($str);
    
    $str = file_get_contents('./alipayCertPublicKey_RSA2.crt');
    $p["alipay_cert_sn"] = getCertSN($str);
    
    $str = file_get_contents('./appCertPublicKey.crt');
    $p["app_cert_sn"] = getCertSN($str);
    
    var_export($p);
    
    function getRootCertSN($str)
    {
    	$arr = preg_split('/(?=-----BEGIN)/', $str, -1, PREG_SPLIT_NO_EMPTY);
    	$str = null;
    	foreach ($arr as $e) {
    		$sn = getCertSN($e, true);
    		if (!$sn)
    			continue;
    		if ($str === null)
    			$str = $sn;
    		else
    			$str .= "_" . $sn;
    	}
    	return $str;
    }
    
    function getCertSN($str, $matchAlgo=false)
    {
    /* 
    根据java SDK源码:AntCertificationUtil::getRootCertSN
    对证书链中RSA的项目进行过滤(猜测是gm国密算法java抛错搞不定,故意略去)
    java源码为:
    
    	if(c.getSigAlgOID().startsWith("1.2.840.113549.1.1"))
    
    根据 https://www.alvestrand.no/objectid/1.2.840.113549.1.1.html
    该OID为RSA算法系。
    */ 
    	if ($matchAlgo) {
    		openssl_x509_export($str, $out, false);
    		if (!preg_match('/Signature Algorithm:.*?RSA/im', $out, $m))
    			return;
    	}
    	$a = openssl_x509_parse($str);
    	$issuer = null;
    	// 注意:根据java代码输出,需要倒着排列 CN,OU,O
    	foreach ($a["issuer"] as $k=>$v) {
    		if ($issuer === null) {
    			$issuer = "$k=$v";
    		}
    		else {
    			$issuer = "$k=$v," . $issuer;
    		}
    	}
    #	echo($issuer . $a["serialNumber"] . "\n");
    	$sn = md5($issuer . $a["serialNumber"]);
    	return $sn;
    }
    
    
    
    

    做为首批踩坑者,只能长叹一声

    === 2018/8/30 12:15 更新:

    名字和序列化取出来像这样:

    CN=Ant Financial Certification Authority Class 2 R1,OU=Certification Authority,O=Ant Financial,C=CN
    42665268812499181166312682537244063920
    
    • 名字各段之间用逗号分隔,没有额外的空格
    • 序列号是10进制数,不是16进制,也没有任何分隔符
    • 取根证书sn需要遍历其中所有证书链,分别对name+serial进行md5编码,再用"_"连接起来。

    看了半天java源码,终于运行起来,把sn值打出来了,拷贝到php中,终于看到手机上支付页面出来了。结果大致像这样:

    "app_cert_sn" => "06de145e15a73a8ce87f3eefeddce8b2",
    //"alipay_cert_sn" => "b1a855128d973b1ff1b1609bbce77fe0",
    "alipay_root_cert_sn" => "687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6"
    

    支付宝根证书要用特殊方法来取(源码里用AntCertificationUtil.getRootCertSN),怪不得之前写的不行。

    定此接口的人需要深刻检讨,竟然直接以java语言的某个输出来定接口规范。难怪php或其它语言的相关sdk没有出来,我感觉也不好写。

    ============ 旧消息

    新申请的开放平台帐号,被强制使用了公钥证书签名。 !!!下面开始吐槽:

    首先是接口文档比如 https://docs.open.alipay.com/api_1/alipay.trade.app.pay 中完全没提到有公钥证书签名这回事。

    调用支付出问题后(报错总是报“支付取消”??),四处查文档,才找到php SDK没有提供此功能,于是决定按接口文档来写。

    签名算法的文档中介绍是这样介绍算法的: “SN值是通过解析X.509证书文件中签发机构名称(name)以及内置序列号(serialNumber),将二者拼接后的字符串计算MD5值获取”

    尼码,文档完全没有介绍name和serialNumber分别是怎样的文本格式?也没有例子。 我试了name是 “ROOTCA”,“/C=CN/O=NRCAC/CN=ROOTCA”,“C=CN, O=NRCAC, CN=ROOTCA”各种格式,而serialNumber格式我试了 “69e2fec0170ac67b”以及加"69 e2..."、加“69:e2..."等,以及各种组合,都没有验签成功。

    于是走第二条路,找它说的java SDK源码来参考。打开java sdk链接,是在maven上的,找了好久才搜到了新版本java源码。

    终于找到了介绍的getCertSN函数,其核心是这样实现的:

        public static String getCertSN(String certPath)throws AlipayApiException{
            InputStream inputStream = null;
                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;
    
    

    于是找这cert.getIssuerX500Principal().getName()+cert.getSerialNumber()文档 实在看不懂,算了,还是直接运行这段java代码吧,把结果复制到php中填参数也可以,眼看就要成功了,结果运行java是这样的:

    java.security.cert.CertificateParsingException: java.io.IOException: Unknown named curve: 1.2.156.10197.1.301
            at sun.security.x509.X509CertInfo.<init>(Unknown Source)
            at sun.security.x509.X509CertImpl.parse(Unknown Source)
            at sun.security.x509.X509CertImpl.<init>(Unknown Source)
            at sun.security.provider.X509Factory.engineGenerateCertificate(Unknown Source)
            at java.security.cert.CertificateFactory.generateCertificate(Unknown Source)
            at j1.getCertSN(j1.java:33)
            at j1.main(j1.java:24)
    

    搜索发现,是java不支持国密算法。。。(https://cloud.tencent.com/info/fcfae2f8bd49fcce8737c7485afcdf29.html)

    踩0 评论1
  • 回答了问题 2019-07-17

    阿里云22端口IP黑名单机制?

    发现同样问题,公司内网无法访问所有阿里云服务器,怀疑是否被阿里云屏蔽IP。

    • 连接所有阿里云公网服务器都报同样的错:ssh_exchange_identification: read: Connection reset by peer
    • 让朋友从其它网络可以正常访问
    • 除22端口外其它端口可以正常访问

    已提交工单,期待结果。


    很遗憾工单回复说没有检查到异常。

    我们做了更多的测试,连接我公司的杭州、香港阿里云服务器,又测试了朋友公司的青岛和杭州阿里云服务器,发现在公司内网任何一台机器使用ssh登录任何阿里云主机均失败报错:ssh_exchange_identification: read: Connection reset by peer

    我们又在内网用ssh登录一些其它服务商主机,发现可正常登录,比如北京市互联互通服务器,美国github服务器。

    因此,我们仍怀疑是阿里云的某机制中断了公司出口IP对阿里云主机的访问。
    如果是其它中间网关上设置的问题,不应出现非阿里云主机可访问,而阿里云无论华东华北和香港服务器均无法访问的情况。
    如果是我司服务器设置出问题,不会别人公司的服务器也刚好无法访问;
    如果是发起请求的电脑问题,不应所有内网都出问题,也不应能ssh登录其它服务商的服务器。

    此外,我们搜索到这篇文章:http://blog.csdn.net/woshizhangliang999/article/details/50563247
    其中出现问题与我们遇到一样,文章提出“最终结论是阿里云那边把我的公司内部服务器对外访问IP设置了禁止ssh登录阿里云服务器”。

    已经耽误了一天工作,非常郁闷。于是再次发起工单,希望能及早解决。


    我们在内网机器和服务器上分别使用tcpdump抓包,结果显示:

    在内网机器上,发现ssh服务TCP连接成功后服务端主动发送FIN包结束会话;
    在服务器上,发现连接成功后客户端主机发送RESET包中止会话;

    显然两边对不上,分析必为链路中间被劫持所致。
    而如果是阿里云外其它网关的问题,不太可能刚好屏蔽所有阿里云主机的ssh登录。


    经确认确实是公司IP被阿里云禁止ssh登录。
    按客服建议,临时在控制台-服务器安全-登录安全中设置登录IP白名单解决我管理的几台服务器的登录问题;
    但对其它服务器的访问的解除,还在等待阿里云客服去解决。


    更新:一来一去和客服好些回合后,客服查到说屏蔽是由于登录了一些服务器错误次数比较多,后来客服帮忙解除了IP屏蔽。之后会不会再被自动屏蔽却不好说。希望此案例给大家一些参考。

    踩0 评论0
正在加载, 请稍后...
滑动查看更多