Lesson 7: Digital Signatures
Digital signatures are digital proof of identity that create transitive trust in our digital world.
In our modern world where day-to-day transactions are online and digital documents are used instead of paper documents, digital signatures play a pivotal role in securing electronic communication and transactions. While a message digest confirms the integrity of a message, a digital signature cryptographically binds the identity of the signer to a message, allowing us to authenticate both the origin and integrity of the message.
A digital signature is used to assure:
Authenticity —The receiving party can confirm the identity that signed the message. We say the signature provides the proof of origin.
Integrity — The receiving party can verify the message has not been changed since it was digitally signed. If someone alters the message in any way, it voids the signature.
Non-repudiation — All parties can confirm the message is actually signed by a given entity owning the signing key, assuring that the signer cannot deny sending the message.
Message authentication codes vs. digital signatures
Message authentication codes (MACs) allow two parties to authenticate messages using a secret key. They do not provide the property of non-repudiation. On the other hand, digital signatures allow us to authenticate messages asymmetrically. The receiving party can validate the authenticity of our message without us giving away our signing key.
Uses of digital signatures
Document signing: Electronic documents can be digitally signed to provide assurances about the validity and authenticity of the documents, making digital signatures legally recognised.
Code signing: Software developers use digital signatures to sign their code or software, ensuring the software has not been altered or compromised before installation or execution.
Financial transactions: Digital signatures significantly enhance the security of financial transactions such as online banking and trading by providing identity verification. Two parties can use digital signatures to verify the legitimacy of a transaction, making it harder for malicious actors to engage in fraudulent schemes.
Self-contained tokens: Self-contained tokens like JSON Web Tokens (JWTs) are signed to protect against manipulation. The signature allows the recipient to verify the token is legitimate and ensure the integrity of the claims contained within it.
Public-key infrastructures (PKIs): A PKI is a system that governs the issuance of digital certificates to individuals and organisations with the help of certificate authorities (CAs). Digital signatures are used to verify a digital certificate is issued by a trusted CA.
How digital signatures work
Digital signatures are digital proof of identity. A signature is created using a signer’s private key, which is always securely kept by the signer. Unlike MACs, verifying a signature is valid does not require knowledge of the private key. Digital signatures use asymmetric cryptography to allow anyone to verify our signatures using our public key, which proves that we own the private key and only we can generate a valid signature. This is called zero-knowledge proof. An impersonator cannot forge our signature by just knowing our public key.
To digitally sign a message, the sender:
Hashes the message, producing a message digest, and
Encrypts the message digest with the private key, hence producing the digital signature.
The sender then sends the digital signature with the message. To verify the digital signature, the receiver:
Decrypts the digital signature (i.e. the encrypted message digest) using the sender’s public key,
Recomputes the hash (message digest) from the message received, and lastly,
Compares the two digests and verifies that they are the same.
Because only the sender knows the private key, this provides strong evidence that the sender is the originator of the message. When signing a message, hashing is used to convert the variable-length message into a fixed-length message digest. As we have learnt in the previous lesson, asymmetric encryption is limited by the size of data it can encrypt. Therefore, it is more efficient and secure to encrypt a message digest, which as a result, produces a fixed-length signature.
Here is the distinction when an asymmetric key pair is used in asymmetric encryption and digital signatures.
In asymmetric encryption:
A public key is used to encrypt a message and its corresponding private key is used to decrypt the ciphertext.When signing a message:
A private key is used to encrypt a message digest and its corresponding public key is used to decrypt the digital signature.
This is the interesting part about asymmetric encryption. When one key is used to encrypt a message, only the other key can be used to decrypt the ciphertext. The private key used to sign a message digest is sometimes called a signing key, and its public key called a verification key.
The most commonly used digital signature algorithm is RSA signature with SHA-256. Different frameworks use different notations to represent this algorithm, such as RSA-SHA256 or RS256. Just like we should use RSA-OAEP for RSA encryption, the best practice is to use RSA-PSS for signatures, which encodes a message using the PSS encoding algorithm. However, many real-world applications still use RSA PKCS#1 v1.5 signatures. When integrating with a third-party application, make sure you know the exact algorithm that is used.
Let’s see how to use RSA-PSS in coding.
Python (using pyca/cryptography):
from base64 import b64encode
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
# Generate a RSA private key
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
# Get the RSA public key from the private key
public_key = private_key.public_key()
# Sign using RSA-PSS algorithm
message = b'This is a message.'
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print('The signature is: ' + b64encode(signature).decode('utf-8'))
# Verify the signature
try:
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print('The signature is valid')
except InvalidSignature:
print('The signature is invalid')
This will print an output that looks like the following:
The signature is: TpvSlZKI3dheepmDfceyA2jTtQZj/50hgm+GtShKV5P4gINehlqgRBbs9dADP+bQ32NvO3fuMns46vqM2rzg989ku4REKS/0LoL9KinT7ElKe6wjwRNurJ3xwcY/3BgNvqT1kIQrW8T7LPY08FRb6b76zMLAlil82hTnbfc36qdzvcYTGSXjbtK3SuzH0UTsiNsCLL65uXOQG6ClK27+qi3Ay6VPeNKh7vbVtoavpLHoO55DVrHWL2H/U+D94eeiFghZkxEtKJcmLpkXfAiV6RxYq9ZgYSUX25MK+yDWXqCz9KaI5MU/829Nxs0RMO5KOnCBIF2fQftBBVN1hMz9Dw==
The signature is valid
Why digital certificates are required
Publishing your public key online does not make anyone trust it automatically. A public key does not state the owner. Proof is needed to show that you own the public key. Also, key exchanges between two participants over an insecure connection are subject to a man-in-the-middle (MITM) attack. Just because someone we know on the Internet send us their public key does not mean we do not need to verify its source. A malicious third party can intercept the message and disguise themselves as the sender with a fake public key.
In the real world, our public key is only as authoritative as an authority certifies it with a certificate. For the same reason we trust driving licences issued by our government, we can also trust digital certificates signed and issued by a trusted CA. A CA signs our public key and issues us a digital certificate containing a signature to our public key and other information about us. Any user who trusts the CA then also trusts the digital certificate it signed and our public key on the certificate. This is how PKIs secure public keys and help two parties not knowing each other in advance verify their true identity by digital certificates.
A digital certificate confirms the identity of a person or organisation online, and helps the receiver know they can trust a public key and a digital signature associated with it. A digital certificate is verified using the CA public key. The receiver first checks the certificate is issued to the sender by a trusted CA, confirming the sender owns the certificate. They then check the digital signature of the signed document matches the public key on the certificate, ensuring the sender signed the document.
The most common format of digital certificates is the X.509 certificate. It includes the signer’s public key, registered domain name, CA signature of the certificate, and other identifying information about both the signer and the CA who issued the certificate.
Conclusion
A digital signature is a cryptographic technique that authenticates the origin and integrity of digital messages or documents. Digital signatures can prove the identity of a sender and ensure that the message is authentic and retains its integrity. One crucial property of digital signatures is non-repudiation. Our signing key is the secret only we know. Anyone that has access to our public key can verify our digital signature and confirm that the message came from us.
Digital signatures are commonly used in applications for authentication purposes and protecting the integrity of transactions. For example, a client signs its requests and a server also signs its responses for mutual authentication. This technique adds an extra layer of security to the API, making it harder for attackers to forge requests and responses.
It is common to see people often mistake public keys for signing keys for their lack of understanding during the process of exchanging public keys or certificates. Always remember that signing requires something only we own, which is our private key. It is crucial to keep the private key closely guarded. If the private key is compromised, an attacker could impersonate the sender and generate valid signatures.