В процесс разработки собственного основанного на CryptoApi механизма генерации запросов на сертификаты столкнулся со следующим непонятным явлением. Запросы на сертификацию одного и того же открытого ключа, сформированные при помощи системного компонента XEnroll и при помощи моей утилиты, полностью совпадают во всем кроме собственно значения ЭЦП запроса (используется КриптоПро CSP 3.0.3300.2 КС1, алгоритм ключа GOST R 34.10-2001, алгоритм подписи GOST R 34.11/34.10-2001). При этом обе подписи признаются валидными (!) всеми доступными мне инструментами проверки ЭЦП: certutil.exe из Windows Server 2003 Administration Tools Pack, КриптоПро УЦ, библиотекой
BouncyCastle, в которой имеется собственная (не использующая CryptoApi) реализация криптографических ГОСТ-алгоритмов. Сертификат, выпущенный КриптоПро УЦ на мой запрос, без проблем привязывается к ключевому контейнеру и в дальнейшем нормально функционирует (в частности, позволяет организовать TLS-сессию с защищенным веб-приложением).
Описанное поведение наблюдается в следующем тестовом сценарии:
- При помощи CryptoApi-функций CryptAcquireContext() и CryptGenKey() формируется ключевой контейнер и ключевая пара.
- Открытый ключ экспортируется при помощи функции CryptExportPublicKeyInfoEx() в ASN.1-структуру SubjectPublicKeyInfo.
- Формируется тело запроса (ASN.1-структура CertificationRequestInfo), содержащее имя субъекта, открытый ключ, расширения сертификата и другие атрибуты запроса. Элементы запроса подбираются и кодируются так, чтобы DER-представление тела запроса было идентично запросу, формируемому XEnroll'ом.
- Тело запроса, закодированное в DER, подписывается при помощи последовательности вызова функций CryptCreateHash(), CryptHashData() и CryptSignHash(). Полученный в результате массив байтов переворачивается (преобразование из little-endian в big-endian представление).
- Тело запроса, идентификатор использованного алгоритма подписи (ASN.1-структура AlgorithmIdentifier) и сама подпись (сконвертированная в BIT STRING) объединяются в ASN.1-структуру CertificationRequest.
- Полученная структура CertificationRequest кодируется в DER. Запрос №1 готов.
- Объект XEnroll настраивается на использование сформированного на первом шаге ключевого контейнера (при помощи свойства UseExistingKeySet).
- При помощи метода XEnroll.createRequest(XECR_PKCS10_V2_0, ...) формируется запрос №2.
- В итоге запросы №1 и №2 отличаются только значениями ЭЦП, представленными BIT STRING'ами (сравнение осуществлялось при помощи утилиты
GUIdumpASN), но при этом обе подписи признаются валидными.
Ниже приводятся примеры запросов №1 и №2, закодированные в Base64 (кажется, форум не позволяет прицепить файлы...):
Запрос №1Код:MIIF1DCCBYECAQAwggIVMSIwIAYJKoZIhvcNAQkBFhNhbmRyZXdAc2tia29udHVyLnJ1MQswCQYDVQQGEwJSVTE7MDkGA1UECB4yADYANgAgAC0
AIAQhBDIENQRABDQEOwQ+BDIEQQQ6BDAETwAgBD4EMQQ7BDAEQQRCBEwxITAfBgNVBAceGAQVBDoEMARCBDUEQAQ4BD0EMQRDBEAEMzEr
MCkGA1UECh4iBBcEEAQeACAEHwQkACAEIQQaBBEAIAQaBD4EPQRCBEMEQDEVMBMGA1UECx4MBCMEIAAgBBQEHwQfMTMwMQYDVQQDHioEIg
Q1BEEEQgQ+BDIESwQ5ACAEPQQwBDEEPgRAACAENAQwBD0EPQRLBEUxZTBjBgkqhkiG9w0BCQgeVgA2ADIAMAAwADcAOAAsACAEMwAuBBUEO
gQwBEIENQRABDgEPQQxBEMEQAQzACwAIAQ/BDUEQAAuBB4EQgQ0BDUEOwRMBD0ESwQ5ACwAIAA4AC0AMQA1MTAwLgYJKoZIhvcNAQkCEyEx
MjM0NTY3ODkwLTEyMzQ1Njc4OS0xMjM0NTY3ODkwMTIxLzAtBgNVBAweJgQ8BDUEPQQ1BDQENgQ1BEAAIARABDAENwRABDAEMQQ+BEIEOgQ4MT
8wPQYDVQQEHjYEGgQ+BEEEQgQ+BEMEQQQ+BDIAIAQQBD0ENARABDUEOQAgBBIEOAQ6BEIEPgRABD4EMgQ4BEcwYzAcBgYqhQMCAhMwEgYHKo
UDAgIkAAYHKoUDAgIeAQNDAARAyCwzwrTGaEK4F4A8glWa1viDSptCQESckSC7UB/+ttMptPLuCzg6goZg7/66RjGQfDpzlsAcjz1mut3gXve78KCCAvw
wGgYKKwYBBAGCNw0CAzEMFgo1LjEuMjYwMC4yMEAGCSsGAQQBgjcVFDEzMDECAlssDBFsaXQtYW5kcmV3LmtvbnR1cgwNS09OVFVSXGFuZHJldww
JbnVuaXQuZXhlMIIBGwYKKwYBBAGCNw0CAjGCAQswggEHAgEBHnYAQwByAHkAcAB0AG8ALQBQAHIAbwAgAEcATwBTAFQAIABSACAAMwA0AC4A
MQAwAC0AMgAwADAAMQAgAEMAcgB5AHAAdABvAGcAcgBhAHAAaABpAGMAIABTAGUAcgB2AGkAYwBlACAAUAByAG8AdgBpAGQAZQByA4GJAGIv
EvFcI4DJj4+ctIbm6V0/eZcPB3v/xn78G1ucopJkhu1OveAiSm4sdwyklaWsGM/XjQ5EpKOey3vqJRO4JHh2YOQl1MKnSlb7MI0ZPG+sUrnSMJToFMzMiU1z
IMzuYQBYrvwaJG6N/weImkK+J+FxybBfd9P+Ec/0ALXNK1xSAAAAAAAAAAAwggF7BgkqhkiG9w0BCQ4xggFsMIIBaDAOBgNVHQ8BAf8EBAMCBPAwG
QYJKoZIhvcNAQkPBAwwCjAIBgYqhQMCAhUwTgYDVR0lBEcwRQYIKwYBBQUHAwIGCCsGAQUFBwMEBgcqhQMCAiIGBgorBgEEAYI3FAICBggqhQMCG
QEDAQYGKoUDAwcBBggqhQMDBwEBATCB6gYDVR0RBIHiMIHfgRNhbmRyZXdAc2tia29udHVyLnJ1oDIGCSqFAwMHAQEBAaAlDCMxMjM0NTY3ODkwM
TItMTIzNDU2Nzg5LTEyMzQ1Njc4OTAxMqATBgkqhQMDBwEBAwGgBgwEMTIzNKAUBgkqhQMDBwEBBAGgBwwFNjYwMDCgFAYJKoUDAwcBAQUBoAcM
BTY2LTAwoDUGCisGAQQBgjcUAgOgJwwlQW5kcmV3S29zdG91c292QGtvbnR1ci50cnVzdHBvcnRhbC5ydaAcBgoqhQMDBwMEAgEBoA4MDEtvbnR1ckV
zdGFtcDAKBgYqhQMCAgMFAANBAKW7DDL45tAg1UPAOFA3rFUNL5E4QgbbvsXdDpy+6oJTVhf0PcIp/f8qAoC6Cr6m2t/uCmXSaiZe9XauqRjLD+M=
Запрос №2Код:MIIF1DCCBYECAQAwggIVMSIwIAYJKoZIhvcNAQkBFhNhbmRyZXdAc2tia29udHVyLnJ1MQswCQYDVQQGEwJSVTE7MDkGA1UECB4yADYANgAgAC0
AIAQhBDIENQRABDQEOwQ+BDIEQQQ6BDAETwAgBD4EMQQ7BDAEQQRCBEwxITAfBgNVBAceGAQVBDoEMARCBDUEQAQ4BD0EMQRDBEAEMzEr
MCkGA1UECh4iBBcEEAQeACAEHwQkACAEIQQaBBEAIAQaBD4EPQRCBEMEQDEVMBMGA1UECx4MBCMEIAAgBBQEHwQfMTMwMQYDVQQDHioEIg
Q1BEEEQgQ+BDIESwQ5ACAEPQQwBDEEPgRAACAENAQwBD0EPQRLBEUxZTBjBgkqhkiG9w0BCQgeVgA2ADIAMAAwADcAOAAsACAEMwAuBBUEO
gQwBEIENQRABDgEPQQxBEMEQAQzACwAIAQ/BDUEQAAuBB4EQgQ0BDUEOwRMBD0ESwQ5ACwAIAA4AC0AMQA1MTAwLgYJKoZIhvcNAQkCEyEx
MjM0NTY3ODkwLTEyMzQ1Njc4OS0xMjM0NTY3ODkwMTIxLzAtBgNVBAweJgQ8BDUEPQQ1BDQENgQ1BEAAIARABDAENwRABDAEMQQ+BEIEOgQ4MT
8wPQYDVQQEHjYEGgQ+BEEEQgQ+BEMEQQQ+BDIAIAQQBD0ENARABDUEOQAgBBIEOAQ6BEIEPgRABD4EMgQ4BEcwYzAcBgYqhQMCAhMwEgYHKo
UDAgIkAAYHKoUDAgIeAQNDAARAyCwzwrTGaEK4F4A8glWa1viDSptCQESckSC7UB/+ttMptPLuCzg6goZg7/66RjGQfDpzlsAcjz1mut3gXve78KCCAvw
wGgYKKwYBBAGCNw0CAzEMFgo1LjEuMjYwMC4yMEAGCSsGAQQBgjcVFDEzMDECAlssDBFsaXQtYW5kcmV3LmtvbnR1cgwNS09OVFVSXGFuZHJldww
JbnVuaXQuZXhlMIIBGwYKKwYBBAGCNw0CAjGCAQswggEHAgEBHnYAQwByAHkAcAB0AG8ALQBQAHIAbwAgAEcATwBTAFQAIABSACAAMwA0AC4A
MQAwAC0AMgAwADAAMQAgAEMAcgB5AHAAdABvAGcAcgBhAHAAaABpAGMAIABTAGUAcgB2AGkAYwBlACAAUAByAG8AdgBpAGQAZQByA4GJAGIv
EvFcI4DJj4+ctIbm6V0/eZcPB3v/xn78G1ucopJkhu1OveAiSm4sdwyklaWsGM/XjQ5EpKOey3vqJRO4JHh2YOQl1MKnSlb7MI0ZPG+sUrnSMJToFMzMiU1z
IMzuYQBYrvwaJG6N/weImkK+J+FxybBfd9P+Ec/0ALXNK1xSAAAAAAAAAAAwggF7BgkqhkiG9w0BCQ4xggFsMIIBaDAOBgNVHQ8BAf8EBAMCBPAwG
QYJKoZIhvcNAQkPBAwwCjAIBgYqhQMCAhUwTgYDVR0lBEcwRQYIKwYBBQUHAwIGCCsGAQUFBwMEBgcqhQMCAiIGBgorBgEEAYI3FAICBggqhQMCG
QEDAQYGKoUDAwcBBggqhQMDBwEBATCB6gYDVR0RBIHiMIHfgRNhbmRyZXdAc2tia29udHVyLnJ1oDIGCSqFAwMHAQEBAaAlDCMxMjM0NTY3ODkwM
TItMTIzNDU2Nzg5LTEyMzQ1Njc4OTAxMqATBgkqhQMDBwEBAwGgBgwEMTIzNKAUBgkqhQMDBwEBBAGgBwwFNjYwMDCgFAYJKoUDAwcBAQUBoAcM
BTY2LTAwoDUGCisGAQQBgjcUAgOgJwwlQW5kcmV3S29zdG91c292QGtvbnR1ci50cnVzdHBvcnRhbC5ydaAcBgoqhQMDBwMEAgEBoA4MDEtvbnR1ckV
zdGFtcDAKBgYqhQMCAgMFAANBAAz2Jehe9WJ002W2LptclOtUe3fT+3B4Y862UPz3y0PlwewWkiXSCXTkjnNHBYGqCrGKpCkcEn7xyok8zTnHyww=
Интересно, что в том же сценарии при использовании системного RSA-криптопровайдера обе подписи получаются одинаковыми (как я и ожидал).
Каковы могут быть причины описанного поведения?
И главное, должен ли я переживать по этому поводу? :-) В принципе, запросы обрабатываются, сертификаты выдаются и все вроде бы работает...