у меня есть сертификат:
-----BEGIN CERTIFICATE-----
MIIEvzCCBGSgAwIBAgITeAAARlJXpwVPUYDkbAABAABGUjAKBggqhkjOPQQDAjBi
MRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAV
BgoJkiaJk/IsZAEZFgdleHRnYXp0MRswGQYDVQQDExJQRVpFSU5WT0lDRVNDQTQt
Q0EwHhcNMjQwNzAyMDMxMTE2WhcNMjYwNzAyMDMyMTE2WjBJMQswCQYDVQQGEwJT
QTESMBAGA1UEChMJZnJlZSB0ZXh0MRIwEAYDVQQLEwlmcmVlIHRleHQxEjAQBgNV
BAMTCWZyZWUgdGV4dDBWMBAGByqGSM49AgEGBSuBBAAKA0IABP/Lh6KN1Y5Z6CzW
+3hLkwGLUyorKQoCCQZzrbtvHNLocvv7OFDUHZZiOnwzhavZLkOEPjH8ojIH1kb1
A4LsTEejggMTMIIDDzB+BgNVHREEdzB1pHMwcTEgMB4GA1UEBAwXMS1TaGFFa3wy
LVNoYUVrfDMtU2hhRWsxHzAdBgoJkiaJk/IsZAEBDA8zMTExOTAyOTM3MDAwMDMx
DTALBgNVBAwMBDExMDAxDjAMBgNVBBoMBWphemFuMQ0wCwYDVQQPDARUZWNoMB0G
A1UdDgQWBBQJ9H6ej6E1lbOuT+RG6WI9yHrdATAfBgNVHSMEGDAWgBTHwOa3qd0S
yk89bWiCliFF8wF1pDCB5QYDVR0fBIHdMIHaMIHXoIHUoIHRhoHObGRhcDovLy9D
Tj1QRVpFSU5WT0lDRVNDQTQtQ0EoMSksQ049UFJaRUlOVk9JQ0VQS0k0LENOPUNE
UCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25m
aWd1cmF0aW9uLERDPWV4dHphdGNhLERDPWdvdixEQz1sb2NhbD9jZXJ0aWZpY2F0
ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9u
UG9pbnQwgc4GCCsGAQUFBwEBBIHBMIG+MIG7BggrBgEFBQcwAoaBrmxkYXA6Ly8v
Q049UEVaRUlOVk9JQ0VTQ0E0LUNBLENOPUFJQSxDTj1QdWJsaWMlMjBLZXklMjBT
ZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPWV4dHphdGNh
LERDPWdvdixEQz1sb2NhbD9jQUNlcnRpZmljYXRlP2Jhc2U/b2JqZWN0Q2xhc3M9
Y2VydGlmaWNhdGlvbkF1dGhvcml0eTAOBgNVHQ8BAf8EBAMCB4AwPAYJKwYBBAGC
NxUHBC8wLQYlKwYBBAGCNxUIgYaoHYTQ+xKG7Z0kh877GdPAVWaBnNgtg+XFXQIB
ZAIBEDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwMwJwYJKwYBBAGCNxUK
BBowGDAKBggrBgEFBQcDAjAKBggrBgEFBQcDAzAKBggqhkjOPQQDAgNJADBGAiEA
2sVgVJidhX8lnKshVB4JL7YBY+F1p48JI7NPpO4xJDQCIQCWf97LYNEK7358msfA
RK1iTp3om8fnJ33A59SurRjENA==
-----END CERTIFICATE-----
и я хочу получить значение подписи, указанное в этом сертификате.
Я попробовал много решений, и это наиболее близко к необходимому результату.
$cert = openssl_x509_read($certificate);
openssl_x509_export($cert, $out, FALSE);
echo $out;
это решение дает мне всю информацию о сертификате, а также подпись с ним, но в виде строки:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
78:00:00:46:52:57:a7:05:4f:51:80:e4:6c:00:01:00:00:46:52
Signature Algorithm: ecdsa-with-SHA256
Issuer: DC=local, DC=gov, DC=extgazt, CN=PEZEINVOICESCA4-CA
Validity
Not Before: Jul 2 03:11:16 2024 GMT
Not After : Jul 2 03:21:16 2026 GMT
Subject: C=SA, O=free text, OU=free text, CN=free text
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:ff:cb:87:a2:8d:d5:8e:59:e8:2c:d6:fb:78:4b:
93:01:8b:53:2a:2b:29:0a:02:09:06:73:ad:bb:6f:
1c:d2:e8:72:fb:fb:38:50:d4:1d:96:62:3a:7c:33:
85:ab:d9:2e:43:84:3e:31:fc:a2:32:07:d6:46:f5:
03:82:ec:4c:47
ASN1 OID: secp256k1
X509v3 extensions:
X509v3 Subject Alternative Name:
DirName:/SN=1-ShaEk|2-ShaEk|3-ShaEk/UID=311190293700003/title=1100/registeredAddress=jazan/businessCategory=Tech
X509v3 Subject Key Identifier:
09:F4:7E:9E:8F:A1:35:95:B3:AE:4F:E4:46:E9:62:3D:C8:7A:DD:01
X509v3 Authority Key Identifier:
keyid:C7:C0:E6:B7:A9:DD:12:CA:4F:3D:6D:68:82:96:21:45:F3:01:75:A4
X509v3 CRL Distribution Points:
Full Name:
URI:ldap:///CN=PEZEINVOICESCA4-CA(1),CN=PRZEINVOICEPKI4,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=extzatca,DC=gov,DC=local?certificateRevocationList?base?objectClass=cRLDistributionPoint
Authority Information Access:
CA Issuers - URI:ldap:///CN=PEZEINVOICESCA4-CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=extzatca,DC=gov,DC=local?cACertificate?base?objectClass=certificationAuthority
X509v3 Key Usage: critical
Digital Signature
1.3.6.1.4.1.311.21.7:
0-.%+.....7.............$......Uf...-...]..d...
X509v3 Extended Key Usage:
TLS Web Client Authentication, Code Signing
1.3.6.1.4.1.311.21.10:
0.0
..+.......0
..+.......
Signature Algorithm: ecdsa-with-SHA256
30:46:02:21:00:da:c5:60:54:98:9d:85:7f:25:9c:ab:21:54:
1e:09:2f:b6:01:63:e1:75:a7:8f:09:23:b3:4f:a4:ee:31:24:
34:02:21:00:96:7f:de:cb:60:d1:0a:ef:7e:7c:9a:c7:c0:44:
ad:62:4e:9d:e8:9b:c7:e7:27:7d:c0:e7:d4:ae:ad:18:c4:34
я тоже пробовал openssl_x509_parse
, но подпись не даёт
Если вы можете изменить библиотеку: phpseclib позволяет напрямую определять подпись сертификата.
@Topaco, можешь ли ты дать мне пример документации? пожалуйста
@Topaco огромное тебе спасибо, чувак, ты лучший, я использовал это, и это сработало. если хотите, вы можете написать это как ответ, чтобы принять его.
Пожалуйста. Я оставил свой комментарий в качестве ответа.
Вы же не проводите проверку сертификата сами, не так ли? Это гораздо больше, чем просто проверка подписей. Базовая проверка пути описана в RFC 5280, раздел 6, но проверка — это нечто большее, чем просто проверка пути. Задавать вопросы о том, как получить подпись из сертификата X509, — это явный признак того, что вы недостаточно знакомы с PKI на основе X509, чтобы выполнять пользовательскую проверку. Замечательно, если вы пытаетесь научиться это делать, поскольку это очень полезные знания, но ваша первая попытка — это не то, что вы хотите использовать по-настоящему.
@AndrewHenle Спасибо за ваш комментарий, проблема в том, что у меня есть проект по работе со счетами, и эти счета должны быть электронными, и они просят включить подпись сертификата в определенное поле счета. это все.
@momo Проверка соответствия подписи некоторого сертификата X509 набору байтов ничего не проверяет — сертификаты X509 являются общедоступными. Вы можете получить биты подписи из сертификата X509, который Google использует для своей поисковой системы, и наличие этих битов не доказывает, что вы Google. Что вам нужно сделать, так это попросить создателя счета использовать свой закрытый ключ, чтобы подписать некоторые данные в счете указанным способом. Именно так они доказывают, кто они есть: у них есть закрытый ключ, который нужно было использовать для создания этой подписи.
(продолжение) Затем вы используете открытый ключ сертификата X509 для проверки подписи. И если вы проверите X509 по доверенной цепочке сертификатов, это подтвердит ваше доверие к X509. Если я дам вам 30:64:02:30:13:19:f0:9f:88:d6:e9:e5:88:82:...
байты подписи из stackoverflow.com
X509 в вашем электронном счете, это ничего не докажет.
(продолжение) Вы можете использовать echo | openssl s_client -connect stackoverflow.com:443 -servername stackoverflow.com | openssl x509 -noout -text
, чтобы увидеть информацию о сертификате stackoverflow.com
X509, включая подпись.
@AndrewHenle Спасибо, Эндрю, но проблема в том, что я создаю клиентскую службу, а эту спецификацию создало правительство, и они просто сказали: просто поставьте подпись в счете-фактуре, а затем отправьте его нам... если честно, они творили сумасшедшие вещи, но мы ничего не можем сказать.
phpseclib позволяет напрямую определять подпись сертификата, s. Чтение сертификатов в документации phpseclib.
Сертификат загружается с помощью X509#loadX509()
, который возвращает массив с тремя полями сертификата tbsCertificate
, signatureAlgorithm
и signature
(см. RFC 5280). Последний содержит подпись.
Образец кода:
use phpseclib3\File\X509;
$certPem = "-----BEGIN CERTIFICATE-----
MIIEvzCCBGSgAwIBAgITeAAARlJXpwVPUYDkbAABAABGUjAKBggqhkjOPQQDAjBi
MRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEzARBgoJkiaJk/IsZAEZFgNnb3YxFzAV
BgoJkiaJk/IsZAEZFgdleHRnYXp0MRswGQYDVQQDExJQRVpFSU5WT0lDRVNDQTQt
Q0EwHhcNMjQwNzAyMDMxMTE2WhcNMjYwNzAyMDMyMTE2WjBJMQswCQYDVQQGEwJT
QTESMBAGA1UEChMJZnJlZSB0ZXh0MRIwEAYDVQQLEwlmcmVlIHRleHQxEjAQBgNV
BAMTCWZyZWUgdGV4dDBWMBAGByqGSM49AgEGBSuBBAAKA0IABP/Lh6KN1Y5Z6CzW
+3hLkwGLUyorKQoCCQZzrbtvHNLocvv7OFDUHZZiOnwzhavZLkOEPjH8ojIH1kb1
A4LsTEejggMTMIIDDzB+BgNVHREEdzB1pHMwcTEgMB4GA1UEBAwXMS1TaGFFa3wy
LVNoYUVrfDMtU2hhRWsxHzAdBgoJkiaJk/IsZAEBDA8zMTExOTAyOTM3MDAwMDMx
DTALBgNVBAwMBDExMDAxDjAMBgNVBBoMBWphemFuMQ0wCwYDVQQPDARUZWNoMB0G
A1UdDgQWBBQJ9H6ej6E1lbOuT+RG6WI9yHrdATAfBgNVHSMEGDAWgBTHwOa3qd0S
yk89bWiCliFF8wF1pDCB5QYDVR0fBIHdMIHaMIHXoIHUoIHRhoHObGRhcDovLy9D
Tj1QRVpFSU5WT0lDRVNDQTQtQ0EoMSksQ049UFJaRUlOVk9JQ0VQS0k0LENOPUNE
UCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25m
aWd1cmF0aW9uLERDPWV4dHphdGNhLERDPWdvdixEQz1sb2NhbD9jZXJ0aWZpY2F0
ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9u
UG9pbnQwgc4GCCsGAQUFBwEBBIHBMIG+MIG7BggrBgEFBQcwAoaBrmxkYXA6Ly8v
Q049UEVaRUlOVk9JQ0VTQ0E0LUNBLENOPUFJQSxDTj1QdWJsaWMlMjBLZXklMjBT
ZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPWV4dHphdGNh
LERDPWdvdixEQz1sb2NhbD9jQUNlcnRpZmljYXRlP2Jhc2U/b2JqZWN0Q2xhc3M9
Y2VydGlmaWNhdGlvbkF1dGhvcml0eTAOBgNVHQ8BAf8EBAMCB4AwPAYJKwYBBAGC
NxUHBC8wLQYlKwYBBAGCNxUIgYaoHYTQ+xKG7Z0kh877GdPAVWaBnNgtg+XFXQIB
ZAIBEDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwMwJwYJKwYBBAGCNxUK
BBowGDAKBggrBgEFBQcDAjAKBggrBgEFBQcDAzAKBggqhkjOPQQDAgNJADBGAiEA
2sVgVJidhX8lnKshVB4JL7YBY+F1p48JI7NPpO4xJDQCIQCWf97LYNEK7358msfA
RK1iTp3om8fnJ33A59SurRjENA==
-----END CERTIFICATE-----";
$x509 = new X509();
$cert = $x509->loadX509($certPem);
$signature = $cert["signature"];
print(bin2hex($signature) . PHP_EOL); // 003046022100dac56054989d857f259cab21541e092fb60163e175a78f0923b34fa4ee312434022100967fdecb60d10aef7e7c9ac7c044ad624e9de89bc7e7277dc0e7d4aead18c434
$signature = substr($signature, 1); // remove leading 0x00
print(bin2hex($signature) . PHP_EOL); // 3046022100dac56054989d857f259cab21541e092fb60163e175a78f0923b34fa4ee312434022100967fdecb60d10aef7e7c9ac7c044ad624e9de89bc7e7277dc0e7d4aead18c434
Для опубликованного сертификата подпись представляет собой подпись ECDSA в формате ASN.1/DER, которую можно проанализировать с помощью анализатора ASN.1, например вот .
Обратите внимание, что по какой-то причине в начале добавляется ведущий 0x00 (возможно, последний байт предыдущей BIT STRING
кодировки 0x034900
, s. здесь), который следует удалить для правильного формата подписи ASN.1/DER.
спасибо, насчет 0x00, который стоит в первом, я думаю, это пробел, я избавился от него с помощью обрезки вот так: bin2hex(trim($cert["signature"]))
@момо, не делай этого. обрезка удалит законные байты из некоторых подписей, сделав их недействительными. Кажется, это работает, потому что 0x00 - это один из байтов, который удаляет обрезка, но он удаляет как в начале, так и в конце, и не останавливается после первого.
@momo — подписи ECDSA в кодировке ASN.1/DER начинаются с 0x30 (SEQUENCE
), поэтому ltrim()
(но не trim()
, комментарий Питера) будет вариантом. Однако другие подписи (например, RSA) вполне могут иметь ведущие значения 0x00, которые являются частью подписи, так что даже ltrim()
может удалить слишком много. Поэтому более надежно удалить ведущий байт 0x00.
@Topaco, спасибо, но как это решить?
@momo — Просто используйте substr(..., 1)
вместо trim()
/ltrim()
.
@brombeer показывает всю информацию о сертификате, не извлекая «только значение подписи».