openssl_pkcs7_verify的问题之旅

写在前面

在做php的pkcs7签名校验的时候,遇到一个校验问题,我的情况是,生成的签名不包含证书信息,而在校验时一定要指定一个文件(对应参数$outfilename),用于存放从签名提取的证书。于是就很迷惑,签名都已经不包含证书了,怎么在没有证书的情况下提取证书,然后就看到一个网站,看起来好像是php的bug问题?

bug提出地址

php的openssl_pkcs7_verify源码地址:4077行

/* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
   Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers */
PHP_FUNCTION(openssl_pkcs7_verify)
{
	X509_STORE * store = NULL;
	zval * cainfo = NULL;
	STACK_OF(X509) *signers= NULL;
	STACK_OF(X509) *others = NULL;
	PKCS7 * p7 = NULL;
	BIO * in = NULL, * datain = NULL, * dataout = NULL;
	zend_long flags = 0;
	char * filename;
	size_t filename_len;
	char * extracerts = NULL;
	size_t extracerts_len = 0;
	char * signersfilename = NULL;
	size_t signersfilename_len = 0;
	char * datafilename = NULL;
	size_t datafilename_len = 0;

	RETVAL_LONG(-1);

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl|papp", &filename, &filename_len,
				&flags, &signersfilename, &signersfilename_len, &cainfo,
				&extracerts, &extracerts_len, &datafilename, &datafilename_len) == FAILURE) {
		return;
	}

	if (extracerts) {
		others = load_all_certs_from_file(extracerts);
		if (others == NULL) {
			goto clean_exit;
		}
	}

	flags = flags & ~PKCS7_DETACHED;

	store = setup_verify(cainfo);

	if (!store) {
		goto clean_exit;
	}
	if (php_openssl_open_base_dir_chk(filename)) {
		goto clean_exit;
	}

	in = BIO_new_file(filename, (flags & PKCS7_BINARY) ? "rb" : "r");
	if (in == NULL) {
		goto clean_exit;
	}
	p7 = SMIME_read_PKCS7(in, &datain);
	if (p7 == NULL) {
#if DEBUG_SMIME
		zend_printf("SMIME_read_PKCS7 failed
");
#endif
		goto clean_exit;
	}

	if (datafilename) {

		if (php_openssl_open_base_dir_chk(datafilename)) {
			goto clean_exit;
		}

		dataout = BIO_new_file(datafilename, "w");
		if (dataout == NULL) {
			goto clean_exit;
		}
	}
#if DEBUG_SMIME
	zend_printf("Calling PKCS7 verify
");
#endif

	if (PKCS7_verify(p7, others, store, datain, dataout, (int)flags)) {

		RETVAL_TRUE;

		if (signersfilename) {
			BIO *certout;

			if (php_openssl_open_base_dir_chk(signersfilename)) {
				goto clean_exit;
			}

			certout = BIO_new_file(signersfilename, "w");
			if (certout) {
				int i;
				signers = PKCS7_get0_signers(p7, NULL, (int)flags);

				for(i = 0; i < sk_X509_num(signers); i++) {
					PEM_write_bio_X509(certout, sk_X509_value(signers, i));
				}
				BIO_free(certout);
				sk_X509_free(signers);
			} else {
				php_error_docref(NULL, E_WARNING, "signature OK, but cannot open %s for writing", signersfilename);
				RETVAL_LONG(-1);
			}
		}
		goto clean_exit;
	} else {
		RETVAL_FALSE;
	}
clean_exit:
	X509_STORE_free(store);
	BIO_free(datain);
	BIO_free(in);
	BIO_free(dataout);
	PKCS7_free(p7);
	sk_X509_free(others);
}
/* }}} */

简单看了下,前面部分基本是读取签名和证书信息,而从(PKCS7_verify(p7, others, store, datain, dataout, (int)flags)这句开始,这部分应该是校验p7签名,在校验通过之后,会判断是否指定了存放证书的文件$outfilename(该证书从签名里提取)。

在正常签名包含证书的情况下,因为提取的证书存放文件($outfilename)没有默认值,是个必填参数,所以只要指定了就不会出错。

但是在签名没有包含证书的情况下,应该是无法提取证书的,可是指定证书文件是个必填参数。如果随便指定个文件,会报无法提取证书的错误;如果指定$outfilename为null的话,会有错误信息提示无法写入啥的,报错会有警告,其中一段就包含了signature OK, but cannot open xxxxxxxxxx。

所以简单总结的话,似乎是php的判断不够严谨导致的问题。

不够本人对这些了解的不多,如果因为自己错漏导致的理解错误,希望能有人及时指出,非常感谢!

原文地址:https://www.cnblogs.com/inkqx/p/14486586.html