How does a public key verify a signature? - digital-signature

I am trying to get a better grapple on how public/private keys work. I understand that a sender may add a digital signature to a document using his/her private key to essentially obtain a hash of the document, but what I do not understand is how the public key can be used to verify that signature.
My understanding was that public keys encrypt, private keys decrypt... can anyone help me understand?

Your understanding of "public keys encrypt, private keys decrypt" is correct... for data/message ENCRYPTION. For digital signatures, it is the reverse. With a digital signature, you are trying to prove that the document signed by you came from you. To do that, you need to use something that only YOU have: your private key.
A digital signature in its simplest description is a hash (SHA1, MD5, etc.) of the data (file, message, etc.) that is subsequently encrypted with the signer's private key. Since that is something only the signer has (or should have) that is where the trust comes from. EVERYONE has (or should have) access to the signer's public key.
So, to validate a digital signature, the recipient
Calculates a hash of the same data (file, message, etc.),
Decrypts the digital signature using the sender's PUBLIC key, and
Compares the 2 hash values.
If they match, the signature is considered valid. If they don't match, it either means that a different key was used to sign it, or that the data has been altered (either intentionally or unintentionally).

The keys work inversely:
Public key encrypts, private key decrypts (encrypting):
openssl rsautl -encrypt -inkey public.pem -pubin -in message.txt -out message.ssl
openssl rsautl -decrypt -inkey private.pem -in message.ssl -out message.txt
Private key encrypts, public key decrypts (signing):
openssl rsautl -sign -inkey private.pem -in message.txt -out message.ssl
openssl rsautl -inkey public.pem -pubin -in message.ssl -out message.txt
Below is an example script to test this whole flow with openssl.
#!/bin/sh
# Create message to be encrypted
echo "Creating message file"
echo "---------------------"
echo "My secret message" > message.txt
echo "done\n"
# Create asymmetric keypair
echo "Creating asymmetric key pair"
echo "----------------------------"
openssl genrsa -out private.pem 1024
openssl rsa -in private.pem -out public.pem -pubout
echo "done\n"
# Encrypt with public & decrypt with private
echo "Public key encrypts and private key decrypts"
echo "--------------------------------------------"
openssl rsautl -encrypt -inkey public.pem -pubin -in message.txt -out message_enc_pub.ssl
openssl rsautl -decrypt -inkey private.pem -in message_enc_pub.ssl -out message_pub.txt
xxd message_enc_pub.ssl # Print the binary contents of the encrypted message
cat message_pub.txt # Print the decrypted message
echo "done\n"
# Encrypt with private & decrypt with public
echo "Private key encrypts and public key decrypts"
echo "--------------------------------------------"
openssl rsautl -sign -inkey private.pem -in message.txt -out message_enc_priv.ssl
openssl rsautl -inkey public.pem -pubin -in message_enc_priv.ssl -out message_priv.txt
xxd message_enc_priv.ssl
cat message_priv.txt
echo "done\n"
This script outputs the following:
Creating message file
---------------------
done
Creating asymmetric key pair
----------------------------
Generating RSA private key, 1024 bit long modulus
...........++++++
....++++++
e is 65537 (0x10001)
writing RSA key
done
Public key encrypts and private key decrypts
--------------------------------------------
00000000: 31c0 f70d 7ed2 088d 9675 801c fb9b 4f95 1...~....u....O.
00000010: c936 8cd0 0cc4 9159 33c4 9625 d752 5b77 .6.....Y3..%.R[w
00000020: 5bfc 988d 19fe d790 b633 191f 50cf 1bf7 [........3..P...
00000030: 34c0 7788 efa2 4967 848f 99e2 a442 91b9 4.w...Ig.....B..
00000040: 5fc7 6c79 40ea d0bc 6cd4 3c9a 488e 9913 _.ly#...l.<.H...
00000050: 387f f7d6 b8e6 5eba 0771 371c c4f0 8c7f 8.....^..q7.....
00000060: 8c87 39a9 0c4c 22ab 13ed c117 c718 92e6 ..9..L".........
00000070: 3d5b 8534 7187 cc2d 2f94 0743 1fcb d890 =[.4q..-/..C....
My secret message
done
Private key encrypts and public key decrypts
--------------------------------------------
00000000: 6955 cdd0 66e4 3696 76e1 a328 ac67 4ca3 iU..f.6.v..(.gL.
00000010: d6bb 5896 b6fe 68f1 55f1 437a 831c fee9 ..X...h.U.Cz....
00000020: 133a a7e9 005b 3fc5 88f7 5210 cdbb 2cba .:...[?...R...,.
00000030: 29f1 d52d 3131 a88b 78e5 333e 90cf 3531 )..-11..x.3>..51
00000040: 08c3 3df8 b76e 41f2 a84a c7fb 0c5b c3b2 ..=..nA..J...[..
00000050: 9d3b ed4a b6ad 89bc 9ebc 9154 da48 6f2d .;.J.......T.Ho-
00000060: 5d8e b686 635f b6a4 8774 a621 5558 7172 ]...c_...t.!UXqr
00000070: fbd3 0c35 df0f 6a16 aa84 f5da 5d5e 5336 ...5..j.....]^S6
My secret message
done

If I had to re-phrase your question from how I understand it, you are asking the following:
If public key cryptography ensures that a public key can be derived from a private key, but a private key cannot be derived from a public key, then you might wonder, how can a public key decrypt a message signed with a private key without the sender exposing the private key within the signed message to the recipient? (re-read that a few times until it makes sense)
Other answers have already explained how asymmetric cryptography means that you can either:
Encrypt with public key, decrypt with matching private key (pseudocode below)
var msg = 'secret message';
var encryptedMessage = encrypt(pub_key, msg);
var decryptedMessage = decrypt(priv_key, encryptedMessage);
print(msg == decryptedMessage == 'secret message'); // True
Encrypt with private key, decrypt with matching public key (pseudocode below)
var msg = 'secret message';
var encryptedMessage = encrypt(priv_key, msg);
var decryptedMessage = decrypt(pub_key, encryptedMessage); // HOW DOES THIS WORK???
print(msg == decryptedMessage == 'secret message'); // True
We know that both example #1 and #2 work. Example #1 makes intuitive sense, while example #2 begs the original question.
Turns out, elliptic curve cryptography (also called "elliptic curve multiplication") is the answer to the original question. Elliptic curve cryptography is the mathematical relationship that makes the following conditions possible:
A public key can be mathematically generated from a private key
A private key cannot be mathematically generated from a public key (i.e. "trapdoor function")
A private key can be verified by a public key
To most, conditions #1 and #2 make sense, but what about #3?
You have two choices here:
You can go down a rabbit-hole and spend hours upon hours learning how elliptic curve cryptography works (here is a great starting point)... OR...
You can accept the properties above--just like you accept Newton's 3 laws of motion without needing to derive them yourself.
In conclusion, a public/private keypair is created using elliptic curve cryptography, which by nature, creates a public and private key that are mathematically linked in both directions, but not mathematically derived in both directions. This is what makes it possible for you to use someone's public key to verify that they signed a specific message, without them exposing their private key to you.

The public key encrypts and only the private key can decrypt it, and the reverse is true. They both encrypt to different hashes but each key can decrypt the other's encryption.
There are a few different ways to verify that a message came from some expected sender. For example:
The sender sends:
The message
The hash of the message encrypted with their private key
The receiver:
Decrypts the signature (2) with the public key to obtain a message, supposedly the same message as (1) but we don't know yet. We now have two messages that we need to verify are identical. So to do this, we will encrypt them both with our public key and compare the two hashes. So we will ....
Encrypt the original message (1) with the public key to obtain a hash
Encrypt the decrypted message (3) to get a second hash and compare to (4) to verify that they are identical.
If they aren't identical it means either the message was tampered with or it was signed with some other key and not the one we thought...
Another example would be for the sender to use a common hash that the receiver might know to use as well. For example:
The sender sends:
A message
Takes a known hash of the message, then encrypts the hash with the private key
The receiver:
Decrypts (2) and gets a hash value
Hashes the message (1) with the same hash used by the sender
Compares the two hashes to make sure they match
This again ensures the message wasn't tampered with and it is from the expected sender.

Thought I'd provide a supplemental explanation for anyone looking for something more intuitively revealing.
A big part of this confusion arises from naming 'public keys' and 'private keys' as such because how these things actually work is directly at odds with how a 'key' is understood to be.
Take encryption for example. It could be thought of as working like so:
The parties that want to be able to read the secret messages each keep a key
hidden (i.e. a private key)
The parties that want to be able to send secret messages all have the ability to obtain an unlocked locked (i.e. a public lock)
Then sending a secret message is as easy as locking it with an unlocked lock, but unlocking it afterwards can only be done with one of the hidden keys.
This allows secret messages to be sent between parties, but from an intuitive standpoint here, 'public lock' is a more suitable name than 'public key'.
However, for sending digital signatures the roles are somewhat reversed:
The party that wants to sign messages is the only one with access to the unlocked locks (i.e. a private lock)
The parties that want to verify the signature all have the ability to obtain a key (i.e. a public key)
Then what the signer does is create two identical messages: the one that anyone can read and one to accompany it, but which they lock with one of their private locks.
Then when the receiver gets the message, they can read it, and then use the public key to unlock the locked message and compare the two messages. If the messages are the same, then they know that:
The unlocked message wasn't tampered with during travel and,
The message must have been from the person who has the matching lock to their public key.
And finally, this entire system only works if anyone who wants to validate a signer's signature has an authoritative place to go to to get the matching key to the signer's locks. Otherwise, anyone can say "Hey, here's the key to so-and-so's private lock", send you a message pretending to be them but lock it with their private lock, you perform all the above steps and believe the message must actually be from the person you thought, but you're fooled because you were mislead as to the true owner of a public key.
So long as there's a trust-worthy source for retrieving a signer's public key, you'll know who the rightful owner of a public key is, and will be able to validate their signature.

To your question - i was looking at the RSA implementation. And got more clarity on the way a public key is used to verify the signature using a private key. Undoubtedly, the private key is not exposed. Here is how...
Trick here is to hide the private key within a function. In this case, (p-1)*(q-1).
Consider p to be the private key and e to be the public key. p is encapsulated within another function to make it hidden.
E.g., `d = (p-1)(q-1); d * e = 1` (d is the inverse of e - public key)
Data sent = [encrypted(hash), message] = [m ^d, message];
where m is the message
Suppose
'Data sent' = y
To check for the integrity we find y^e to get m. Since m ^(d*e) = m ^1 = m.
Hope this helps! :)

If I encrypt some text with my private key then anyone who has my public key can decrypt it. Public key being public, anyone can have it (including thieves and cheats), so what is the point of encrypting a text with my private key (given it can be decrypted with my public key, which is available publicly)?
Well it gives us authenticity. I mean, If you were able to decrypt a message with my public key, then you can say the message was by me. But, there is a but. Suppose someone decrypted a message with my public key and got the message "Hi!", does it mean I said "Hi!". It is possible that some other private key was used to encrypt a message, and coincidentally my private key decrypt it as a meaningful text instead of gibberish.
Well this is why we also need to provide the exact message with our public key. So that, the receiver can compare it with the decrypted message.
So, we provide
Public Key
Original Message
Encrypted Message
If Original Message = Decrypt(Encrypted Message, Public Key) then the message inside was definitely by me as only I have the private key.
Bonus:
Always sending the "Original Message" is not convenient. The "Original Message" can be a 4GB ISO file. It is better to calculate a hash (A one-way hash function, also known as a message digest, is a mathematical function that takes a variable-length input string and converts it into a fixed-length binary sequence that is computationally difficult to invert—that is, generate the original string from the hash) of the original message and send it.
So, now we are sending:
Public Key
Hash of the Original Message
Encrypted Hash of the Original Message
Now If Hash(Original Message) = Decrypt(Encrypted Message, Public Key) then the message inside was definitely by me as only I have the private key.

I think the big issue in the misunderstanding is that when people read "Asymmetric", in their heads they think "Ok, one key encrypts and the other decrypts, hence they are asymmetrical". But if you understand that Asymmetrical actually means "IF key A encrypted data, then its "sister" key B can decrypt data. If Key B was used to encrypt data, then key A can now only decrypt." Symmetric means the same key that was used to encrypt the data can be used to decrypt the data.

Here is an example of public key verify a signature using Python
you need to install pycryptodome. taken from here
# pip install pycryptodome
from Crypto.PublicKey import RSA
from hashlib import sha512
# create RSA key-pair
keyPair = RSA.generate(bits=1024)
public_key = (keyPair.e, keyPair.n)
private_key = (keyPair.d, keyPair.n)
msg = b'A message for signing'
hash = int.from_bytes(sha512(msg).digest(), byteorder='big')
# RSA sign the message using private key
signature = pow(hash, private_key[0], private_key[1])
# RSA verify signature using public key
hashFromSignature = pow(signature, public_key[0], public_key[1])
print("Signature valid:", hash == hashFromSignature)

Related

GPG verify file and detached signature with given public key

I have a public key pubkey.asc.
Also I have a file file.txt and its detached signature file.txt.asc.
I want to check the following steps:
file.txt.asc is a signature for file.txt
file.txt.asc was created using secret key from a keyring with public key pubkey.asc
I can do the 1st step using gpg --verify file.txt.asc file.txt. This command outputs public key fingerprint which was used to create a signature:
gpg: Signature made <date and time>
gpg: using RSA key <fingerprint>
...
I can see pubkey.asc fingerprint just by running gpg pubkey.asc.
Can I use this information and just check if fingerprints are equal to perform 2nd step? If not, how can I verify a file signature with given public key?
I saw this question about how to verify if signature matches public key, but it only works if key has extension .gpg which is not my case.
OpenPGP key's fingerprint is a SHA1 hash of public key itself plus some additional data, and it uniquely identifies the key (excluding collision cases, which are not known yet for OpenPGP key fingerprints).
So, yeah, seeing 'good signature made by key [fingerprint]' is enough to rely on fact that signature is made by the aforementioned key.

Digital Key Signature without retrieving the private key - Luna HSM

I currently try to do a ECDSA Signature with a Luna HSM. This did work so far.
KeyStore ks = KeyStore.getInstance("Luna");
ks.load(null, null);
Key key = ks.getKey("keyAlias", null);
String message = "Sign this!";
byte messageBytes[] = message.getBytes();
Signature sig = Signature.getInstance("SHA256withECDSA","LunaProvider");
sig.initSign((PrivateKey) key);
sig.update(messageBytes);
But now I'm wondering if it is possible to do a key signature, without retrieving the private key out of the HSM? My understanding is, that retrieving the private key out of the HSM contradicts the whole idea of an HSM.
It would be great, if somebody could help me with this.
Thanks a lot in advance.
Have you double checked? Is it returning key or just handle to private key? And then signing operation happens inside HSM using private key handle ?! Thus private key may not come out of HSM.

Problems decrypting data in node.js (RSA PKCS 1.5)

I am having great difficulty implementing some encryption for a node.js project. I am using node-rsa which in turn uses the built in node crypto module.
My code appears to encrypt the data just fine and I can decrypt it. Unfortunately, the system I am sending the data to cannot. Having hit a bit of a wall I've tried decryption some data generated the other end. That also fails.
What is unusual in this case is that the data has to be encrypted using the PRIVATE key and decrypted using the PUBLIC key. As stupid as this is, I had no say in the decision to use this mechanism and it is already in wide use in a UK pubic transport ticketing system. So there is no possibility of doing anything different.
The specification [only] says:
The payload data (Appendix A) is encrypted using the TIS private key,
using 1024Bit RSA PKCS#1v1.5. (This is a freely available public
standard). This protects a payload of up to 116 bytes, or 928Bits,
creating a 128 byte or 1024Bit encrypted output. The RSA encryption
algorithm adds a header of length 12 bytes to the payload before
encryption to improve security. For decryption purposes, this header
should be removed to get the correct payload.
The format of this header is: 0x00 [10 non-zero bytes which are
padding] 0x00 [content]. This is often done automatically by
encryption/decryption software.
Here is an example public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDriuvWAKiTKFgmjHjHYjWK4IZ6
W1UOdrntxFAGT1szZlbKJfWGeBOPSk6MnW6AeX54zEta74Dnoj4MBT+MlekVQ2zp
G3qIZpp4DR+kF6eVRlguUg9l/Fgr9ZrFgkumCKIx/ccc6rvZcNvPnOVdPCBVoMRr
3X59WlLmH4d+RAivAQIDAQAB
-----END PUBLIC KEY-----
I've derived this from the Modules / Exponent listed below... I think it is correct...?
Which is [supposedly] valid to decode this encrypted data:
1f876b22ea8af4341c4198347aabf1b1ba40649ad6918bbe26ebfcf4633786a2669ee499d796cf438a76ff0d4f6efc43f605994330e6fb88c8117eea6aad1a12c2525940f7b9946b3f6cbd674e2740070400ae6f2cf1ed6d36f610822a23d5a8a4717ecf671bf0530200f75a269951020be9f69a992d423f55eb6fefe6caa449
I get the following error:
decryptData error Error: Error during decryption (probably incorrect key). Original error: Error: error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01
I hate to say it, but I am now completely stumped. If anyone is able to decrypt that data using the supplied key and can explain how I would be very grateful.
Just to save the usual response to this sort of question that I see whenever anyone else has asked a similar question in the past. Please do not comment if all you want to say is the process is wrong. That is not helpful in any way. Just to confirm, whilst encryption with the private key is unusual, it is not impossible....It is supported by the RSA specification.
There are several ticket retailers who are encrypting their data this way and several scanning equipment manufacturers who are producing equipment that decrypts in this way. It's out there, it's working.
The only issue seems to be that there is some small mismatch between the encrypted data I am seeing from node-rsa (node.js crypto library) and the example data I have been given.
This is the original format the keys were given to me.....
Appendix D - Example Security Keys for Tickets
All example tickets are encrypted using the following fixed 1024Bit RSA key
(Never use these keys in production, you must generate your own key pair for this purpose as this private key cannot be considered secure and is for testing only)
Modulus = 16540368155253773454125654483794538435594869143015457768150677916704644349481001621163 93533906833356026635527060528398017212524170789714748325265707476885395169247950736258 76338137535298120673942399072260833758038152595105311260002268650951968659366409471762 707986435753824060971148933772149012757770173722369
= HEX [00eb8aeb d600a893 2858268c 78c76235 8ae0867a 5b550e76 b9edc450 064f5b33 6656ca25 f5867813 8f4a4e8c 9d6e8079 7e78cc4b 5aef80e7 a23e0c05 3f8c95e9 15436ce9 1b7a8866 9a780d1f a417a795 46582e52 0f65fc58 2bf59ac5 824ba608 a231fdc7 1ceabbd9 70dbcf9c e55d3c20 55a0c46b dd7e7d5a 52e61f87 7e4408af 01]
Public Exponent = 65537
= HEX [010001]
Private Exponent = 23262061627321059848127951748956201956280865601289878122136319690902346303487555417896 45421978315004119428666694674801184773156427997741861134011936129888508204465790901818 10860126020371138225614906842567831455763314175530505369901364910691824152015094858350 72189773434923487177372245370361799421231555208773
= HEX [21205394 b0590501 3a8c895a ff2797c2 255ba45f adf1afce ec5a9caa 96848c11 0b89b896 f44774f0 c5119103 1f246071 e209515b c3ad4c66 6bf582d3 72312f2b 7250fe61 f6abed7f e219c08d c3985ae1 3f6b6db2 0e3c040b df7a817d 14a5a6f1 20d94047 08512132 aca00baa 29805440 4ad5dec2 1bd544bb 8938c74b 2904e645]
Just to confirm... I was given incorrect data.

failed to read private key from .pem file in c

I created mykey.pem using command
"openssl genrsa -out mykey.pem 1024"
and then I separate public key using command
'openssl rsa -in key.pem -pubout -out pubkey.pem'
I am reading private key using function,
PEM_read_RSAPrivateKey(fp,NULL,NULL,NULL)
But I could not retrieve the private key.
Do i have to get rid of headers like 'Begin RSA private key' and 'End RSA private key'? //Which i tried but didn't work
Do I have to send any other value to the PEM_read_RSAPrivateKey function?
or use some other function to retrieve the private key?
if you are still not able to retrieve the private key using
PEM_read_RSAPrivateKey()
even after supplying the passphrase, a reason could be that you have not initialised the OpenSSL library properly, try adding
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
before you call
PEM_read_RSAPrivateKey()
You do not need to get rid of headers from the file. However, if the file is password protected, you need to pass the password into function PEM_read_RSAPrivateKey.
You can also see PEM_read_PrivateKey. Other functions are listed here.
If the fp points to a file with RSA private key in PEM form and without any password, then it should succeed.

Key Management: Public/Subordinate key

I was looking at GnuPG manual (Manual) and came across below section at page 18:
chloe% gpg -edit-key chloe#cyb.org
Secret key is available.
pub 1024D/26B6AAE1 created: 1999-06-15 expires: never trust: -/u
sub 2048g/0CF8CB7A created: 1999-06-15 expires: never
sub 1792G/08224617 created: 1999-06-15 expires: 2002-06-14
sub 960D/B1F423E7 created: 1999-06-15 expires: 2002-06-14
(1) Chloe (Jester) <chloe#cyb.org>
(2) Chloe (Plebian) <chloe#tel.net>
It says: The keyword pub identifies the public master signing key, and the keyword sub identifies a public subordinate
key.
I am not understanding, what is subordinate key for? Any help?
Short version: keys are tagged and used for different types of functions. For example, the primary key must be a signing key. Subordinate keys allow for additional functions (ie encryption).
Long Version: From the GNUPG site:In a public-key system, each user has a pair of keys consisting of a private key and a public key.... GnuPG uses a somewhat more sophisticated scheme in which a user has a primary keypair and then zero or more additional subordinate keypairs. The primary and subordinate keypairs are bundled to facilitate key management and the bundle can often be considered simply as one keypair.
...
GnuPG is able to create several different types of keypairs, but a primary key must be capable of making signatures. There are therefore only three options. Option 1 actually creates two keypairs. A DSA keypair is the primary keypair usable only for making signatures. An ElGamal subordinate keypair is also created for encryption
...
it is possible to later add additional subkeys for encryption and signing.

Resources