diff --git a/examples/data/cert/26a91b3f.0 b/examples/data/cert/26a91b3f.0 new file mode 100644 index 00000000..ee80a731 --- /dev/null +++ b/examples/data/cert/26a91b3f.0 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlQdWJs +aWMgQ0EwIhgPMjAyNDA1MTMxOTI5MjhaGA8yMDM5MDUxMzE5MjkyOFowFjEUMBIG +A1UEAxMLUGVyc29uYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCssG1zXFpUHMMkiIuRfQ6SBFsGzhx+oScng8xjwo0kTJZaAUfQhAxD1YYg+uqC +p8JzH9iC2PAz+kpg/j5pWrf6WX9zL1Va8LWPQyBdTPKa9MwjkeTYOhvDKQbhGVfY +z8DAWkzMFiE9aEgu9Hl6u8CaMZLhnMUrm8CMsJcrJ/NIiRBIHYTDGf706OhCzu5b +bRZw2RTfjeqIheH09z9mVqnEyuHU5rKeVjhMftVbE6GXDdNRG/ARxlVo0dgBelaQ +rCf5uvVjARALzD1r/8DfImkpIA+OfMA2CBQeMy+8OAUZRWPI3tjGZdzLjSOXg33r +hUhR9MYnSHBaRoqJUYTwCkxxAgMBAAGjggEAMIH9MB0GA1UdDgQWBBTpyNlCFqQn +2qhkmOlyy3Pdf0eC6DAfBgNVHSMEGDAWgBTUe13jd2Z2QcE2fXaHqWzu1WmmnTBj +BggrBgEFBQcBAQRXMFUwLwYIKwYBBQUHMAKGI2h0dHA6Ly9sb2NhbGhvc3QvY2Ev +UHVibGljJTIwQ0EuY3J0MCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0L29j +c3AvMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9sb2NhbGhvc3QvY3JsL1B1Ymxp +YyUyMENBLmNybDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAQEAJZr10PkZEbqYGr29DoDHxetbRdpnAvm+n8bO9hA7LzY4 +puw06LERRpNIWdxP6Gqvx69aYIoPCRUfvX1ogaE7kXLpjhq+Wpv662U8BLbCa69T +l0C2JFhp+9X+QUhPnxP1hLSIlzkY6cSZ+AhNgEK6v2CTFs45i86KoaGAAP+OR9Xi +vz0GdrOf9ig/KITXWWHCMioGdricB1fhAEBujnGJeDUu846mU4LC014U6Pvd8tWI +K2ADFR1Ya5fCxvT2KgUIeFrrdfnQmpoTAnUWSkmyxOjQhZbLz1KjLrzGsHoAdBpX +UbxvGSm3aNLmRqoaay/gMIYKUaWEI+XYHFZEY1I+Zw== +-----END CERTIFICATE----- diff --git a/examples/data/cert/5a0ce691.0 b/examples/data/cert/5a0ce691.0 new file mode 100644 index 00000000..68c7392c --- /dev/null +++ b/examples/data/cert/5a0ce691.0 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIBAjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDExRJbnRl +cm1lZGlhdGUgQ0EgVGVzdDAiGA8yMDI0MDUxMTE4NDEwM1oYDzIwNDMwNTExMTg0 +MTAzWjAUMRIwEAYDVQQDEwlQdWJsaWMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCR5YVeDNgKS6G2dVSIW2U6t62gjLNXX0K/wGAZuqSvAL93ouDl +OmNLumn7OAo7Dl6nc06Rnmuz4F5bw+Odf6G2ipoT4Wvy0rw6N0Qm4WoFTrW4wt9E +uesSklfALBix9sQYFut5ytEw+PufG5i45RqqpZy8CG7LbJF35ky8uIrF92fNm/ay ++K2pbsFD2i9c2E5pWNHRKmOnsHMZ0N+v/elcuzjD+rxzNi76MaR5m6hb3tMY6Vgh +HI3yywJhWSmsQ61f1VswsEuZhxbr2h/hJFzEtgd+z8JfKsbGAsSGwLWOIiGdLSrJ +I4/1PbgdZIUn4xTlNSvuWw2OvxkymoHjlI6BAgMBAAGjggEbMIIBFzAdBgNVHQ4E +FgQU1Htd43dmdkHBNn12h6ls7tVppp0wHwYDVR0jBBgwFoAUEVBtMxMV+BGeGX/A +9ThzMAYi2tgwcAYIKwYBBQUHAQEEZDBiMDwGCCsGAQUFBzAChjBodHRwOi8vbG9j +YWxob3N0L2NhL0ludGVybWVkaWF0ZSUyMENBJTIwVGVzdC5jcnQwIgYIKwYBBQUH +MAGGFmh0dHA6Ly9sb2NhbGhvc3Qvb2NzcC8wQgYDVR0fBDswOTA3oDWgM4YxaHR0 +cDovL2xvY2FsaG9zdC9jcmwvSW50ZXJtZWRpYXRlJTIwQ0ElMjBUZXN0LmNybDAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AQEA04noC97jcVo7rYPUwkfrUZlAyrC07j9bazV8NkZlGLQh0JqgP6/ZQ2UPp18d +ruP66eEOf4UDOedtmOSSVf3B8klL+ZyBxotSuWw8B9VMYhc3G4K7qClFSp0kj2uM +Zrbai5qa8QwU4wvz6w2AhQ01kVbo4GDMw60/iLZOym0TJWIdjhHcD8MU1gr1rkF0 +GIauvqpFmP3gzwnhirVsVBpsvMgsAcOL+eJ1gLfr2EeWtxQldHTaGlf/CSxq1/nN +Ffs/xCVoi6AmusoUmpF6QaWAGqnoW0P04qVrZoHz1ZZvDYDAV1ZB4wocynzGRDkc +qCGQb1Y4kFkhehUUKrXcnKM/UQ== +-----END CERTIFICATE----- diff --git a/examples/data/cert/71ab782e.0 b/examples/data/cert/71ab782e.0 new file mode 100644 index 00000000..ee867804 --- /dev/null +++ b/examples/data/cert/71ab782e.0 @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEwjCCAqqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDEwxSb290 +IENBIFRlc3QwIhgPMjAyNDA1MTExODM5NTFaGA8yMDQ0MDUxMTE4Mzk1MVowHzEd +MBsGA1UEAxMUSW50ZXJtZWRpYXRlIENBIFRlc3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDXqS/QL4alYkE6jXQgpRcEdsypoOnniw3mBJtWgyN/OLlc +RAoppEkQlj06j3hKLoc5VcSSLM4o6texHj6bU8f/j5bItY6tC5jnzv9bRatryc+5 +n67yjEViH0BvoQsxetuCeskTqY5iFbQSz4329rWmZQaBdrCodzYi2HtNi4vuLqT/ +HrbLsjbcW9OaQgg5ErQiBTxheCFf1wip1ceytBFSelcfOiqzUj6WXZcHT79NgCVH +8PSvPNQYhdjwU5+JSl6sQ1nRIxZ+KYEAVidfipp+jNGYSKDTs26lFYVKQHQDB91V +RK/pFXIPCfdYsM355zIsD+cgO8pe8TTycSEIZcD/AgMBAAGjggELMIIBBzAdBgNV +HQ4EFgQUEVBtMxMV+BGeGX/A9ThzMAYi2tgwHwYDVR0jBBgwFoAUcsmsDkmic5JU +9HLw9k61FYV1ra4waAYIKwYBBQUHAQEEXDBaMDQGCCsGAQUFBzAChihodHRwOi8v +bG9jYWxob3N0L2NhL1Jvb3QlMjBDQSUyMFRlc3QuY3J0MCIGCCsGAQUFBzABhhZo +dHRwOi8vbG9jYWxob3N0L29jc3AvMDoGA1UdHwQzMDEwL6AtoCuGKWh0dHA6Ly9s +b2NhbGhvc3QvY3JsL1Jvb3QlMjBDQSUyMFRlc3QuY3JsMA4GA1UdDwEB/wQEAwIB +hjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCWOWSUEDeRxP2a +DpLHwa6aedFjkwsudIsBt1gFqey4wkbFxAxnbUSL0jlnGTa+QtPS+4NZipjwTMWT +l8xZVCebya4R2l3wmormesFEZ4q2X1FNjqBC8k1LvzvDRCAqoqo3UAYqcIYPN9R/ +kdmQQm8e1TXbqMa9tH68M+D3vP8bx1thD4BaIltlCB52vlROhJxNjOhuD0V6KdFQ +BdCZqOpLUZuLO1hsmy2v4cgBJLH7aGqmEu3iFSe5lEPMHvA/8qNQ3jVEhV725yKC +9qm6KHZu+VLcJi54LtvJa1XM9VWFPerIp8UbmDOcVD8qaZaIp07TwubmjJGhO6up +4psYm7vxP3UsD2tviG+7qpOP/bOxCKa4Alengk3AAUt5S2y5xI9tX8qAfWpkW0GG +ZflUm+ayt1Zg1HRS9h2WFjeUTuxubS7uexwJFn2dUC0Sor1nN3Mim8k4uv++pPBu +c9c5w7KwsygNDRhMJEjlVa36BACmN7y7YyY7POZQmIYHJtzhDuHoaXpzbBsTat2V +CG4VozT0JeKQfo+w50K1Oh+tweTneDfBSomau/7Uh1fzbMX4WcvvQYio14oWCoJS +zIe48cJD7afiDsv39y/3G2Bp3qZ/PZIzhA84nyfcbpUOkbyhrFV5Xl48+axFMRrP +kvbP86Zybll5qscOHklYACNocGHTzA== +-----END CERTIFICATE----- diff --git a/examples/data/cert/7262ea48.0 b/examples/data/cert/7262ea48.0 new file mode 100644 index 00000000..1d192632 --- /dev/null +++ b/examples/data/cert/7262ea48.0 @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE7zCCAtegAwIBAgIBATANBgkqhkiG9w0BAQ0FADAXMRUwEwYDVQQDEwxSb290 +IENBIFRlc3QwIhgPMjAyNDA1MTExNzU4MjNaGA8yMDc0MDUxMTE3NTgyM1owFzEV +MBMGA1UEAxMMUm9vdCBDQSBUZXN0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAwhg2cjBOC/nq5Mt9l6yfBvO4tKiLqUmMCHUCRlouKplVUsnzfmspRBdx +OeNfoHrvCETeIntuY3Hx3outNfUtQVQPqDqs0itPW69bjdJrFiQ8Eo9qjbCH3rKS +eB1/xUGlu+jOeMYYBBYSMCm4j2Z6if4qoy07k63LeAi+2gFtaZQ11pNAachVCLsQ +Z2flPgngPEPpZhYY4hjygdrMPcbuvRXDgSREQCro1+vo1igjBwrXD8bJhMyGlwdk +YDndFzhjTSFWX0sH5MQCfhKa/UZqS5viCFdx4Gzu0C3pw72rUWO/ig3SpYBLRDFU +lXw9osIHTx6/0azR9htVvJEFFqX5jci0dAjHmddrvYj4TxY7wisKleJMdJDTXrTu +fV5uiszlJ+lVPHBRhxh8Fn76+gdMXHzu/CoHTsCwR2ivQry6duKNzil+cgLchVL4 +lfcd6VxxYF/winx8YPWYDajxdhBxM08FHZQdvWvYkSXI/FbeI0vFjjLIfUFZBP6M +3n+VpgbzDZ5f0Jp3338NWfmX42LHidbw/WGu+ylRnqlV5xQ2TQ61L4PnTOHVzktR +UtxoKP6wX1j6F3TjE44HAqgTVdkCCmskSbFnMuK5Igs0UTRtPkrjWilefBn95jm2 +PvL4XHwDS6jSEehYmaRfJ0KYfcigbP5T+gaNlIEczAfKTaVbVakCAwEAAaNCMEAw +HQYDVR0OBBYEFHLJrA5JonOSVPRy8PZOtRWFda2uMA4GA1UdDwEB/wQEAwIBhjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4ICAQCvtiQWXKQf3bKD7/At +hpWLRZXLb8Rlqg3OjZo70OBX7LcRVSGsmLjgy0OU/nTpwpzVR80AgmUemFdmImJl +dMXPsAucs7vLbnJXBScG1y3Goa9K6ocWp0uELonLcHmScLGV4jWSBUp7TFcJWTzy +lR4Ttz1rQ0XacSGWuQ+js0tabTCVX9JxAn+qtHOjYBR5gvrIYLGFpOjRwX71qD6K +NyTQAFGZV2VqErw+5GJCue9Rs9Yc7mWPXuECF9mELjEWF+5pG8HGMkHShT2qth4Z +VLehJPy9iaxnFRhsOvIq+JtFVFAUmYgZg0C0xwjqqoKGa7/rpNWznowm1NTcTb6r +QnlB7nZL6eyfUql7gGELinBU5nzXgYoMf7+WsKp1dEoZg5EM0+5zHyYFXXEdmbvb +xXLVSzaW7XTzDVM6xCZv1Q5+lA6jGKsrD/l5R6yYP6BTpKZgRpLQ/h834P5TcyQq ++3uh01SESdvSGn8JOcRY8WTcPMcUPQwV4iHTdZ9yZe0Gu6WYUGXPB6IFx0MJT4cV +cOEJhKEhahBbxH01dNOAs8iYzCL7rlXu0uX6RxBUQ/2u1Pbo0Ydb6KRSPkNqw6yh +QWdo6LweoHn7dTobXEt5btIKWO7aCfJU3ZUeQ7TjfQJDXD2vAK/RPK8R1vNKxau5 +fYQbDNIG/CWb1dl0Qh1KDiu83Q== +-----END CERTIFICATE----- diff --git a/examples/data/cert/PDF Signing CA.crt b/examples/data/cert/PDF Signing CA.crt new file mode 100644 index 00000000..66c220e7 --- /dev/null +++ b/examples/data/cert/PDF Signing CA.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIBAjANBgkqhkiG9w0BAQ0FADAWMRQwEgYDVQQDEwtQZXJz +b25hbCBDQTAiGA8yMDI0MDUxMzE5MzMxOVoYDzIwMzcwNTEzMTkzMzE5WjAZMRcw +FQYDVQQDEw5QREYgU2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAOJ0Pbd0og+olkz/4aRw5vLXS1YnsB3ourtpjEo23bck1QMAO4d6DF8e +onuMrs0FeICyZHhfUjw0lndllgtzoRlcrC6qSuu5V7NCz3ZbiPUSb32vqaMgjRHM +kQAkucdvbe8KImE2TaU/RcdpjpsbW+/JoL3kkqoili3v1MwnCGsbET83vMoR2GsB +Jio3+Cl/+fJePx1dgnJXPHEG0EPzpdZfok1OQgs9IBVDNriMwQVMyTeijJTqSoED +Swqku5p27YH4cY2ie0FKc9uGHb4YjEC+XMbKSwgtr2AKgKBksj6t2GP5YuBfUEOA +gNKEc4DqUQzhILLtZHm73zB4xt0qmdMCAwEAAaOCAQUwggEBMB0GA1UdDgQWBBRr +t40sVU95nZ3Ym/9wQQcN34RcLTAfBgNVHSMEGDAWgBTpyNlCFqQn2qhkmOlyy3Pd +f0eC6DBlBggrBgEFBQcBAQRZMFcwMQYIKwYBBQUHMAKGJWh0dHA6Ly9sb2NhbGhv +c3QvY2EvUGVyc29uYWwlMjBDQS5jcnQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2Nh +bGhvc3Qvb2NzcC8wNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2xvY2FsaG9zdC9j +cmwvUGVyc29uYWwlMjBDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQENBQADggEBAI8Zf5mWpP1ArERZ/D/umTKIcafCDGEY +tIvDDjlOc5KegPiZHa1MUzyL1hdyr/CIHaOA9tzBiLyCthrJkIQB8sHbrIUDOmyJ +V5fcdvUFyco9Rmn+WH0nubmx3fDdP6kbhJ3sta9ejLKlLKV9zi8/EILy133Shbgq +My5EAeauAubFhxQ9mICSSLvGt557S12ITroTzu46+DnX0QJ0uT7KtTgR91RZP0Lo +1hX4MjhniIL1MgOdaPpHHTJm+5Lgr3ldGp0jySuqCcEaxxWGQJ+U3UhfKydGJB+J +CzG0tP7+RN2UHZJJ1CrujReH6q9qFi1HJTmmvHmtfEP4FWsmnMRghfw= +-----END CERTIFICATE----- diff --git a/examples/data/cert/b6ce4782.0 b/examples/data/cert/b6ce4782.0 new file mode 100644 index 00000000..66c220e7 --- /dev/null +++ b/examples/data/cert/b6ce4782.0 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIBAjANBgkqhkiG9w0BAQ0FADAWMRQwEgYDVQQDEwtQZXJz +b25hbCBDQTAiGA8yMDI0MDUxMzE5MzMxOVoYDzIwMzcwNTEzMTkzMzE5WjAZMRcw +FQYDVQQDEw5QREYgU2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAOJ0Pbd0og+olkz/4aRw5vLXS1YnsB3ourtpjEo23bck1QMAO4d6DF8e +onuMrs0FeICyZHhfUjw0lndllgtzoRlcrC6qSuu5V7NCz3ZbiPUSb32vqaMgjRHM +kQAkucdvbe8KImE2TaU/RcdpjpsbW+/JoL3kkqoili3v1MwnCGsbET83vMoR2GsB +Jio3+Cl/+fJePx1dgnJXPHEG0EPzpdZfok1OQgs9IBVDNriMwQVMyTeijJTqSoED +Swqku5p27YH4cY2ie0FKc9uGHb4YjEC+XMbKSwgtr2AKgKBksj6t2GP5YuBfUEOA +gNKEc4DqUQzhILLtZHm73zB4xt0qmdMCAwEAAaOCAQUwggEBMB0GA1UdDgQWBBRr +t40sVU95nZ3Ym/9wQQcN34RcLTAfBgNVHSMEGDAWgBTpyNlCFqQn2qhkmOlyy3Pd +f0eC6DBlBggrBgEFBQcBAQRZMFcwMQYIKwYBBQUHMAKGJWh0dHA6Ly9sb2NhbGhv +c3QvY2EvUGVyc29uYWwlMjBDQS5jcnQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2Nh +bGhvc3Qvb2NzcC8wNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2xvY2FsaG9zdC9j +cmwvUGVyc29uYWwlMjBDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQENBQADggEBAI8Zf5mWpP1ArERZ/D/umTKIcafCDGEY +tIvDDjlOc5KegPiZHa1MUzyL1hdyr/CIHaOA9tzBiLyCthrJkIQB8sHbrIUDOmyJ +V5fcdvUFyco9Rmn+WH0nubmx3fDdP6kbhJ3sta9ejLKlLKV9zi8/EILy133Shbgq +My5EAeauAubFhxQ9mICSSLvGt557S12ITroTzu46+DnX0QJ0uT7KtTgR91RZP0Lo +1hX4MjhniIL1MgOdaPpHHTJm+5Lgr3ldGp0jySuqCcEaxxWGQJ+U3UhfKydGJB+J +CzG0tP7+RN2UHZJJ1CrujReH6q9qFi1HJTmmvHmtfEP4FWsmnMRghfw= +-----END CERTIFICATE----- diff --git a/examples/data/cert/long.pfx b/examples/data/cert/long.pfx new file mode 100644 index 00000000..7caa4fb9 Binary files /dev/null and b/examples/data/cert/long.pfx differ diff --git a/examples/data/cert/longChain.pem b/examples/data/cert/longChain.pem new file mode 100644 index 00000000..07342cc2 --- /dev/null +++ b/examples/data/cert/longChain.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIDbzCCAlegAwIBAgIBBDANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDEw5QREYg +U2lnbmluZyBDQTAiGA8yMDI0MDUxMzIwNTYyMFoYDzIxMTQwNTEzMjA1NjIwWjAR +MQ8wDQYDVQQDEwZVc2VyIDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPsE +bWmWn6umOySB6DySZK7xBlotEdnE4kHJ7eVdIN1SP5M9/92N+WN2X03C/PkXjAS/ +KeoWfeyBNykfGkRkNGVYY8L8qvM02Q3LxWIO6QiLK7NUZt31fFZi0RRlqcKK45pA +tjsgdYIUYySuYhfPYXw4hHzkWaMJNME0JCzgH72dAgMBAAGjggFIMIIBRDAdBgNV +HQ4EFgQUA/NECRdmtM8L2UBlBhfA3OhKORcwHwYDVR0jBBgwFoAUa7eNLFVPeZ2d +2Jv/cEEHDd+EXC0wagYIKwYBBQUHAQEEXjBcMDYGCCsGAQUFBzAChipodHRwOi8v +bG9jYWxob3N0L2NhL1BERiUyMFNpZ25pbmclMjBDQS5jcnQwIgYIKwYBBQUHMAGG +Fmh0dHA6Ly9sb2NhbGhvc3Qvb2NzcC8wPAYDVR0fBDUwMzAxoC+gLYYraHR0cDov +L2xvY2FsaG9zdC9jcmwvUERGJTIwU2lnbmluZyUyMENBLmNybDALBgNVHQ8EBAMC +B4AwPQYDVR0lBDYwNAYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYB +BQUHAwQGCisGAQQBgjcKAwQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOC +AQEAp3Au1XXaiieStbk4aySQxlpcn0dYx+Uf97o/V/P5x4kHQn9GD1vNYNK7eED5 +fN+wXq39IP7b3N9Ajq9I+bhIl0JmeOTrUFJIZtsCk+0vilpf0vi+Mm3q8InVZ512 +ihjS4fXRvoN0vQWVYbvRB+C71q14V0EJx+7vSE069WUDym+k+LoIMBEiq3jDZhN5 +2UMcC+NgV1359skxrcqBs9MXDDvxoA3a7IR1CBCBSekM4SwkVCjwdSBH2p0zBG4i +bXbkN6WkSEftZSG5S2fgIW3INFwEvdUryuMkKT7DpyoE1velos1eiyWMsyxo5uIA +pmwC89FZWSTBeUf1DzoTLQssPg== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAPsEbWmWn6umOySB +6DySZK7xBlotEdnE4kHJ7eVdIN1SP5M9/92N+WN2X03C/PkXjAS/KeoWfeyBNykf +GkRkNGVYY8L8qvM02Q3LxWIO6QiLK7NUZt31fFZi0RRlqcKK45pAtjsgdYIUYySu +YhfPYXw4hHzkWaMJNME0JCzgH72dAgMBAAECgYEApQygNJFJo7/Fm5efcFSBx1XL +63c/CYWfj8qKlgnKFjyCePqHRtKXbILk32iald6ZvnVDxhvL6Lp93pgI2XdZywwZ +ldUCqjxHP2uGqETZH9LsevV//UG2IJHx5a+BwNGXpQdBAijXHDG3G4mJEHZEXSsW +ll8vdovkyhR5Y8HEJjECQQD+hIYn3ZplkDrzhPToCtVYt92seplM6Wf08WgdJpGY +tk0t6KOzvoxEkC66MRJNfaSiZ1gJPtRkwgIP++3hVnrHAkEA/HqvNiIE9LnwFKUo +z+L/A1QTbaNk5RotbL4AuqBSpV8wIvMtJSH1l9GNIYcmoxseVRlztxGl6o2olSFU +YxdAewJBALyKJ2c4vbsm0MS8ZT/PZqPF8KxoVU+oR54nhlyxyYE66olroHjyWq4f +sL20xD4rKN41d/3BUU0ieqykj18x1NkCQC4y5Myq/O0q+NtOPWGMz8z0HXn6ZAPn +/lffjJKZs4W/HTfH94LL6/yTw5dW+6Ls22gG+D2PZSS+NxLSvnZDUvECQQDVZ+1p +p0wym0kPcFO1Gah++Z7Ch5ZbM7/l82YsHW2BW7nkYlgFIIDugicYW0/yoh1gBIZC +rMPKhCGqyDtQ8T2s +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/examples/data/cert/longChain.pfx b/examples/data/cert/longChain.pfx new file mode 100644 index 00000000..7caa4fb9 Binary files /dev/null and b/examples/data/cert/longChain.pfx differ diff --git a/examples/data/cert/ocspTest.bat b/examples/data/cert/ocspTest.bat new file mode 100644 index 00000000..048d1364 --- /dev/null +++ b/examples/data/cert/ocspTest.bat @@ -0,0 +1,3 @@ +openssl ocsp -text -url http://localhost/ocsp/ -issuer "PDF Signing CA.crt" -CApath "." -nonce -signer "longChain.pem" -cert "longChain.pem" +pause +ocspTest.bat \ No newline at end of file diff --git a/examples/example_012.pdf b/examples/example_012.pdf deleted file mode 100644 index eec8ee0c..00000000 Binary files a/examples/example_012.pdf and /dev/null differ diff --git a/examples/example_052.php b/examples/example_052.php index 10cfb2e4..e114ea4e 100644 --- a/examples/example_052.php +++ b/examples/example_052.php @@ -76,7 +76,10 @@ */ // set certificate file -$certificate = 'file://data/cert/tcpdf.crt'; +$certificate = file_get_contents("data/cert/longChain.pem"); +//$issuer = "data/cert/Root CA Test.crt"; +$extracerts = false; +//$crl = "data/cert/RootCATest.der.crl"; // set additional information $info = array( @@ -86,8 +89,19 @@ 'ContactInfo' => 'http://www.tcpdf.org', ); +// set LTV setLtv($ocspURI=false, $crlURIorFILE=false, $issuerURIorFILE=false) +// set false to skip ocsp or crl +// set to empty or null will continue lookup in certificate attributes (Authority Info Access(AIA) and CRL Distribution Points(CDP)) +// skip both will result no LTV. Note that issuerURIorFILE cannot skipped. It will use given address/file location or lookup in cert AIA attributes if null. +// dont give any parameter to lookup all address in cert attributes. +$pdf->setLtv(); + +// Set TSA server address +$pdf->setTimeStamp('http://timestamp.apple.com/ts01'); + // set document signature -$pdf->setSignature($certificate, $certificate, 'tcpdfdemo', '', 2, $info); +// added digest algorithm in last arguments +$pdf->setSignature($certificate, $certificate, 'tcpdfdemo', $extracerts, 2, $info, 'sha256'); // set font. 'helvetica' MUST be used to avoid a PHP notice from PHP 7.4+ $pdf->setFont('helvetica', '', 12); @@ -117,7 +131,12 @@ //Close and output PDF document $pdf->Output('example_052.pdf', 'D'); +//$r=$pdf->Output('example_052.pdf', 'S'); +//$h = fopen('../example_052.pdf','w'); +//fwrite($h, $r); +//fclose($h); //============================================================+ // END OF FILE //============================================================+ +?> \ No newline at end of file diff --git a/include/tcpdf_cmssignature.php b/include/tcpdf_cmssignature.php new file mode 100644 index 00000000..35491914 --- /dev/null +++ b/include/tcpdf_cmssignature.php @@ -0,0 +1,1847 @@ +writeLog) { + $logs = date("Y-m-d H:i:s")."\n"; + $logs .= "========== START LOG ==========\n"; + $arrLog = explode("\n", rtrim($this->log)); + $newlines = ''; + foreach($arrLog as $line) { + $head = trim(substr($line, 0, strpos($line, ":"))); + $newhead = str_pad($head, 10, " "); + $ct = rtrim(substr($line, strpos($line, ":")+1)); + $newline = "$newhead: $ct\n"; + $newlines .= $newline; + } + $logs .= $newlines; + $logs .= "========== END LOG ==========\n\n"; + // echo "
$logs
"; + if(@$h = fopen($this->logFile, 'w')) { + fwrite($h, $logs); + fclose($h); + } + } + } + + + /** + * send tsa/ocsp query with curl + * @param array $reqData + * @return string response body + * @public + */ + public function sendReq($reqData) { + if (!function_exists('curl_init')) { + $this->Error('Please enable cURL PHP extension!'); + } + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $reqData['uri']); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0); + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: {$reqData['req_contentType']}",'User-Agent: TCPDF')); + curl_setopt($ch, CURLOPT_POSTFIELDS, $reqData['data']); + $tsResponse = curl_exec($ch); + if($tsResponse) { + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + curl_close($ch); + $header = substr($tsResponse, 0, $header_size); + $body = substr($tsResponse, $header_size); + + // Get the HTTP response code + $headers = explode("\n", $header); + foreach ($headers as $key => $r) { + if (stripos($r, 'HTTP/') === 0) { + list(,$code, $status) = explode(' ', $r, 3); + break; + } + } + if($code != '200') { + $this->log .= "error: response error! Code=\"$code\", Status=\"".trim($status)."\"\n"; + return false; + } + $contentTypeHeader = ''; + $headers = explode("\n", $header); + foreach ($headers as $key => $r) { + // Match the header name up to ':', compare lower case + if (stripos($r, "Content-Type".':') === 0) { + list($headername, $headervalue) = explode(":", $r, 2); + $contentTypeHeader = trim($headervalue); + } + } + if($contentTypeHeader != $reqData['resp_contentType']) { + $this->log .= "error: response content type not {$reqData['resp_contentType']}, but: \"$contentTypeHeader\"\n"; + return false; + } + return $body; // binary response + } + } + + /** + * parse tsa response to array + * @param string $binaryTsaRespData binary tsa response to parse + * @return array asn.1 hex structure of tsa response + * @private + */ + private function tsa_parseResp($binaryTsaRespData) { + if(!@$ar = asn1::parse(bin2hex($binaryTsaRespData), 3)) { + $this->log .= "info: can't parse invalid tsa Response.\n"; + return false; + } + $curr = $ar; + foreach($curr as $key=>$value) { + if($value['type'] == '30') { + $curr['TimeStampResp']=$curr[$key]; + unset($curr[$key]); + } + } + $ar=$curr; + $curr = $ar['TimeStampResp']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30' && !array_key_exists('status', $curr)) { + $curr['status']=$curr[$key]; + unset($curr[$key]); + } else if($value['type'] == '30') { + $curr['timeStampToken']=$curr[$key]; + unset($curr[$key]); + } + } + } + $ar['TimeStampResp']=$curr; + $curr = $ar['TimeStampResp']['timeStampToken']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '06') { + $curr['contentType']=$curr[$key]; + unset($curr[$key]); + } + if($value['type'] == 'a0') { + $curr['content']=$curr[$key]; + unset($curr[$key]); + } + } + } + $ar['TimeStampResp']['timeStampToken'] = $curr; + $curr = $ar['TimeStampResp']['timeStampToken']['content']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + $curr['TSTInfo']=$curr[$key]; + unset($curr[$key]); + } + } + } + $ar['TimeStampResp']['timeStampToken']['content'] = $curr; + if(@$ar['TimeStampResp']['timeStampToken']['content']['hexdump'] != '') { + return $ar; + } else { + return false; + } + } + + /** + * Create timestamp + * @param string $data + * @return string hex TSTinfo. + * @public + */ + protected function createTimestamp($data, $hashAlg='sha1') { + $TSTInfo=false; + $tsaQuery = x509::tsa_query($data, $hashAlg); + $reqData = array( + 'data'=>$tsaQuery, + 'uri'=>$this->signature_data_tsa['host'], + 'req_contentType'=>'application/timestamp-query', + 'resp_contentType'=>'application/timestamp-reply' + ); + $this->log .= "info: sending TSA query to \"".$this->signature_data_tsa['host']."\"...\n"; + if(!$binaryTsaResp = self::sendReq($reqData)) { + $this->log .= "error: TSA query FAILED!\n"; + } else { + $this->log .= "info: TSA query OK\n"; + $this->log .= "info: Parsing Timestamp response..."; + if(!$tsaResp = $this->tsa_parseResp($binaryTsaResp)) { + $this->log .= "FAILED!\n"; + } + $this->log .= "OK\n"; + $TSTInfo = $tsaResp['TimeStampResp']['timeStampToken']['hexdump']; + } + return $TSTInfo; + } + + /** + * Perform OCSP/CRL Validation + * @param + * @return array + * @public + */ + public function LTVvalidation($parsedCert, $ocspURI=null, $crlURIorFILE=null, $issuerURIorFILE=false) { + $ltvResult['issuer']=false; + $ltvResult['ocsp']=false; + $ltvResult['crl']=false; + $certSigner_parse = $parsedCert; + $this->log .= ("info: getting OCSP & CRL address...\n"); + $this->log .= ("info: reading AIA OCSP attribute..."); + $ocspURI = @$certSigner_parse['tbsCertificate']['attributes']['1.3.6.1.5.5.7.1.1']['value']['1.3.6.1.5.5.7.48.1'][0]; + if(empty(trim($ocspURI))) { + $this->log .= ("info: FAILED!\n"); + } else { + $this->log .= ("info: OK got address:\"$ocspURI\"\n"); + } + $ocspURI = trim($ocspURI); + $this->log .= ("info: reading CRL CDP attribute...\n"); + $crlURIorFILE = @$certSigner_parse['tbsCertificate']['attributes']['2.5.29.31']['value'][0]; + if(empty(trim($crlURIorFILE))) { + $this->log .= ("info: FAILED!\n"); + } else { + $this->log .= ("info: OK got address:\"$crlURIorFILE\"\n"); + } + if(empty($ocspURI) && empty($crlURIorFILE)) { + $this->log .= ("info: can't get OCSP/CRL address! Process terminated.\n"); + } else { // Perform if either ocspURI/crlURIorFILE exists + $this->log .= ("info: getting Issuer...\n"); + $this->log .= ("info: looking for issuer address from AIA attribute...\n"); + $issuerURIorFILE = @$certSigner_parse['tbsCertificate']['attributes']['1.3.6.1.5.5.7.1.1']['value']['1.3.6.1.5.5.7.48.2'][0]; + $issuerURIorFILE = trim($issuerURIorFILE); + if(empty($issuerURIorFILE)) { + $this->log .= ("info: Failed!\n"); + } else { + $this->log .= ("info: OK got address \"$issuerURIorFILE\"...\n"); + $this->log .= ("info: load issuer from \"$issuerURIorFILE\"...\n"); + if($issuerCert = @file_get_contents($issuerURIorFILE)) { + $this->log .= ("info: OK. size ".round(strlen($issuerCert)/1024,2)."Kb\n"); + $this->log .= ("info: reading issuer certificate...\n"); + if($issuer_certDER = x509::get_cert($issuerCert)) { + $this->log .= ("info: OK\n"); + $this->log .= ("info: check if issuer is cert issuer...\n"); + $certIssuer_parse = x509::readcert($issuer_certDER, 'oid'); // Parsing Issuer cert + $certSigner_signatureField = $certSigner_parse['signatureValue']; + if(openssl_public_decrypt(hex2bin($certSigner_signatureField), $decrypted, x509::x509_der2pem($issuer_certDER), OPENSSL_PKCS1_PADDING)) { + $this->log .= ("info: OK issuer is cert issuer.\n"); + $ltvResult['issuer'] = $issuer_certDER; + } else { + $this->log .= ("info: FAILED! issuer is not cert issuer.\n"); + } + } else { + $this->log .= ("info: FAILED!\n"); + } + } else { + $this->log .= ("info: FAILED!.\n"); + } + } + + if(!$ltvResult['issuer']) { + $this->log .= ("info: search for issuer in extracerts.....\n"); + if(array_key_exists('extracerts', $this->signature_data) && (count($this->signature_data['extracerts']) > 0)) { + $i=0; + foreach($this->signature_data['extracerts'] as $extracert) { + $this->log .= ("info: extracerts[$i] ...\n"); + $certSigner_signatureField = $certSigner_parse['signatureValue']; + if(openssl_public_decrypt(hex2bin($certSigner_signatureField), $decrypted, $extracert, OPENSSL_PKCS1_PADDING)) { + $this->log .= ("info: OK got issuer.\n"); + $certIssuer_parse = x509::readcert($extracert, 'oid'); // Parsing Issuer cert + $ltvResult['issuer'] = x509::get_cert($extracert); + } else { + $this->log .= ("info: FAIL!\n"); + } + $i++; + } + } else { + $this->log .= ("info: FAILED! no extracerts available\n"); + } + } + + } + + if($ltvResult['issuer']) { + if(!empty($ocspURI)) { + $this->log .= ("info: OCSP start...\n"); + $ocspReq_serialNumber = $certSigner_parse['tbsCertificate']['serialNumber']; + $ocspReq_issuerNameHash = $certIssuer_parse['tbsCertificate']['subject']['sha1']; + $ocspReq_issuerKeyHash = $certIssuer_parse['tbsCertificate']['subjectPublicKeyInfo']['sha1']; + $ocspRequestorSubjName = $certSigner_parse['tbsCertificate']['subject']['hexdump']; + $this->log .= ("info: OCSP create request...\n"); + if($ocspReq = x509::ocsp_request($ocspReq_serialNumber, $ocspReq_issuerNameHash, $ocspReq_issuerKeyHash)) { + $this->log .= ("info: OK.\n"); + $ocspBinReq = pack("H*", $ocspReq); + $reqData = array( + 'data'=>$ocspBinReq, + 'uri'=>$ocspURI, + 'req_contentType'=>'application/ocsp-request', + 'resp_contentType'=>'application/ocsp-response' + ); + $this->log .= ("info: OCSP send request to \"$ocspURI\"...\n"); + if($ocspResp = self::sendReq($reqData)) { + $this->log .= ("info: OK.\n"); + $this->log .= ("info: OCSP parsing response...\n"); + if($ocsp_parse = x509::ocsp_response_parse($ocspResp, $return)) { + $this->log .= ("info: OK.\n"); + $this->log .= ("info: OCSP check cert validity...\n"); + $certStatus = $ocsp_parse['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responses'][0]['certStatus']; + if($certStatus == 'valid') { + $this->log .= ("info: OK. VALID.\n"); + $ocspRespHex = $ocsp_parse['hexdump']; + $ltvResult['ocsp'] = $ocspRespHex; + } else { + $this->log .= ("info: FAILED! cert not valid, status:\"".strtoupper($certStatus)."\"\n"); + } + } else { + $this->log .= ("info: FAILED! Ocsp server status \"$return\"\n"); + } + } else { + $this->log .= ("info: FAILED!\n"); + } + } else { + $this->log .= ("info: FAILED!\n"); + } + } + + if(!$ltvResult['ocsp']) {// CRL not processed if OCSP validation already success + if(!empty($crlURIorFILE)) { + $this->log .= ("info: processing CRL validation since OCSP not done/failed...\n"); + $this->log .= ("info: getting crl from \"$crlURIorFILE\"...\n"); + if($crl = @file_get_contents($crlURIorFILE)) { + $this->log .= ("info: OK. size ".round(strlen($crl)/1024,2)."Kb\n"); + $this->log .= ("info: reading crl...\n"); + if($crlread=x509::crl_read($crl)) { + $this->log .= ("info: OK\n"); + $this->log .= ("info: verify crl signature...\n"); + $crl_signatureField = $crlread['parse']['signature']; + if(openssl_public_decrypt(hex2bin($crl_signatureField), $decrypted, x509::x509_der2pem($ltvResult['issuer']), OPENSSL_PKCS1_PADDING)) { + $this->log .= ("info: OK\n"); + $this->log .= ("info: check CRL validity...\n"); + $crl_parse=$crlread['parse']; + $thisUpdate = str_pad($crl_parse['TBSCertList']['thisUpdate'], 15, "20", STR_PAD_LEFT); + $thisUpdateTime = strtotime($thisUpdate); + $nextUpdate = str_pad($crl_parse['TBSCertList']['nextUpdate'], 15, "20", STR_PAD_LEFT); + $nextUpdateTime = strtotime($nextUpdate); + $nowz = strtotime("now"); + if(($nowz-$thisUpdateTime) < 0) { // 0 sec after valid + $this->log .= ("info: FAILED! not yet valid! valid at ".date("d/m/Y H:i:s", $thisUpdateTime)); + } elseif(($nextUpdateTime-$nowz) < 1) { // not accept if crl 1 sec remain to expired + $this->log .= ("info: FAILED! Expired crl at ".date("d/m/Y H:i:s", $nextUpdateTime)." and now ".date("d/m/Y H:i:s", $nowz)."!\n"); + } else { + $this->log .= ("info: OK CRL still valid until ".date("d/m/Y H:i:s", $nextUpdateTime)."\n"); + $crlCertValid=true; + $this->log .= ("info: check if cert not revoked...\n"); + if(array_key_exists('revokedCertificates', $crl_parse['TBSCertList'])) { + $certSigner_serialNumber = $certSigner_parse['tbsCertificate']['serialNumber']; + if(array_key_exists($certSigner_serialNumber, $crl_parse['TBSCertList']['revokedCertificates']['lists'])) { + $crlCertValid=false; + $this->log .= ("info: FAILED! Certificate Revoked!\n"); + } + } + if($crlCertValid == true) { + $this->log .= ("info: OK. VALID\n"); + $crlHex = current(unpack('H*', $crlread['der'])); + $ltvResult['crl'] = $crlHex; + } + } + } else { + $this->log .= ("info: FAILED! Wrong CRL.\n"); + } + } else { + $this->log .= ("info: FAILED! can't read crl\n"); + } + } else { + $this->log .= ("info: FAILED! can't get crl\n"); + } + } + } + } + if(!$ltvResult['issuer']) { + return false; + } + if(!$ltvResult['ocsp'] && !$ltvResult['crl']) { + return false; + } + return $ltvResult; + } + + /** + * Perform PKCS7 Signing + * @param string $binaryData + * @return string hex + * @public + */ + public function pkcs7_sign($binaryData) { + $hexOidHashAlgos = array( + 'md2'=>'06082A864886F70D0202', + 'md4'=>'06082A864886F70D0204', + 'md5'=>'06082A864886F70D0205', + 'sha1'=>'06052B0E03021A', + 'sha224'=>'0609608648016503040204', + 'sha256'=>'0609608648016503040201', + 'sha384'=>'0609608648016503040202', + 'sha512'=>'0609608648016503040203' + ); + $hashAlgorithm = $this->signature_data['hashAlgorithm']; + if(!array_key_exists($hashAlgorithm, $hexOidHashAlgos)) { + $this->log .= ("info:not support hash algorithm!"); + return false; + } + $this->log .= ("info:hash algorithm is \"$hashAlgorithm\""); + $x509 = new x509; + if(!$certParse = $x509->readcert($this->signature_data['signcert'])) { + $this->log .= ("info:certificate error! check certificate"); + } + $hexEmbedCerts[] = bin2hex($x509->get_cert($this->signature_data['signcert'])); + $appendLTV = ''; + // $ltvData = $this->signature_data['ltv']; + $ltvData = $this->signature_data_ltv; + if(!empty($ltvData)) { + $this->log .= ("info: LTV Validation start..."); + $appendLTV = ''; + $LTVvalidation_ocsp = ''; + $LTVvalidation_crl = ''; + $LTVvalidation_issuer = ''; + $LTVvalidationEnd = false; + + $isRootCA = false; + if($certParse['tbsCertificate']['issuer']['hexdump'] == $certParse['tbsCertificate']['subject']['hexdump']) { // check whether root ca + if(openssl_public_decrypt(hex2bin($certParse['signatureValue']), $decrypted, x509::x509_der2pem($x509->get_cert($this->signature_data['signcert'])), OPENSSL_PKCS1_PADDING)) { + $this->log .= ("info:***** \"{$certParse['tbsCertificate']['subject']['2.5.4.3'][0]}\" is a ROOT CA. No validation performed ***"); + $isRootCA = true; + } + } + if($isRootCA == false) { + $i = 0; + $LTVvalidation = true; + $certtoCheck = $certParse; + while($LTVvalidation !== false) { + $this->log .= ("info:========= $i checking \"{$certtoCheck['tbsCertificate']['subject']['2.5.4.3'][0]}\"==============="); + $LTVvalidation = self::LTVvalidation($certtoCheck); + $i++; + if($LTVvalidation) { + $curr_issuer = $LTVvalidation['issuer']; + $certtoCheck = $x509->readcert($curr_issuer, 'oid'); + if(@$LTVvalidation['ocsp'] || @$LTVvalidation['crl']) { + $LTVvalidation_ocsp .= $LTVvalidation['ocsp']; + $LTVvalidation_crl .= $LTVvalidation['crl']; + $hexEmbedCerts[] = bin2hex($LTVvalidation['issuer']); + } + + if($certtoCheck['tbsCertificate']['issuer']['hexdump'] == $certtoCheck['tbsCertificate']['subject']['hexdump']) { // check whether root ca + if(openssl_public_decrypt(hex2bin($certtoCheck['signatureValue']), $decrypted, $x509->x509_der2pem($curr_issuer), OPENSSL_PKCS1_PADDING)) { + $this->log .= ("info:========= FINISH Reached ROOT CA \"{$certtoCheck['tbsCertificate']['subject']['2.5.4.3'][0]}\"==============="); + $LTVvalidationEnd = true; + break; + } + } + } + } + + if($LTVvalidationEnd) { + $this->log .= ("info: LTV Validation SUCCESS\n"); + $ocsp = ''; + if(!empty($LTVvalidation_ocsp)) { + $ocsp = asn1::expl(1, + asn1::seq( + $LTVvalidation_ocsp + ) + ); + } + $crl = ''; + if(!empty($LTVvalidation_crl)) { + $crl = asn1::expl(0, + asn1::seq( + $LTVvalidation_crl + ) + ); + } + $appendLTV = asn1::seq( + "06092A864886F72F010108". // adbe-revocationInfoArchival (1.2.840.113583.1.1.8) + asn1::set( + asn1::seq( + $ocsp. + $crl + ) + ) + ); + } else { + $this->log .= ("info: LTV Validation FAILED!\n"); + } + } + // foreach($this->signature_data['extracerts'] ?? [] as $extracert) { + // $hex_extracert = bin2hex($x509->x509_pem2der($extracert)); + // if(!in_array($hex_extracert, $hexEmbedCerts)) { + // $hexEmbedCerts[] = $hex_extracert; + // } + // } + } + $messageDigest = hash($hashAlgorithm, $binaryData); + $authenticatedAttributes= asn1::seq( + '06092A864886F70D010903'. //OBJ_pkcs9_contentType 1.2.840.113549.1.9.3 + asn1::set('06092A864886F70D010701') //OBJ_pkcs7_data 1.2.840.113549.1.7.1 + ). + asn1::seq( // signing time + '06092A864886F70D010905'. //OBJ_pkcs9_signingTime 1.2.840.113549.1.9.5 + asn1::set( + asn1::utime(date("ymdHis")) //UTTC Time + ) + ). + asn1::seq( // messageDigest + '06092A864886F70D010904'. //OBJ_pkcs9_messageDigest 1.2.840.113549.1.9.4 + asn1::set(asn1::oct($messageDigest)) + ). + $appendLTV; + $tohash = asn1::set($authenticatedAttributes); + $hash = hash($hashAlgorithm, hex2bin($tohash)); + $toencrypt = asn1::seq( + asn1::seq($hexOidHashAlgos[$hashAlgorithm]."0500"). // OBJ $messageDigest & OBJ_null + asn1::oct($hash) + ); + $pkey = $this->signature_data['privkey']; + if(!openssl_private_encrypt(hex2bin($toencrypt), $encryptedDigest, $pkey, OPENSSL_PKCS1_PADDING)) { + $this->log .= ("info:openssl_private_encrypt error! can't encrypt"); + return false; + } + $hexencryptedDigest = bin2hex($encryptedDigest); + $timeStamp = ''; + if(!empty($this->signature_data_tsa)) { + $this->log .= ("info: Timestamping process start..."); + if($TSTInfo = self::createTimestamp($encryptedDigest, $hashAlgorithm)) { + $this->log .= ("info: Timestamping SUCCESS.\n"); + $TimeStampToken = asn1::seq( + "060B2A864886F70D010910020E". // OBJ_id_smime_aa_timeStampToken 1.2.840.113549.1.9.16.2.14 + asn1::set($TSTInfo) + ); + $timeStamp = asn1::expl(1,$TimeStampToken); + } else { + $this->log .= ("info: Timestamping FAILED!\n"); + } + } + $issuerName = $certParse['tbsCertificate']['issuer']['hexdump']; + $serialNumber = $certParse['tbsCertificate']['serialNumber']; + $signerinfos = asn1::seq( + asn1::int('1'). + asn1::seq($issuerName . asn1::int($serialNumber)). + asn1::seq($hexOidHashAlgos[$hashAlgorithm].'0500'). + asn1::expl(0, $authenticatedAttributes). + asn1::seq( + '06092A864886F70D010101'. //OBJ_rsaEncryption + '0500' + ). + asn1::oct($hexencryptedDigest) + .$timeStamp + ); + $certs = asn1::expl(0,implode('', $hexEmbedCerts)); + $pkcs7contentSignedData = asn1::seq( + asn1::int('1'). + asn1::set(asn1::seq($hexOidHashAlgos[$hashAlgorithm].'0500')). + asn1::seq('06092A864886F70D010701'). //OBJ_pkcs7_data + $certs. + asn1::set($signerinfos) + ); + $pkcs7ContentInfo = asn1::seq( + "06092A864886F70D010702". // Hexadecimal form of pkcs7-signedData + asn1::expl(0,$pkcs7contentSignedData) + ); + return $pkcs7ContentInfo; + } +} + +/** + * @class x509 + * Perform some x509 operation + * @version 1.1 + * @author M Hida + */ +class x509 { + /* + * create tsa request/query with nonce and cert req extension + * @param string $binaryData raw/binary data of tsa query + * @param string $hashAlg hash Algorithm + * @return string binary tsa query + * @public + */ + public static function tsa_query($binaryData, $hashAlg='sha256') { + $hashAlg = strtolower($hashAlg); + $hexOidHashAlgos = array( + 'md2'=>'06082A864886F70D0202', + 'md4'=>'06082A864886F70D0204', + 'md5'=>'06082A864886F70D0205', + 'sha1'=>'06052B0E03021A', + 'sha224'=>'0609608648016503040204', + 'sha256'=>'0609608648016503040201', + 'sha384'=>'0609608648016503040202', + 'sha512'=>'0609608648016503040203' + ); + if(!array_key_exists($hashAlg, $hexOidHashAlgos)) { + return false; + } + $hash = hash($hashAlg, $binaryData); + $tsReqData = asn1::seq( + asn1::int(1). + asn1::seq( + asn1::seq($hexOidHashAlgos[$hashAlg]."0500"). // object OBJ $hexOidHashAlgos[$hashAlg] & OBJ_null + asn1::oct($hash) + ). + asn1::int(hash('crc32', rand()).'001'). // tsa nonce + '0101ff' // req return cert + ); + return hex2bin($tsReqData); + } + + /** + * Calculate 32 openssl subject hash old and new + * @param string $hex_subjSequence hex subject name sequence + * @return array subject hash old and new + */ + private static function opensslSubjHash($hex_subjSequence){ + $parse = asn1::parse($hex_subjSequence,3); + $hex_subjSequence_new=''; + foreach($parse[0] as $k=>$v) { + if(is_numeric($k)) { + $hex_subjSequence_new .= asn1::set( + asn1::seq( + $v[0][0]['hexdump']. + asn1::utf8(strtolower(hex2bin($v[0][1]['value_hex']))) + ) + ); + } + } + $tohash = pack("H*", $hex_subjSequence_new); + $openssl_subjHash_new = hash('sha1', $tohash); + $openssl_subjHash_new = substr($openssl_subjHash_new, 0, 8); + $openssl_subjHash_new = str_split($openssl_subjHash_new, 2); + $openssl_subjHash_new = array_reverse($openssl_subjHash_new); + $openssl_subjHash_new = implode("", $openssl_subjHash_new); + + $openssl_subjHash_old = hash('md5', hex2bin($hex_subjSequence)); + $openssl_subjHash_old = substr($openssl_subjHash_old, 0, 8); + $openssl_subjHash_old = str_split($openssl_subjHash_old, 2); + $openssl_subjHash_old = array_reverse($openssl_subjHash_old); + $openssl_subjHash_old = implode("", $openssl_subjHash_old); + + return array( + "old"=>$openssl_subjHash_old, + "new"=>$openssl_subjHash_new + ); + } + + /** + * Parsing ocsp response data + * @param string $binaryOcspResp binary ocsp response + * @return array ocsp response structure + */ + public static function ocsp_response_parse($binaryOcspResp, &$status='') { + $hex = current(unpack("H*", $binaryOcspResp)); + $parse = asn1::parse($hex,10); + if($parse[0]['type'] == '30') { + $ocsp = $parse[0]; + } else { + return false; + } + + foreach($ocsp as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '0a') { + $ocsp['responseStatus']=$value['value_hex']; + unset($ocsp[$key]); + } + if($value['type'] == 'a0') { + $ocsp['responseBytes']=$value; + unset($ocsp[$key]); + } + } else { + //unset($ocsp[$key]); + unset($ocsp['depth']); + unset($ocsp['type']); + unset($ocsp['typeName']); + unset($ocsp['value_hex']); + } + } + //OCSPResponseStatus ::= ENUMERATED { + // successful (0), --Response has valid confirmations + // malformedRequest (1), --Illegal confirmation request + // internalError (2), --Internal error in issuer + // tryLater (3), --Try again later + // --(4) is not used + // sigRequired (5), --Must sign the request + // unauthorized (6) --Request unauthorized + //} + if(@$ocsp['responseStatus'] != '00') { + $responseStatus['01']='malformedRequest'; + $responseStatus['02']='internalError'; + $responseStatus['03']='tryLater'; + $responseStatus['05']='sigRequired'; + $responseStatus['06']='unauthorized'; + $status = $responseStatus[$ocsp['responseStatus']]; + return false; + } + + if(!@$curr = $ocsp['responseBytes']) { + return false; + } + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + $curr['responseType']=self::oidfromhex($value[0]['value_hex']); + $curr['response']=$value[1]; + unset($curr[$key]); + } + } else { + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes'] = $curr; + + $curr = $ocsp['responseBytes']['response']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + $curr['BasicOCSPResponse']=$value; + unset($curr[$key]); + } + } else { + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes']['response'] = $curr; + + $curr = $ocsp['responseBytes']['response']['BasicOCSPResponse']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30' && !array_key_exists('tbsResponseData', $curr)) { + $curr['tbsResponseData']=$value; + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('signatureAlgorithm', $curr)) { + $curr['signatureAlgorithm']=$value[0]['value_hex']; + unset($curr[$key]); + continue; + } + if($value['type'] == '03') { + $curr['signature']=substr($value['value_hex'], 2); + unset($curr[$key]); + } + if($value['type'] == 'a0') { + foreach($value[0] as $certsK=>$certsV) { + if(is_numeric($certsK)) { + $certs[$certsK] = $certsV['value_hex']; + } + } + $curr['certs']=$certs; + unset($curr[$key]); + } + } else { + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes']['response']['BasicOCSPResponse'] = $curr; + + $curr = $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == 'a0') { + $curr['version']=$value[0]['value']; + unset($curr[$key]); + } + if($value['type'] == 'a1' && !array_key_exists('responderID', $curr)) { + $curr['responderID']=$value; + unset($curr[$key]); + } + if($value['type'] == 'a2') { + $curr['responderID']=$value; + unset($curr[$key]); + } + if($value['type'] == '18') { + $curr['producedAt']=$value['value']; + unset($curr[$key]); + } + if($value['type'] == '30') { + $curr['responses']=$value; + unset($curr[$key]); + } + if($value['type'] == 'a1') { + $curr['responseExtensions']=$value; + unset($curr[$key]); + } + } else { + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData'] = $curr; + + $curr = $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responseExtensions']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + $curr['lists']=$value; + unset($curr[$key]); + } + } else { + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responseExtensions'] = $curr; + + $curr = $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responseExtensions']['lists']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + if($value[0]['value_hex'] == '2b0601050507300102') { // nonce + $curr['nonce']=$value[0]['value_hex']; + } else { + $curr[$value[0]['value_hex']]=$value[1]; + } + unset($curr[$key]); + } + } else { + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responseExtensions']['lists'] = $curr; + + $curr = $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responses']; + $i=0; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + foreach($value as $SingleResponseK=>$SingleResponseV) { + if(is_numeric($SingleResponseK)) { + if($SingleResponseK == 0) { + foreach($SingleResponseV as $certIDk=>$certIDv) { + if(is_numeric($certIDk)) { + if($certIDv['type'] == '30') { + $certID['hashAlgorithm'] = $certIDv[0]['value_hex']; + } + if($certIDv['type'] == '04' && !array_key_exists('issuerNameHash', $certID)) { + $certID['issuerNameHash'] = $certIDv['value_hex']; + } + if($certIDv['type'] == '04') { + $certID['issuerKeyHash'] = $certIDv['value_hex']; + } + if($certIDv['type'] == '02') { + $certID['serialNumber'] = $certIDv['value_hex']; + } + } + } + $cert['certID'] = $certID; + } + if($SingleResponseK == 1) { + if($SingleResponseV['type'] == '82') { + $certStatus = 'unknown'; + } elseif($SingleResponseV['type'] == '80') { + $certStatus = 'valid'; + } else { + $certStatus = 'revoked'; + } + $cert['certStatus'] = $certStatus; + } + if($SingleResponseK == 2) { + $cert['thisUpdate'] = $SingleResponseV['value']; + } + if($SingleResponseK == 3) { + $cert['nextUpdate'] = $SingleResponseV[0]['value']; + } + if($SingleResponseK == 4) { + $cert['singleExtensions'] = $SingleResponseV; + } + } + } + $curr[$i] = $cert; + } else { + unset($curr[$key]); + unset($curr['typeName']); + unset($curr['type']); + unset($curr['depth']); + } + } + $ocsp['responseBytes']['response']['BasicOCSPResponse']['tbsResponseData']['responses'] = $curr; + + $arrModel = array( + 'responseStatus'=>'', + 'responseBytes'=>array( + 'response'=>'', + 'responseType'=>'' + ) + ); + + $differ=array_diff_key($arrModel,$ocsp); + if(count($differ) == 0) { + $differ=array_diff_key($arrModel['responseBytes'],$ocsp['responseBytes']); + if(count($differ) > 0) { + foreach($differ as $key=>$val) { + } + return false; + } + } else { + foreach($differ as $key=>$val) { + } + return false; + } + return $ocsp; + } + + /** + * Create ocsp request + * @param string $serialNumber serial number to check + * @param string $issuerNameHash sha1 hex form of issuer subject hash + * @param string $issuerKeyHash sha1 hex form of issuer subject public info hash + * @param string $signer_cert cert to sign ocsp request + * @param string $signer_key privkey to sign ocsp request + * @param string $subjectName hex form of asn1 subject + * @return string hex form ocsp request + */ + public static function ocsp_request($serialNumber, $issuerNameHash, $issuerKeyHash, $signer_cert = false, $signer_key = false, $subjectName=false) { + $Request = false; + $hashAlgorithm = asn1::seq( + "06052B0E03021A". // OBJ_sha1 + "0500" + ); + $issuerNameHash = asn1::oct($issuerNameHash); + $issuerKeyHash = asn1::oct($issuerKeyHash); + $serialNumber = asn1::int($serialNumber); + $CertID = asn1::seq($hashAlgorithm.$issuerNameHash.$issuerKeyHash.$serialNumber); + $Request = asn1::seq($CertID); // one request + if($signer_cert) { + $requestorName = asn1::expl("1", asn1::expl("4", $subjectName)); + } else { + $requestorName = false; + } + $requestList = asn1::seq($Request); // add more request into sequence + $rand = microtime (true)*rand(); + $nonce = md5(base64_encode($rand).$rand); + $ReqExts = asn1::seq( + '06092B0601050507300102'. // OBJ_id_pkix_OCSP_Nonce + asn1::oct("0410".$nonce) + ); + $requestExtensions = asn1::expl( "2", asn1::seq( + $ReqExts + ) + ); + $TBSRequest = asn1::seq($requestorName.$requestList.$requestExtensions); + $optionalSignature = ''; + if($signer_cert) { + if(!openssl_sign (hex2bin($TBSRequest), $signature_value, $signer_key)) { + return false; + //die("Ora bisa gawe signature maring request"); + } + $signatureAlgorithm = asn1::seq( + '06092A864886F70D010105'. // OBJ_sha1WithRSAEncryption. + "0500" + ); + $signature = asn1::bit("00".bin2hex($signature_value)); + $signer_cert = x509::x509_pem2der($signer_cert); + $certs = asn1::expl("0", asn1::seq(bin2hex($signer_cert))); + $optionalSignature = asn1::expl("0",asn1::seq($signatureAlgorithm.$signature.$certs)); + } + $OCSPRequest = asn1::seq($TBSRequest.$optionalSignature); + return $OCSPRequest; + } + + /** + * Convert crl from pem to der (binary) + * @param string $crl pem crl to convert + * @return string der crl form + */ + public static function crl_pem2der($crl) { + $begin = '-----BEGIN X509 CRL-----'; + $end = '-----END X509 CRL-----'; + $beginPos = stripos($crl, $begin); + if($beginPos===false) { + return false; + } + $crl = substr($crl, $beginPos+strlen($begin)); + $endPos = stripos($crl, $end); + if($endPos===false) { + return false; + } + $crl = substr($crl, 0, $endPos); + $crl = str_replace("\n", "", $crl); + $crl = str_replace("\r", "", $crl); + $dercrl = base64_decode($crl); + return $dercrl; + } + + /** + * Read crl from pem or der (binary) + * @param string $crl pem or der crl + * @return array der crl and parsed crl + */ + public static function crl_read($crl) { + if(!$crlparse=self::parsecrl($crl)) { // if cant read, thats not crl + return false; + } + if(!$dercrl=self::crl_pem2der($crl)) { // if not pem, thats already der + $dercrl=$crl; + } + $res['der'] = $dercrl; + $res['parse'] = $crlparse; + return $res; + } + + /** + * parsing crl from pem or der (binary) + * @param string $crl pem or der crl + * @param string $oidprint option show obj as hex/oid + * @return array parsed crl + */ + private static function parsecrl($crl, $oidprint = false) { + if($derCrl = self::crl_pem2der($crl)) { + $derCrl = bin2hex($derCrl); + } else { + $derCrl = bin2hex($crl); + } + + $curr = asn1::parse($derCrl, 7); + foreach($curr as $key=>$value) { + if($value['type'] == '30') { + $curr['crl']=$curr[$key]; + unset($curr[$key]); + } + } + $ar=$curr; + if(!array_key_exists('crl', $ar)) { + return false; + } + $curr = $ar['crl']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30' && !array_key_exists('TBSCertList', $curr)) { + $curr['TBSCertList']=$curr[$key]; + unset($curr[$key]); + } + if($value['type'] == '30') { + $curr['signatureAlgorithm']=self::oidfromhex($value[0]['value_hex']); + unset($curr[$key]); + } + if($value['type'] == '03') { + $curr['signature']=substr($value['value'], 2); + unset($curr[$key]); + } + } else { + unset($curr[$key]); + } + } + $ar['crl'] = $curr; + + $curr = $ar['crl']['TBSCertList']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '02') { + $curr['version']=$curr[$key]['value']; + unset($curr[$key]); + } + if($value['type'] == '30' && !array_key_exists('signature', $curr)) { + $curr['signature']=$value[0]['value_hex']; + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('issuer', $curr)) { + $curr['issuer']=$value; + unset($curr[$key]); + continue; + } + if($value['type'] == '17' && !array_key_exists('thisUpdate', $curr)) { + $curr['thisUpdate']=hex2bin($value['value_hex']); + unset($curr[$key]); + continue; + } + if($value['type'] == '17' && !array_key_exists('nextUpdate', $curr)) { + $curr['nextUpdate']=hex2bin($value['value_hex']); + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('revokedCertificates', $curr)) { + $curr['revokedCertificates']=$value; + unset($curr[$key]); + continue; + } + if($value['type'] == 'a0') { + $curr['crlExtensions']=$curr[$key]; + unset($curr[$key]); + } + } else { + unset($curr[$key]); + } + } + $ar['crl']['TBSCertList'] = $curr; + + if(array_key_exists('revokedCertificates', $curr)) { + $curr = $ar['crl']['TBSCertList']['revokedCertificates']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + $serial = $value[0]['value']; + $revoked['time']=hex2bin($value[1]['value_hex']); + $lists[$serial]=$revoked; + unset($curr[$key]); + } + } else { + unset($curr['depth']); + unset($curr['type']); + unset($curr['typeName']); + } + } + $curr['lists'] = $lists; + $ar['crl']['TBSCertList']['revokedCertificates'] = $curr; + } + + if(array_key_exists('crlExtensions', $ar['crl']['TBSCertList'])) { + $curr = $ar['crl']['TBSCertList']['crlExtensions'][0]; + unset($ar['crl']['TBSCertList']['crlExtensions']); + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + $attributes_name = self::oidfromhex($value[0]['value_hex']); + if($oidprint == 'oid') { + $attributes_name = self::oidfromhex($value[0]['value_hex']); + } + if($oidprint == 'hex') { + $attributes_name = $value[0]['value_hex']; + } + $attributes_oid = self::oidfromhex($value[0]['value_hex']); + if($value['type'] == '30') { + $crlExtensionsValue = $value[1][0]; + if($attributes_oid == '2.5.29.20') { // OBJ_crl_number + $crlExtensionsValue = $crlExtensionsValue['value']; + } + if($attributes_oid == '2.5.29.35') { // OBJ_authority_key_identifier + foreach($crlExtensionsValue as $authority_key_identifierValueK=>$authority_key_identifierV) { + if(is_numeric($authority_key_identifierValueK)) { + if($authority_key_identifierV['type'] == '80') { + $authority_key_identifier['keyIdentifier'] = $authority_key_identifierV['value_hex']; + } + if($authority_key_identifierV['type'] == 'a1') { + $authority_key_identifier['authorityCertIssuer'] = $authority_key_identifierV['value_hex']; + } + if($authority_key_identifierV['type'] == '82') { + $authority_key_identifier['authorityCertSerialNumber'] = $authority_key_identifierV['value_hex']; + } + } + } + $crlExtensionsValue = $authority_key_identifier; + } + $attribute_list=$crlExtensionsValue; + } + $ar['crl']['TBSCertList']['crlExtensions'][$attributes_name] = $attribute_list; + } + } + } + + $curr = $ar['crl']['TBSCertList']['issuer']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '31') { + if($oidprint == 'oid') { + $subjOID = self::oidfromhex($curr[$key][0][0]['value_hex']); + } elseif($oidprint == 'hex') { + $subjOID = $curr[$key][0][0]['value_hex']; + } else { + $subjOID = self::oidfromhex($curr[$key][0][0]['value_hex']); + } + $curr[$subjOID][]=hex2bin($curr[$key][0][1]['value_hex']); + unset($curr[$key]); + + } + } else { + unset($curr['depth']); + unset($curr['type']); + unset($curr['typeName']); + if($key == 'hexdump') { + $curr['sha1']=hash('sha1', pack("H*", $value)); + } + } + } + $ar['crl']['TBSCertList']['issuer'] = $curr; + + $arrModel['TBSCertList']['version'] = ''; + $arrModel['TBSCertList']['signature'] = ''; + $arrModel['TBSCertList']['issuer'] = ''; + $arrModel['TBSCertList']['thisUpdate'] = ''; + $arrModel['TBSCertList']['nextUpdate'] = ''; + $arrModel['signatureAlgorithm'] = ''; + $arrModel['signature'] = ''; + + $crl = $ar['crl']; + $differ=array_diff_key($arrModel,$crl); + if(count($differ) == 0) { + $differ=array_diff_key($arrModel['TBSCertList'],$crl['TBSCertList']); + if(count($differ) > 0) { + foreach($differ as $key=>$val) { + } + return false; + } + } else { + foreach($differ as $key=>$val) { + } + return false; + } + return $ar['crl']; + } + + /** + * Convert x509 pem certificate to x509 der + * @param string $pem pem form cert + * @return string der form cert + */ + public static function x509_pem2der($pem) { + $x509_der = false; + if($x509_res = @openssl_x509_read($pem)) { + openssl_x509_export ($x509_res, $x509_pem); + $arr_x509_pem = explode("\n", $x509_pem); + $numarr = count($arr_x509_pem); + $i=0; + $cert_pem = false; + foreach($arr_x509_pem as $val) { + if($i > 0 && $i < ($numarr-2)) { + $cert_pem .= $val; + } + $i++; + } + $x509_der = base64_decode($cert_pem); + } + return $x509_der; + } + + /** + * Convert x509 der certificate to x509 pem form + * @param string $der_cert der form cert + * @return string pem form cert + */ + public static function x509_der2pem($der_cert) { + $x509_pem = "-----BEGIN CERTIFICATE-----\r\n"; + $x509_pem .= chunk_split(base64_encode($der_cert),64); + $x509_pem .= "-----END CERTIFICATE-----\r\n"; + return $x509_pem; + } + + /** + * get x.509 DER/PEM Certificate and return DER encoded x.509 Certificate + * @param string $certin pem/der form cert + * @return string der form cert + */ + public static function get_cert($certin) { + if($rsccert = @openssl_x509_read ($certin)) { + openssl_x509_export ($rsccert, $cert); + return self::x509_pem2der($cert); + } else { + $pem = @self::x509_der2pem($certin); + if($rsccert = @openssl_x509_read ($pem)) { + openssl_x509_export ($rsccert, $cert); + return self::x509_pem2der($cert); + } else { + return false; + } + } + } + + /** + * parse x.509 DER/PEM Certificate structure + * @param string $certin pem/der form cert + * @param string $oidprint show oid as oid number or hex + * @return array cert structure + */ + public static function readcert($cert_in, $oidprint=false) { + if(!$der = self::get_cert($cert_in)) { + return false; + } + $hex = bin2hex($der); + $curr = asn1::parse($hex,10); + foreach($curr as $key=>$value) { + if($value['type'] == '30') { + $curr['cert']=$curr[$key]; + unset($curr[$key]); + } + } + $ar=$curr; + + $curr = $ar['cert']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30' && !array_key_exists('tbsCertificate', $curr)) { + $curr['tbsCertificate']=$curr[$key]; + unset($curr[$key]); + } + if($value['type'] == '30') { + $curr['signatureAlgorithm']=self::oidfromhex($value[0]['value_hex']); + unset($curr[$key]); + } + if($value['type'] == '03') { + $curr['signatureValue']=substr($value['value'], 2); + unset($curr[$key]); + } + } else { + unset($curr[$key]); + } + } + $ar['cert'] = $curr; + $ar['cert']['sha1Fingerprint']=hash('sha1', $der); + $curr = $ar['cert']['tbsCertificate']; + $i=0; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == 'a0') { + $curr['version']=$value[0]['value']; + unset($curr[$key]); + } + if($value['type'] == '02') { + $curr['serialNumber']=$value['value']; + unset($curr[$key]); + } + if($value['type'] == '30' && !array_key_exists('signature', $curr)) { + $curr['signature']=$value[0]['value_hex']; + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('issuer', $curr)) { + foreach($value as $issuerK=>$issuerV) { + if(is_numeric($issuerK)) { + $issuerOID = $issuerV[0][0]['value_hex']; + if($oidprint == 'oid') { + $issuerOID = self::oidfromhex($issuerOID); + } elseif($oidprint == 'hex') { + } else { + $issuerOID = self::oidfromhex($issuerOID); + } + $issuer[$issuerOID][] = hex2bin($issuerV[0][1]['value_hex']); + } + } + $hexdump = $value['hexdump']; + $issuer['sha1'] = hash('sha1', hex2bin($hexdump)); + $issuer['opensslHash'] = self::opensslSubjHash($hexdump); + $issuer['hexdump'] = $hexdump; + + $curr['issuer']=$issuer; + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('validity', $curr)) { + $curr['validity']['notBefore']=hex2bin($value[0]['value_hex']); + $curr['validity']['notAfter']=hex2bin($value[1]['value_hex']); + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('subject', $curr)) { + $asn1SubjectToHash = ''; + foreach($value as $subjectK=>$subjectV) { + if(is_numeric($subjectK)) { + $subjectOID = $subjectV[0][0]['value_hex']; + if($oidprint == 'oid') { + $subjectOID = self::oidfromhex($subjectOID); + } elseif($oidprint == 'hex') { + } else { + $subjectOID = self::oidfromhex($subjectOID); + } + $subject[$subjectOID][] = hex2bin($subjectV[0][1]['value_hex']); + } + } + $hexdump = $value['hexdump']; + $subject['sha1'] = hash('sha1', hex2bin($hexdump)); + $subject['opensslHash'] = self::opensslSubjHash($hexdump); + $subject['hexdump'] = $hexdump; + + $curr['subject']=$subject; + unset($curr[$key]); + continue; + } + if($value['type'] == '30' && !array_key_exists('subjectPublicKeyInfo', $curr)) { + foreach($value as $subjectPublicKeyInfoK=>$subjectPublicKeyInfoV) { + if(is_numeric($subjectPublicKeyInfoK)) { + if($subjectPublicKeyInfoV['type'] == '30') { + $subjectPublicKeyInfo['algorithm']=self::oidfromhex($subjectPublicKeyInfoV[0]['value_hex']); + } + if($subjectPublicKeyInfoV['type'] == '03') { + $subjectPublicKeyInfo['subjectPublicKey']=substr($subjectPublicKeyInfoV['value'], 2); + } + } else { + unset($curr[$key]); + } + } + $subjectPublicKeyInfo['hex']=$value['hexdump']; + $subjectPublicKey_parse =asn1::parse($subjectPublicKeyInfo['subjectPublicKey']); + $subjectPublicKeyInfo['keyLength']=(strlen(substr($subjectPublicKey_parse[0][0]['value'], 2))/2)*8; + $subjectPublicKeyInfo['sha1']=hash('sha1', pack('H*', $subjectPublicKeyInfo['subjectPublicKey'])); + + $curr['subjectPublicKeyInfo']=$subjectPublicKeyInfo; + unset($curr[$key]); + continue; + } + if($value['type'] == 'a3') { + $curr['attributes']=$value[0]; + unset($curr[$key]); + } + $i++; + } else { + $tbsCertificateTag[$key]=$value; + } + } + $ar['cert']['tbsCertificate'] = $curr; + + if(array_key_exists('attributes', $ar['cert']['tbsCertificate'])) { + $curr = $ar['cert']['tbsCertificate']['attributes']; + foreach($curr as $key=>$value) { + if(is_numeric($key)) { + if($value['type'] == '30') { + $critical = 0; + $extvalue = $value[1]; + $name_hex = $value[0]['value_hex']; + $value_hex = $value[1]['hexdump']; + + if($value[1]['type'] == '01' && $value[1]['value_hex'] == 'ff') { + $critical = 1; + $extvalue = $value[2]; + } + if($name_hex == '551d0e') { // OBJ_subject_key_identifier + $extvalue = $value[1][0]['value_hex']; + } + if($name_hex == '551d23') { // OBJ_authority_key_identifier + foreach($value[1][0] as $OBJ_authority_key_identifierKey=>$OBJ_authority_key_identifierVal) { + if(is_numeric($OBJ_authority_key_identifierKey)) { + if($OBJ_authority_key_identifierVal['type'] == '80') { + $OBJ_authority_key_identifier['keyid'] = $OBJ_authority_key_identifierVal['value_hex']; + } + if($OBJ_authority_key_identifierVal['type'] == 'a1') { + $OBJ_authority_key_identifier['issuerName'] = $OBJ_authority_key_identifierVal['value_hex']; + } + if($OBJ_authority_key_identifierVal['type'] == '82') { + $OBJ_authority_key_identifier['issuerSerial'] = $OBJ_authority_key_identifierVal['value_hex']; + } + } + } + $extvalue = $OBJ_authority_key_identifier; + } + if($name_hex == '2b06010505070101') { // OBJ_info_access + foreach($value[1][0] as $OBJ_info_accessK=>$OBJ_info_accessV) { + if(is_numeric($OBJ_info_accessK)) { + $OBJ_info_accessHEX = $OBJ_info_accessV[0]['value_hex']; + $OBJ_info_accessOID = self::oidfromhex($OBJ_info_accessHEX); + $OBJ_info_accessNAME = $OBJ_info_accessOID; + $OBJ_info_access[$OBJ_info_accessNAME][] = hex2bin($OBJ_info_accessV[1]['value_hex']); + } + } + $extvalue = $OBJ_info_access; + } + if($name_hex == '551d1f') { // OBJ_crl_distribution_points 551d1f + foreach($value[1][0] as $OBJ_crl_distribution_pointsK=>$OBJ_crl_distribution_pointsV) { + if(is_numeric($OBJ_crl_distribution_pointsK)) { + $OBJ_crl_distribution_points[] = hex2bin($OBJ_crl_distribution_pointsV[0][0][0]['value_hex']); + } + } + $extvalue = $OBJ_crl_distribution_points; + } + if($name_hex == '551d0f') { // OBJ_key_usage + // $extvalue = self::parse_keyUsage($extvalue[0]['value']); + } + if($name_hex == '551d13') { // OBJ_basic_constraints + $bc['ca'] = '0'; + $bc['pathLength'] = ''; + foreach($extvalue[0] as $bck=>$bcv) { + if(is_numeric($bck)) { + if($bcv['type'] == '01') { + if($bcv['value_hex'] == 'ff') { + $bc['ca'] = '1'; + } + } + if($bcv['type'] == '02') { + $bc['pathLength'] = $bcv['value']; + } + } + } + $extvalue = $bc; + } + if($name_hex == '551d25') { // OBJ_ext_key_usage 551d1f + foreach($extvalue[0] as $OBJ_ext_key_usageK=>$OBJ_ext_key_usageV) { + if(is_numeric($OBJ_ext_key_usageK)) { + $OBJ_ext_key_usageHEX = $OBJ_ext_key_usageV['value_hex']; + $OBJ_ext_key_usageOID = self::oidfromhex($OBJ_ext_key_usageHEX); + $OBJ_ext_key_usageNAME = $OBJ_ext_key_usageOID; + $OBJ_ext_key_usage[] = $OBJ_ext_key_usageNAME; + } + } + $extvalue = $OBJ_ext_key_usage; + } + + $extsVal=array( + 'name_hex'=>$value[0]['value_hex'], + 'name_oid'=>self::oidfromhex($value[0]['value_hex']), + 'name'=>self::oidfromhex($value[0]['value_hex']), + 'critical'=>$critical, + 'value'=>$extvalue + ); + + $extNameOID = $value[0]['value_hex']; + if($oidprint == 'oid') { + $extNameOID = self::oidfromhex($extNameOID); + } elseif($oidprint == 'hex') { + } else { + $extNameOID = self::oidfromhex($extNameOID); + } + $curr[$extNameOID] = $extsVal; + unset($curr[$key]); + } + } else { + unset($curr[$key]); + } + unset($ar['cert']['tbsCertificate']['attributes']); + $ar['cert']['tbsCertificate']['attributes'] = $curr; + } + } + return $ar['cert']; + } + + /** + * read oid number of given hex + * @param string $hex hex form oid number + * @return string oid number + */ + private static function oidfromhex($hex) { + $split = str_split($hex, 2); + $i = 0; + foreach($split as $val) { + $dec = hexdec($val); + $mplx[$i] = ($dec-128)*128; + $i++; + } + $i = 0; + $nex = false; + $result = false; + foreach($split as $val) { + $dec = hexdec($val); + if($i == 0) { + if($dec >= 128) { + $nex = (128*($dec-128))-80; + if($dec > 129) { + $nex = (128*($dec-128))-80; + } + $result = "2."; + } + if($dec >= 80 && $dec < 128) { + $first = $dec-80; + $result = "2.$first."; + } + if($dec >= 40 && $dec < 80) { + $first = $dec-40; + $result = "1.$first."; + } + if($dec < 40) { + $first = $dec-0; + $result = "0.$first."; + } + } else { + if($dec > 127) { + if($nex == false) { + $nex = $mplx[$i]; + } else { + $nex = ($nex*128)+$mplx[$i]; + } + } else { + $result .= ($dec+$nex)."."; + if($dec <= 127) { + $nex = 0; + } + } + } + $i++; + } + return rtrim($result, "."); + } +} + +/** + * @class tcpdf_asn1 + * Asn.1 encode/decode + * @version 1.1 + * @author M Hida + */ +class asn1 { + // =====Begin ASN.1 Parser section===== + /** + * get asn.1 type tag name + * @param string $id hex asn.1 type tag + * @return string asn.1 tag name + */ + protected static function type($id) { + $asn1_Types = array( + "00" => "ASN1_EOC", + "01" => "ASN1_BOOLEAN", + "02" => "ASN1_INTEGER", + "03" => "ASN1_BIT_STRING", + "04" => "ASN1_OCTET_STRING", + "05" => "ASN1_NULL", + "06" => "ASN1_OBJECT", + "07" => "ASN1_OBJECT_DESCRIPTOR", + "08" => "ASN1_EXTERNAL", + "09" => "ASN1_REAL", + "0a" => "ASN1_ENUMERATED", + "0c" => "ASN1_UTF8STRING", + "30" => "ASN1_SEQUENCE", + "31" => "ASN1_SET", + "12" => "ASN1_NUMERICSTRING", + "13" => "ASN1_PRINTABLESTRING", + "14" => "ASN1_T61STRING", + "15" => "ASN1_VIDEOTEXSTRING", + "16" => "ASN1_IA5STRING", + "17" => "ASN1_UTCTIME", + "18" => "ASN1_GENERALIZEDTIME", + "19" => "ASN1_GRAPHICSTRING", + "1a" => "ASN1_VISIBLESTRING", + "1b" => "ASN1_GENERALSTRING", + "1c" => "ASN1_UNIVERSALSTRING", + "1d" => "ASN1_BMPSTRING" + ); + return array_key_exists($id,$asn1_Types)?$asn1_Types[$id]:$id; + } + + /** + * parse asn.1 to array + * to be called from parse() function + * @param string $hex asn.1 hex form + * @return array asn.1 structure + */ + protected static function oneParse($hex) { + if($hex == '') { + return false; + } + if(!@ctype_xdigit($hex) || @strlen($hex)%2!=0) { + echo "input:\"$hex\" not hex string!.\n"; + return false; + } + $stop = false; + while($stop == false) { + $asn1_type = substr($hex, 0, 2); + $tlv_tagLength = hexdec(substr($hex, 2, 2)); + if($tlv_tagLength > 127) { + $tlv_lengthLength = $tlv_tagLength-128; + $tlv_valueLength = substr($hex, 4, ($tlv_lengthLength*2)); + } else { + $tlv_lengthLength = 0; + $tlv_valueLength = substr($hex, 2, 2+($tlv_lengthLength*2)); + } + if($tlv_lengthLength >4) { // limit tlv_lengthLength to FFFF + return false; + } + $tlv_valueLength = hexdec($tlv_valueLength); + $totalTlLength = 2+2+($tlv_lengthLength*2); + $reduction = 2+2+($tlv_lengthLength*2)+($tlv_valueLength*2); + $tlv_value = substr($hex, $totalTlLength, $tlv_valueLength*2); + $remain = substr($hex, $totalTlLength+($tlv_valueLength*2)); + $newhexdump = substr($hex, 0, $totalTlLength+($tlv_valueLength*2)); + $result[] = array( + 'tlv_tagLength'=>strlen(dechex($tlv_tagLength))%2==0?dechex($tlv_tagLength):'0'.dechex($tlv_tagLength), + 'tlv_lengthLength'=>$tlv_lengthLength, + 'tlv_valueLength'=>$tlv_valueLength, + 'newhexdump'=>$newhexdump, + 'typ'=>$asn1_type, + 'tlv_value'=>$tlv_value + ); + if($remain == '') { // if remains string was empty & contents also empty, function return FALSE + $stop = true; + } else { + $hex = $remain; + } + } + return $result; + } + + /** + * parse asn.1 to array recursively + * @param string $hex asn.1 hex form + * @param int $maxDepth maximum parsing depth + * @return array asn.1 structure recursively to specific depth + */ + public static function parse($hex, $maxDepth=5) { + $result = array(); + static $currentDepth = 0; + if($asn1parse_array = self::oneParse($hex)) { + foreach($asn1parse_array as $ff){ + $parse_recursive = false; + unset($info); + $k = $ff['typ']; + $v = $ff['tlv_value']; + $info['depth']=$currentDepth; + $info['hexdump']=$ff['newhexdump']; + $info['type'] = $k; + $info['typeName'] = self::type($k); + $info['value_hex'] = $v; + if(($currentDepth <= $maxDepth)) { + if($k == '06') { + + } else if(in_array($k, ['13', '18'])) { + $info['value'] = hex2bin($info['value_hex']); + } else if(in_array($k, ['03', '02', 'a04'])) { + $info['value'] = $v; + } else { + $currentDepth++; + $parse_recursive = self::parse($v, $maxDepth); + $currentDepth--; + } + if($parse_recursive) { + $result[] = array_merge($info, $parse_recursive); + } else { + $result[] = $info; + } + } + } + } + return $result; + } + // =====End ASN.1 Parser section===== + + + // =====Begin ASN.1 Builder section===== + /** + * create asn.1 TLV tag length, length length and value length + * to be called from asn.1 builder functions + * @param string $str string value of asn.1 + * @return string hex of asn.1 TLV tag length + */ + protected static function asn1_header($str) { + $len = strlen($str)/2; + $ret = dechex($len); + if(strlen($ret)%2 != 0) { + $ret = "0$ret"; + } + $headerLength = strlen($ret)/2; + if($len > 127) { + $ret = "8".$headerLength.$ret; + } + return $ret; + } + + /** + * create various dynamic function for asn1 + */ + private static function asn1Tag($name) { + $functionList = array( + 'seq'=>'30', + 'oct'=>'04', + 'obj'=>'06', + 'bit'=>'03', + 'printable'=>'13', + 'int'=>'02', + 'set'=>'31', + 'expl'=>'a', + 'utime'=>'17', + 'gtime'=>'18', + 'utf8'=>'0c', + 'ia5'=>'16', + 'visible'=>'1a', + 't61'=>'14', + 'impl'=>'80', + 'other'=>'' + ); + if(array_key_exists($name, $functionList)) { + return $functionList[$name]; + } else { + // echo "func \"$name\" not available"; + return false; + } + } + + public static function __callStatic($func, $params) { + $func = strtolower($func); + $asn1Tag = self::asn1Tag($func); + if($asn1Tag !== false){ + $num = $asn1Tag; //valu of array + $hex = $params[0]; + $val = $hex; + if(in_array($func, ['printable', 'utf8', 'ia5', 'visible', 't61'])) { // ($string) + $val = bin2hex($hex); + } + if($func == 'int') { + $val = (strlen($val)%2 != 0)?"0$val":"$val"; + } + if($func == 'expl') { //expl($num, $hex) + $num = $num.$params[0]; + $val = $params[1]; + } + if($func == 'impl') { //impl($num="0") + $val = (!$val)?"00":$val; + $val = (strlen($val)%2 != 0)?"0$val":$val; + return $num.$val; + } + if($func == 'other') { //OTHER($id, $hex, $chr = false) + $id = $params[0]; + $hex = $params[1]; + $chr = @$params[2]; + $str = $hex; + if($chr != false) { + $str = bin2hex($hex); + } + $ret = "$id".self::asn1_header($str).$str; + return $ret; + } + if($func == 'utime') { + $time = $params[0]; //yymmddhhiiss + $oldTz = date_default_timezone_get(); + date_default_timezone_set("UTC"); + $time = date("ymdHis", $time); + date_default_timezone_set($oldTz); + $val = bin2hex($time."Z"); + } + if($func == 'gtime') { + if(!$time = strtotime($params[0])) { + // echo "asn1::GTIME function strtotime cant recognize time!! please check at input=\"{$params[0]}\""; + return false; + } + $oldTz = date_default_timezone_get(); + // date_default_timezone_set("UTC"); + $time = date("YmdHis", $time); + date_default_timezone_set($oldTz); + $val = bin2hex($time."Z"); + } + $hdr = self::asn1_header($val); + return $num.$hdr.$val; + } else { + // echo "asn1 \"$func\" not exists!"; + } + } + // =====End ASN.1 Builder section===== +} +?> \ No newline at end of file diff --git a/tcpdf.php b/tcpdf.php index 60f93c41..24734b1e 100644 --- a/tcpdf.php +++ b/tcpdf.php @@ -1277,13 +1277,25 @@ class TCPDF { * @since 4.6.005 (2009-04-24) */ protected $signature_data = array(); + /** + * Digital signature data. + * @protected + * @since 6.6.2 (2024-04-21) + */ + protected $signature_data_ltv = array(); + /** + * Digital signature data. + * @protected + * @since 6.6.2 (2024-04-21) + */ + protected $signature_data_tsa = array(); /** * Digital signature max length. * @protected * @since 4.6.005 (2009-04-24) */ - protected $signature_max_length = 11742; + protected $signature_max_length = 36864; /** * Data for digital signature appearance. @@ -1299,20 +1311,6 @@ class TCPDF { */ protected $empty_signature_appearance = array(); - /** - * Boolean flag to enable document timestamping with TSA. - * @protected - * @since 6.0.085 (2014-06-19) - */ - protected $tsa_timestamp = false; - - /** - * Timestamping data. - * @protected - * @since 6.0.085 (2014-06-19) - */ - protected $tsa_data = array(); - /** * Regular expression used to find blank characters (required for word-wrapping). * @protected @@ -1991,8 +1989,6 @@ public function __construct($orientation='P', $unit='mm', $format='A4', $unicode $this->setTextShadow(); // signature $this->sign = false; - $this->tsa_timestamp = false; - $this->tsa_data = array(); $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature'); $this->empty_signature_appearance = array(); // user's rights @@ -7680,37 +7676,21 @@ public function Output($name='doc.pdf', $dest='I') { $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc); - // write the document to a temporary folder - $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id); - $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb'); - if (!$f) { - $this->Error('Unable to create temporary file: '.$tempdoc); - } - $pdfdoc_length = strlen($pdfdoc); - fwrite($f, $pdfdoc, $pdfdoc_length); - fclose($f); - // get digital signature via openssl library - $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id); - if (empty($this->signature_data['extracerts'])) { - openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); - } else { - openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']); - } - // read signature - $signature = file_get_contents($tempsign); - // extract signature - $signature = substr($signature, $pdfdoc_length); - $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); - $tmparr = explode("\n\n", $signature); - $signature = $tmparr[1]; - // decode signature - $signature = base64_decode(trim($signature)); - // add TSA timestamp to signature - $signature = $this->applyTSA($signature); - // convert signature to hex - $signature = current(unpack('H*', $signature)); + + require_once(dirname(__FILE__).'/include/tcpdf_cmssignature.php'); + $tcpdf_cms = new tcpdf_cmssignature; + $tcpdf_cms->signature_data = $this->signature_data; + $tcpdf_cms->signature_data_ltv = $this->signature_data_ltv; + $tcpdf_cms->signature_data_tsa = $this->signature_data_tsa; + $tcpdf_cms->log .= "info:Start PKCS7 Signing...\n"; + if(!$signature = $tcpdf_cms->pkcs7_sign($pdfdoc)) { + $tcpdf_cms->log .= "error:PKCS7 Signing end FAILED!\n"; + } + $tcpdf_cms->log .= "info:PDF Sign Finish size \"".strlen($signature)."\" Kb\n"; + $signature = str_pad($signature, $this->signature_max_length, '0'); // Add signature to the document + $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]); $this->bufferlen = strlen($this->buffer); } @@ -7879,10 +7859,10 @@ public function _destroy($destroyall=false, $preserve_objcopy=false) { 'imagekeys', 'sign', 'signature_data', + 'signature_data_ltv', + 'signature_data_tsa', 'signature_max_length', - 'byterange_string', - 'tsa_timestamp', - 'tsa_data' + 'byterange_string' ); foreach (array_keys(get_object_vars($this)) as $val) { if ($destroyall OR !in_array($val, $preserve)) { @@ -13552,7 +13532,7 @@ public function setUserRights( * @author Nicola Asuni * @since 4.6.005 (2009-04-24) */ - public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') { + public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $hashAlgorithm='sha256', $approval='') { // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 // to convert pfx certificate to pem: openssl @@ -13574,6 +13554,7 @@ public function setSignature($signing_cert='', $private_key='', $private_key_pas $this->signature_data['extracerts'] = $extracerts; $this->signature_data['cert_type'] = $cert_type; $this->signature_data['info'] = $info; + $this->signature_data['hashAlgorithm'] = strtolower($hashAlgorithm); $this->signature_data['approval'] = $approval; } @@ -13654,41 +13635,36 @@ protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, * @public * @author Richard Stockinger * @since 6.0.090 (2014-06-16) + * @author M Hida + * @since 6.6.2 (2024-04-21) */ - public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') { - $this->tsa_data = array(); - if (!function_exists('curl_init')) { - $this->Error('Please enable cURL PHP extension!'); - } - if (strlen($tsa_host) == 0) { - $this->Error('Please specify the host of Time Stamping Authority (TSA)!'); - } - $this->tsa_data['tsa_host'] = $tsa_host; - if (is_file($tsa_username)) { - $this->tsa_data['tsa_auth'] = $tsa_username; - } else { - $this->tsa_data['tsa_username'] = $tsa_username; - } - $this->tsa_data['tsa_password'] = $tsa_password; - $this->tsa_data['tsa_cert'] = $tsa_cert; - $this->tsa_timestamp = true; + // other options suggested to be implement: reqPolicy, nonce, certReq, extensions + // and option to abort signing if timestamping failed and LTV enable (embed crl and or ocsp revocation info) + public function setTimeStamp($tsa_host, $tsa_username='', $tsa_password='', $tsa_cert='') { + $this->signature_data_tsa['host'] = $tsa_host; + $this->signature_data_tsa['username'] = $tsa_username; + $this->signature_data_tsa['password'] = $tsa_password; + $this->signature_data_tsa['cert'] = $tsa_cert; } /** - * NOT YET IMPLEMENTED - * Request TSA for a timestamp - * @param string $signature Digital signature as binary string - * @return string Timestamped digital signature - * @protected - * @author Richard Stockinger - * @since 6.0.090 (2014-06-16) - */ - protected function applyTSA($signature) { - if (!$this->tsa_timestamp) { - return $signature; - } - //@TODO: implement this feature - return $signature; + * Set user defined LTV parameters. + * If not set, it will lookup in cert attributes. + * Enable LTV (Long Term Validation) (requires the OpenSSL Library). + * Use with digital signature only! + * @param string $ocspURI Custom OCSP URI address, usefull cert not specify AIA OCSP address in cert attribute. Set null to skip ocsp embedding and set next arguments. + * set false/empty to lookup in cert attribute. + * @param string $crlURIorFILE Custom CDP address/file location, usefull cert not specify CDP address in cert attribute. Set null to skip CRL embedding and set next arguments. + * set false/empty to lookup in cert attribute. + * @param string $issuerURIorFILE Specifies CA Issuer URI address or file name. Its absolutely needed. + * @public + * @author M Hida + * @since 6.6.2 (2024-04-21) + */ + public function setLtv($ocspURI=null, $crlURIorFILE=null, $issuerURIorFILE=null) { + $this->signature_data_ltv['ocspURI'] = $ocspURI; + $this->signature_data_ltv['crlURIorFILE'] = $crlURIorFILE; + $this->signature_data_ltv['issuerURIorFILE'] = $issuerURIorFILE; } /**