Note that even the "Certificate Authority" who signs a key does not know the private key. When somebody requests a certificate, they only send the "hash" to the certificate authority. Therefore, nobody, not even Comodo, should know the private key. There are ways the private key may have been lost. For example, another hacker may have broken in, or it may have been given to a friend, or it may have been left behind on a system. But, beyond a reasonable doubt, this proves the identify of the hacker.
Verification is simple: we just encrypt something with the "public" key, and then decrypt it with the "private" key, then see if things match.
Step #1: download public certificate
To get the public key, I downloaded the forged certificates from https://bugzilla.mozilla.org/attachment.cgi?id=519863. This is in the bug tracking system for Firefox. They shipped an update to make these keys invalid. I saved this in a file named "addons.mozilla.org.cer".
Step #2: download private key from hacker
To get the hacker's private key, I went to his post at http://pastebin.com/X8znzPWH, copied the key, and pasted into a file I called "private.pem". I've also included the private key below because it appears the ComodoHacker has removed it from his pastebin post.
Step #3: extract public key
A certificate contains a lot of information, but all I wanted was the public key. Therefore, I ran the command:
openssl x509 -noout -inform DER -in addons.mozilla.org.cer -pubkey > public.pemThis uses the "OpenSSL" tool, the same code used in most web servers for doing SSL. I tell it I'm working with an X.509 certificate, and that the certificate is encoded as ASN.1 DER (Distinguished Encoding Rules). I tell it I want to extract the public-key. I save it to the file public.pem.
Step #4: create test file
I then create a file called "verify.txt" whose contents are simply "verify".
echo "verify" >verify.txt
Step #5: encrypt test file with public key
I then encrypt the file from the last step:
openssl rsautl -encrypt -inkey public.pem -pubin -in verify.txt -out encryptedAgain, I'm using the OpenSSL tool. This time, I'm using the RSA crypto algorithm. (Note: the RSA algorithm has essentially nothing to do with RSA the company). I tell it I'm going to encrypt the input file using the public key in the key file. I tell it to produce a file named "encrypted".
Step #6: decrypt test file with private key
I then decrypt that file with the private key. Remember: anything encrypted with the public key can only be decrypted with the matching private key.
openssl rsautl -decrypt -inkey private.pem -in encrypted -out decrypted.txtAgain, I'm using the RSA feature of OpenSSL. This time, the encrypted file from the last step becomes the input. The output is written to the file "decrypted.txt".
Step #7: verify test files match
Lastly, I dump the contents of "decrypted.txt" and see that it matches the original file with the contents of "verify".
I created a snapshot of VMware as I did this. In this snapshot, you can see all the steps that I describe above. Click on the image in order to get the big version (the full image won't fit within the space of this blog).
Why -modulus is inadequate
As many people pointed out, I could've used the "-modulus" option for OpenSSL to see if the two keys are equal. That's because the "modulus" (the product of the two primes) makes up both the public and private keys. An example:
openssl rsa -in private.pem -noout -modulus openssl x509 -in addons.mozilla.org.cer -inform DER -noout -modulus
This doesn't actually work (I prove it with a fake private key below). All the OpenSSL tool does at this point is extract the "modulus" field from the files -- it doesn't verify that it's correct. I could easily create a "private.pem" file that contains the correct modulus for a public-key -- but which can't be used to properly encrypt anything encrypted by that public-key.
In other words, using -modulus to verify if a private key matches a public key assumes that nobody is trying to tamper with the key. It's like the difference between a "checksum" which only detects accidental corruption vs. a "hash" that detects intentional corruption.
You would need additional steps, like the following that checks the validity of a private key:
openssl rsa -in private.pem -noout -check
Then there is the problem of making sure that the two modulus are the same. You can't visually inspect them -- they are too big. As a hacker, I could make sure that the first few bytes and last few bytes are the same, and it would trick most people. Therefore, If have to use "diff" or "uniq", or hash them, to make sure they are the same.
This would end up taking MORE steps than my original method.
Then I'd feel obligated to explain the entire RSA algorithm, what precisely the public and private keys contain, and why the modulus (the product of the two primes) is the same for both.
After all this, there's still a chance I made a mistake. The fact that many people assume comparing the modulus was adequate demonstrates how easy it is for even experts to make mistakes.
I chose the method that I felt would have the smallest chance of me making a mistake, and the biggest chance of being understood by readers. I think it's an excellent choice.
In order to prove this, I've created a fake private certificate that I put in a file called "fakeprivate.pem. It contains the modulus (and public exponent) from the certificate, but all the other fields have been filled with 0xFF (which appears as the string of //// when BASE64 encoded). I include the file here so that you can test this yourself:
I then re-run my test, and it shows decryption errors, thus proving it's a fake private key attempting to match the public key. But when, I run your -modulus test, no errors are generated, and the moduluses (sic) appear to match.-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAq8ZtNvMVc3iDc850hdWu7LLw4CQfE4O4IKy7mv6Iu6uhHQsf RQCqSbc1Nwxq70dMudG+41cSBI2Sx7bsAby22seBOCCtcoXmDvyBbAetaHY4xUTX zMZKxZc+ZPRR5vB+suxW9yWCTUmYyxaY3SPxiZHRF5dAmSbW4qIrXt+9ifIbGlMt zFBBetA9KgxVcBQB6VhJEHoLk4KL4R7tOoAQgs6WijTwzNfTubRQh1VUCbidQihV AOWMNVS/3SWRRrcN5V2DqOWL+4TkPK522sRDK1t0C/i+XWjxeFu1zn3xXZlA2sru OIFQvpihbLgkrfOvjA/XESgshBhMfbXZjzC1GwIDAQABAoIBAQD///////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //8P////AoGBAP////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////AoGBAP// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////8P////AoGAAP////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////8CgYAA//////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// /////wKBgA////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////// -----END RSA PRIVATE KEY-----
Your method can be improved by doing the "-check". It fails with the error "n does not equal p q", which is the check you are looking for. But as I said, I did't want to fix your method of checking the modulus, I wanted a method that I felt the average person reading the post could read and feel comfortable that yes, the key has been validated.
Here is a screenshot of the tests I describe above (click on the image to get a larger picture):
By the way, there are other, arguably easier, ways the key, but I didn't want easy, I wanted straight-forward, mistake-free, and verifiable.
-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAq8ZtNvMVc3iDc850hdWu7LLw4CQfE4O4IKy7mv6Iu6uhHQsf RQCqSbc1Nwxq70dMudG+41cSBI2Sx7bsAby22seBOCCtcoXmDvyBbAetaHY4xUTX zMZKxZc+ZPRR5vB+suxW9yWCTUmYyxaY3SPxiZHRF5dAmSbW4qIrXt+9ifIbGlMt zFBBetA9KgxVcBQB6VhJEHoLk4KL4R7tOoAQgs6WijTwzNfTubRQh1VUCbidQihV AOWMNVS/3SWRRrcN5V2DqOWL+4TkPK522sRDK1t0C/i+XWjxeFu1zn3xXZlA2sru OIFQvpihbLgkrfOvjA/XESgshBhMfbXZjzC1GwIDAQABAoIBAQCJoijaEXWLmvFA thiZL7jEATCNd4PK4AyFacG8E9w8+uzR15qLcFgBTqF95R49cNSiQtP/VkGikkkc ao25aprcu2PnNA+lpnHKajnM9G3WOHuOXHXIps08es3MmBKTxvjNph6cUlqQULrz Zry+29DpmIN/snpY/EzLNIMptn4o6xnsjAIgJDpQfFKQztxdmZU6S6eVVn0mJ5cx q+8TTjStaMbh+Yy73s+rcaCXzL7yqWDb1l5oQJ/DMYNfufY6lcLgZUMwFxYKjCFN ScAPCiXFUKTzY3Hy1Z4tLndFxipyEPywDep1TB2nMb+F3OOXUs3z+kKVjGFaGnLZ 591n3x3hAoGBAOOgsb4QybjHh9+CxhUkfsqcztGGdaiI3U5R1qefXL7R47qCWfGc FKdoJh3JwJzHEDX68ZmHz9dPhSXw6YrlLblCi6U/3g7BOMme5KRZKBTjHFo7O9II B0laE5ISRH4OccsOC3XUf9XBkm8szzEBj95DgzB0QydPL4jp7NY0h0QrAoGBAMEv jEFkr/JCRe2RWUSx/a1WT/DHnVLMnDb/FryN2M1fAerpMYNUc2rnndjp2cYbsGLs cSF6Xecm3mUGqn8Y5r8QqBwxCp5OunCFCXEJvkiU3NSs8oskCsB8QJ6vk3qmauUK jClX91heSCigwhC2t+1txnF290m/y0T46EfqOSrRAoGAUlyVk4D9jEdeCWiHBaVj 3ynnx3ZQYj/LW4hPE+2coErPjG+X3c0sx/nuOL8EW3XHjtCS1IuIj45tTfIifqg3 6B2E67D1Rv9w7br5XeIIl64pVxixp2hSQp8+D49eiwHs+JzHVsYhzxUwR9u9yCyZ gsGI2WJn3fRP7ck+ca8l9msCgYB4B2Hec3+6RqEKBSfwvaI+44TRtkSyYDyjEwT+ bCeLGn+ng/Hmhj8b6gKx9kH/i86g+AUmZtAXQZgmLukaBM/BYMkCkxnk2EeQh6gh Goumrw8x+K7N8rvXcpv3vGEmcGW0H0SMn4In3pR44cER/2Tx2SXV87Obl9Xk6b3w iL+yMQKBgFjXcmiBW8lw3l2CaVckd/1SzrT80AfRpMT9vafurxe+iAhl9SDAdoZe 3RlshoItDQLW1ROlkLhM7Pdq/XZvLRm128hiIGKTDBnxtfN8TKAg+V7V+/TTfdqv 8jq7epvZsq5vjOC1FZh2gOhf50QwpqDJktjdyka1sPiBKQSoxfbZ -----END RSA PRIVATE KEY-----