Search code examples
phpsslhashssl-certificatedigital-signature

Manually verifying a digital signature of SSL cert in hex format


I'm creating a program that, given a purely hexidecimal format of a cert, will extract and verify the digital signature.

I have the following example cert in hex format:

308206cd308205b5a003020102021001c3f8388b669f492cfac8731937fc8e300d06092a864886f70d01010b0500304f310b300906035504061302555331153013060355040a130c446967694365727420496e633129302706035504031320446967694365727420544c532052534120534841323536203230323020434131301e170d3232303132313030303030305a170d3233303230353233353935395a3079310b3009060355040613025553311330110603550408130a43616c69666f726e6961310f300d06035504071306497276696e6531253023060355040a131c426c697a7a61726420456e7465727461696e6d656e742c20496e632e311d301b0603550403131475732e61637475616c2e626174746c652e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100b3838051fd24a13681e97e557476f22891b05355157c9ded1c7ca610f07f4d5a5c987e88318c213a62481fc0fd43ece409170cef6ac95ab106dd4cd046e4c3660097d2aeefffc7fe470307b17c9febd3799352a1971b0303360bd9c028c1939d9fbd0148eb0d4dbebfad23e5e3ab3b94943351cd8fe11556bf1ec7370a615ed74c5f5d66d62799f66bb1d1f3a4b661173cafed4b722b03f046572e73eae216b8e35e73add6cb86243fe4b457ffb8e6baf50af563110da121255c676248e8976ab1fe8c221a964cc550901b5191730aa6d616dc6aa9ee87fcaf7ac56e48f1592eeeb836065dd6c1078e724db7607677298169d9225eac5fdc974a21565621d4fb0203010001a382037930820375301f0603551d23041830168014b76ba2eaa8aa848c79eab4da0f98b2c59576b9f4301d0603551d0e04160414402287da2dd56eddfa85ccda1f946e493a08a99b301f0603551d1104183016821475732e61637475616c2e626174746c652e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b0601050507030230818f0603551d1f0481873081843040a03ea03c863a687474703a2f2f63726c332e64696769636572742e636f6d2f4469676943657274544c53525341534841323536323032304341312d342e63726c3040a03ea03c863a687474703a2f2f63726c342e64696769636572742e636f6d2f4469676943657274544c53525341534841323536323032304341312d342e63726c303e0603551d20043730353033060667810c0102023029302706082b06010505070201161b687474703a2f2f7777772e64696769636572742e636f6d2f435053307f06082b0601050507010104733071302406082b060105050730018618687474703a2f2f6f6373702e64696769636572742e636f6d304906082b06010505073002863d687474703a2f2f636163657274732e64696769636572742e636f6d2f4469676943657274544c53525341534841323536323032304341312d312e637274300c0603551d130101ff0402300030820180060a2b06010401d679020402048201700482016c016a007700e83ed0da3ef5063532e75728bc896bc903d3cbd1116beceb69e1777d6d06bd6e0000017e7eaf5af30000040300483046022100ce2d33ce8de3764ccc9d8ac03b936612fd10a4fc1815b3e092352643aa8d07e9022100e2a741a4c3ef5c90882f66951075be12ad54bdc573bd3ae3b17c662f63c6e29400760035cf191bbfb16c57bf0fad4c6d42cbbbb627202651ea3fe12aefa803c33bd64c0000017e7eaf5b14000004030047304502201ef42884353339d4921bf2e47af540b15af6bab94de74c3e3a1224418e9bc4ef0221008846deaa381d08b65607dc3290ba152f1c5aab5c7d03cf116682da8a45922f18007700b3737707e18450f86386d605a9dc11094a792db1670c0b87dcf0030e7936a59a0000017e7eaf5b4400000403004830460221008dc3b79af62dce53af04b8959afcb1f858bea16872eba97b5cc2c8f308b32d490221009b658042c5a0841d670ac1303c06b42e6a494596cb5e333fbdeddc9248e05dcd300d06092a864886f70d01010b0500038201010010170d137389daa010c477c0dde1af6529725489ad07822ace988cb78969683e1686a9fcde2d166b2c7ae5774e782ce7270904fb5abfdc0de25123d7cbcbd855598d35a027d1f5bdf3bd754eb3c6c9b7cc74de740b0b576c629dfd9ff5cca4d773f8bb499cc6f0aa39f269d219f019e62cbb354f32fa171226f58a4582b711e779268baa5d4bcc44f9dda3f2b867344ea29ecb6e28f4f818ef3594da16dc882cfd65cd2875a50d9ee9dff5d297135b5890ce4f583ab770c1d79bd5e7c4b2e1ae2ca0425cf63e12f151e8b6c6228ee53be299ee5ef09c85df0ad24b66e32b37f93193c2495f2de78d28126b3aa8a406b44d8469292fc242c5f39db44be56f4d91

From here, I am able to successfully derive the digital signature (this is located on the end of the cert hex above)

10170d137389daa010c477c0dde1af6529725489ad07822ace988cb78969683e1686a9fcde2d166b2c7ae5774e782ce7270904fb5abfdc0de25123d7cbcbd855598d35a027d1f5bdf3bd754eb3c6c9b7cc74de740b0b576c629dfd9ff5cca4d773f8bb499cc6f0aa39f269d219f019e62cbb354f32fa171226f58a4582b711e779268baa5d4bcc44f9dda3f2b867344ea29ecb6e28f4f818ef3594da16dc882cfd65cd2875a50d9ee9dff5d297135b5890ce4f583ab770c1d79bd5e7c4b2e1ae2ca0425cf63e12f151e8b6c6228ee53be299ee5ef09c85df0ad24b66e32b37f93193c2495f2de78d28126b3aa8a406b44d8469292fc242c5f39db44be56f4d91

I then take the public key that issued this cert, and use it to decrypt the Digital Signature Digest, which looks like this:

3031300d0609608648016503040201050004200bf3dcf2340b972e97fe3c8493e11eeee01f298939734690d0b4e79e1f5701b4

At this point is where I am trying to verify the Digital Signature by creating a hash of the entire SSL cert (this cert uses SHA256 Hash for digest):

hash('sha256', hex2bin($CertWithoutSignature))

Where $CertWithoutSignature is the same hex above (1st hex string above) WITHOUT the digital signature (2nd hex string above).

At this point I'm a bit confused, because not only does the length of the sha256 hash not match, neither does the data. I know I am decrypting the digest correctly, because otherwise I would get an error if the key were invalid, etc. But that extracted value is 102 characters long, and does not match what I will ever get from sha256 since the length of the string is totally different. Basically I know it is valid if I can get them to match because that means the document is the exact same and therefore will get the same hash. Any help is appreciated. Thanks.


Solution

  • You are forgetting that the hash is encoded and then padded in the signature, assuming that you are using PKCS#1 v1.5 padding within the certificate.

    Hashing everything up to the signature is not correct either, you need to hash the TBSCertificate, where TBS means To Be Signed.

    For more information, I would strongly recommend to read the X.509 specifications in RFC 5280. And, since ASN.1 encoders / decoders are rather complex you might want to use a library function instead of programming it yourself.