@@ -129,6 +129,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_x509_export, 0, 0, 2)
129
129
ZEND_ARG_INFO (0 , notext )
130
130
ZEND_END_ARG_INFO ()
131
131
132
+ ZEND_BEGIN_ARG_INFO_EX (arginfo_openssl_x509_fingerprint , 0 , 0 , 1 )
133
+ ZEND_ARG_INFO (0 , x509 )
134
+ ZEND_ARG_INFO (0 , method )
135
+ ZEND_ARG_INFO (0 , raw_output )
136
+ ZEND_END_ARG_INFO ()
137
+
132
138
ZEND_BEGIN_ARG_INFO (arginfo_openssl_x509_check_private_key , 0 )
133
139
ZEND_ARG_INFO (0 , cert )
134
140
ZEND_ARG_INFO (0 , key )
@@ -443,6 +449,7 @@ const zend_function_entry openssl_functions[] = {
443
449
PHP_FE (openssl_x509_checkpurpose , arginfo_openssl_x509_checkpurpose )
444
450
PHP_FE (openssl_x509_check_private_key , arginfo_openssl_x509_check_private_key )
445
451
PHP_FE (openssl_x509_export , arginfo_openssl_x509_export )
452
+ PHP_FE (openssl_x509_fingerprint , arginfo_openssl_x509_fingerprint )
446
453
PHP_FE (openssl_x509_export_to_file , arginfo_openssl_x509_export_to_file )
447
454
448
455
/* PKCS12 funcs */
@@ -1665,6 +1672,121 @@ PHP_FUNCTION(openssl_x509_export)
1665
1672
}
1666
1673
/* }}} */
1667
1674
1675
+ static int php_openssl_x509_fingerprint (X509 * peer , const char * method , zend_bool raw , char * * out , int * out_len )
1676
+ {
1677
+ unsigned char md [EVP_MAX_MD_SIZE ];
1678
+ const EVP_MD * mdtype ;
1679
+ int n ;
1680
+
1681
+ if (!(mdtype = EVP_get_digestbyname (method ))) {
1682
+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "Unknown signature algorithm ");
1683
+ return FAILURE ;
1684
+ } else if (!X509_digest (peer , mdtype , md , & n )) {
1685
+ php_error_docref (NULL TSRMLS_CC , E_ERROR , "Could not generate signature" );
1686
+ return FAILURE ;
1687
+ }
1688
+
1689
+ if (raw ) {
1690
+ * out_len = n ;
1691
+ * out = estrndup (md , n );
1692
+ } else {
1693
+ * out_len = n * 2 ;
1694
+ * out = emalloc (* out_len + 1 );
1695
+
1696
+ make_digest_ex (* out , md , n );
1697
+ }
1698
+
1699
+ return SUCCESS ;
1700
+ }
1701
+
1702
+ static int php_x509_fingerprint_cmp (X509 * peer , const char * method , const char * expected )
1703
+ {
1704
+ char * fingerprint ;
1705
+ int fingerprint_len ;
1706
+ int result = -1 ;
1707
+
1708
+ if (php_openssl_x509_fingerprint (peer , method , 0 , & fingerprint , & fingerprint_len ) == SUCCESS ) {
1709
+ result = strcmp (expected , fingerprint );
1710
+ efree (fingerprint );
1711
+ }
1712
+
1713
+ return result ;
1714
+ }
1715
+
1716
+ static zend_bool php_x509_fingerprint_match (X509 * peer , zval * val )
1717
+ {
1718
+ if (Z_TYPE_P (val ) == IS_STRING ) {
1719
+ const char * method = NULL ;
1720
+
1721
+ switch (Z_STRLEN_P (val )) {
1722
+ case 32 :
1723
+ method = "md5" ;
1724
+ break ;
1725
+
1726
+ case 40 :
1727
+ method = "sha1" ;
1728
+ break ;
1729
+ }
1730
+
1731
+ return method && php_x509_fingerprint_cmp (peer , method , Z_STRVAL_P (val )) == 0 ;
1732
+ } else if (Z_TYPE_P (val ) == IS_ARRAY ) {
1733
+ HashPosition pos ;
1734
+ zval * * current ;
1735
+ char * key ;
1736
+ uint key_len ;
1737
+ ulong key_index ;
1738
+
1739
+ for (zend_hash_internal_pointer_reset_ex (Z_ARRVAL_P (val ), & pos );
1740
+ zend_hash_get_current_data_ex (Z_ARRVAL_P (val ), (void * * )& current , & pos ) == SUCCESS ;
1741
+ zend_hash_move_forward_ex (Z_ARRVAL_P (val ), & pos )
1742
+ ) {
1743
+ int key_type = zend_hash_get_current_key_ex (Z_ARRVAL_P (val ), & key , & key_len , & key_index , 0 , & pos );
1744
+
1745
+ if (key_type == HASH_KEY_IS_STRING
1746
+ && Z_TYPE_PP (current ) == IS_STRING
1747
+ && php_x509_fingerprint_cmp (peer , key , Z_STRVAL_PP (current )) != 0
1748
+ ) {
1749
+ return 0 ;
1750
+ }
1751
+ }
1752
+ return 1 ;
1753
+ }
1754
+ return 0 ;
1755
+ }
1756
+
1757
+ PHP_FUNCTION (openssl_x509_fingerprint )
1758
+ {
1759
+ X509 * cert ;
1760
+ zval * * zcert ;
1761
+ long certresource ;
1762
+ zend_bool raw_output = 0 ;
1763
+ char * method = "sha1" ;
1764
+ int method_len ;
1765
+
1766
+ char * fingerprint ;
1767
+ int fingerprint_len ;
1768
+
1769
+ if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC , "Z|sb" , & zcert , & method , & method_len , & raw_output ) == FAILURE ) {
1770
+ return ;
1771
+ }
1772
+
1773
+ cert = php_openssl_x509_from_zval (zcert , 0 , & certresource TSRMLS_CC );
1774
+ if (cert == NULL ) {
1775
+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "cannot get cert from parameter 1" );
1776
+ RETURN_FALSE ;
1777
+ }
1778
+
1779
+ if (php_openssl_x509_fingerprint (cert , method , raw_output , & fingerprint , & fingerprint_len ) == SUCCESS ) {
1780
+ RETVAL_STRINGL (fingerprint , fingerprint_len , 0 );
1781
+ } else {
1782
+ RETVAL_FALSE ;
1783
+ }
1784
+
1785
+ if (certresource == -1 && cert ) {
1786
+ X509_free (cert );
1787
+ }
1788
+ }
1789
+
1668
1790
/* {{{ proto bool openssl_x509_check_private_key(mixed cert, mixed key)
1669
1791
Checks if a private key corresponds to a CERT */
1670
1792
PHP_FUNCTION (openssl_x509_check_private_key )
@@ -4865,6 +4987,17 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
4865
4987
4866
4988
/* if the cert passed the usual checks, apply our own local policies now */
4867
4989
4990
+ if (GET_VER_OPT ("peer_fingerprint" )) {
4991
+ if (Z_TYPE_PP (val ) == IS_STRING || Z_TYPE_PP (val ) == IS_ARRAY ) {
4992
+ if (!php_x509_fingerprint_match (peer , * val )) {
4993
+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "Peer fingerprint doesn't match" );
4994
+ return FAILURE ;
4995
+ }
4996
+ } else {
4997
+ php_error_docref (NULL TSRMLS_CC , E_WARNING , "Expected peer fingerprint must be a string or an array" );
4998
+ }
4999
+ }
5000
+
4868
5001
name = X509_get_subject_name (peer );
4869
5002
4870
5003
/* Does the common name match ? (used primarily for https://) */
0 commit comments