PS - Practical 2 PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

Practical 2

Aim: To create and verify digital signatures.

Theory : Digital Signature

A digital signature is a mathematical technique used to validate the authenticity and integrity of a message, software, or
digital document.
Key Generation Algorithms: Digital signature is electronic signatures, which assure that the message was sent by a
particular sender. While performing digital transactions authenticity and integrity should be assured, otherwise, the data
can be altered or someone can also act as if he was the sender and expect a reply.
Signing Algorithms: To create a digital signature, signing algorithms like email programs create a one-way hash of the
electronic data which is to be signed. The signing algorithm then encrypts the hash value using the private key (signature
key). This encrypted hash along with other information like the hashing algorithm is the digital signature. This digital
signature is appended with the data and sent to the verifier. The reason for encrypting the hash instead of the entire message
or document is that a hash function converts any arbitrary input into a much shorter fixed-length value. This saves time as
now instead of signing a long message a shorter hash value has to be signed and moreover hashing is much faster than
signing.
Signature Verification Algorithms : Verifier receives Digital Signature along with the data. It then uses Verification
algorithm to process on the digital signature and the public key (verification key) and generates some value. It also applies
the same hash function on the received data and generates a hash value. Then the hash value and the output of the
verification algorithm are compared. If they both are equal, then the digital signature is valid else it is invalid.

Fig: Model of Digital Signature.


1) Each person adopting this scheme has a public-private key pair.
2) Generally, the key pairs used for encryption/decryption and signing/verifying are different. The private key used for
signing is referred to as the signature key and the public key as the verification key.
3) Signer feeds data to the hash function and generates hash of data.
4) Hash value and signature key are then fed to the signature algorithm which produces the digital signature on given
hash. Signature is appended to the data and then both are sent to the verifier.
5) Verifier feeds the digital signature and the verification key into the verification algorithm. The verification algorithm
gives some value as output.
6) Verifier also runs same hash function on received data to generate hash value.
7) For verification, this hash value and output of verification algorithm are compared. Based on the comparison result,
verifier decides whether the digital signature is valid.
8) Since digital signature is created by ‘private’ key of signer and no one else can have this key; the signer cannot
repudiate signing the data in future.

Program:

# DigitalSignatures Practical 2

from cryptography.hazmat.backends import default_backend


from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature

# Generation of Private and Public keys using RSA algorithm


def generate_keys():
private = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public = private.public_key()
return private, public

# Creating a signature
def sign(message, private):
message = bytes(str(message), 'utf-8')
sig = private.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return sig

# Verification of Signature
def verify(message, sig, public) :
message = bytes(str(message), 'utf-8')
try:
retval = public.verify(
sig,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return True
except InvalidSignature:
return False
except:
print("Error Executing public_key.verify")
return False

# Main Function
if __name__ == '__main__' :
print("Generation of Private and Public keys using RSA algorithm")
pr, pu = generate_keys()
print(pr)
print(pu)
message = "This is a secret message"
print("Creating a signature")
sig = sign(message, pr)
print(sig)
print("Verification of Signature")
correct = verify(message, sig, pu)
print(correct)
if correct:
print("Success! Signature Verified")
else:
print("Error! Signature not verified")

print("Trying to sign message with hackers signature")


pr2, pu2 = generate_keys()
sig2 = sign(message, pr2)
correct = verify(message, sig2, pu)
if correct:
print("ERROR! Bad signature checks out!")
else:
print("SUCCESS! Bad signature Detected!")

print("Tampering with messages")


badmess = message + "Q"
correct = verify(badmess, sig, pu)
if correct:
print("ERROR! Tampered message checks out!")
else:
print("SUCCESS! Tampering Detected!")
Output:

Detailed explanation of code:

###Various Imports###

from cryptography.hazmat.backends import default_backend


from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature

### Generation of Private and Public keys using RSA algorithm###

 def generate_keys():
Defines a function generate_keys.
 private = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
➢ private is a variable.
➢ generate_private_key is a member function of class rsa
Parameters:
➢ public_exponent (int) – The public exponent of the new key. Almost everyone should use 65537.
➢ key_size (int) – The length of the modulus in bits. It is strongly recommended to be at in 512, 2048 or 4096.
➢ backend=default_backend() - cryptography was originally designed to support multiple backends, but this design
has been deprecated. You can get the default backend by calling default_backend().
 public = private.public_key() - Public_key() is a member function of class private. Returns an RSA public key object
corresponding to the values of the private key.
 return private, public - Returns private and public variables when the function generate_keys() is called.
### Creating a signature###

⚫ sig = private.sign(message, padding.PSS( mgf=padding.MGF1(hashes.SHA256()),


salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256())
return sig
 def sign(message, private):
Defines a function sign.
Parameters:
➢ message - message is a variable used to store a message string.
➢ private - private is a variable used to store the private key generated through rsa.generate_private_key function.
 message
 message = bytes(str(message), 'utf-8')
Here message is converted to string and then to bytes using inline typecasting.
➢ The str() function converts values to a string form so they can be combined with other strings.
➢ UTF-8 stands for “Unicode Transformation Format - 8 bits.” It can translate any Unicode character to a matching
unique binary string.
➢ The bytes() function returns a bytes object.

⚫ sig = private.sign(message, padding.PSS( mgf=padding.MGF1(hashes.SHA256()),


salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256()) return sig
➢ sig is a variable.
➢ sign is a member function of class private
sign(data, padding, algorithm)
Sign one block of data which can be verified later by others using the public key. Return bytes: Signature.
Parameters:
➢ data (bytes) – The message string to sign.
➢ padding – An instance of AsymmetricPadding.

PSS (Probabilistic Signature Scheme) is a signature scheme defined in RFC 3447. This is the recommended padding algorithm
for RSA signatures. It cannot be used with RSA encryption.

Parameters:
mgf – A mask generation function object. At this time the only supported MGF is MGF1.
salt_length (int) – The length of the salt. It is recommended that this be set to PSS.MAX_LENGTH to get the maximum salt
length available.

algorithm – An instance of HashAlgorithm. SHA-256 is a cryptographic hash function from the SHA-2 family. It produces a
256-bit message digest. A cryptographic hash function takes an arbitrary block of data and calculates a fixed-size bit string
(a digest), such that different data results (with a high probability) in different digests.

### Verification of Signature###

⚫ def verify(message, sig, public) :


Defines a function verify.
Parameters:
➢ message - message is a variable used to store a message string.
➢ sig - is a variable having digital signature generated through private.sign().
➢ public - public is a variable used to store the public key generated through private.public_key() function.

message = bytes(str(message), 'utf-8')


Here message is converted to string and then to bytes using inline typecasting.
➢ The str() function converts values to a string form so they can be combined with other strings.
➢ UTF-8 stands for “Unicode Transformation Format - 8 bits.” It can translate any Unicode character to a matching
unique binary string.
➢ The bytes() function returns a bytes object.

try: retval = public.verify(sig,message,padding.PSS(mgf=padding.MGF1(hashes.SHA256()),


salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256())
return True

Verify one block of data was signed by the private key associated with this public key.
Parameters:
➢ signature (bytes) – The signature to verify. (Same as Sign parameters)
➢ data (bytes) – The message string that was signed. (Same as Sign parameters)
➢ padding – An instance of AsymmetricPadding. (Same as Sign parameters)
➢ algorithm – An instance of HashAlgorithm or Prehashed if the data you want to verify has already been hashed. (Same
as Sign parameters)
➢ Returns:None

except InvalidSignature: return False


except:print("Error Executing public_key.verify") return False
Raises:cryptography.exceptions.InvalidSignature – If the signature does not validate.

### Main Function###

if __name__ == '__main__' :

Before executing code, Python interpreter reads source file and define few special variables/global variables.
If the python interpreter is running that module (the source file) as the main program, it sets the special __name__ variable
to have a value “__main__”.

pr, pu = generate_keys()
➢ Public and private keys are generated using generate_keys() function and values returned are stored in pr and pu
variable.

print(pr)
➢ Value of pr is printed.
print(pu) -
➢ Value of pu is printed.

message = "This is a secret message"


➢ Message to be digitally signed.

sig = sign(message, pr)


print(sig)
➢ The sign() function returns digital signature that is stored in sig variable which is then printed.
correct = verify(message, sig, pu)
➢ The verify() function checks digital signature true is stored in correct variable is signature is verified.
print(correct)
Value of correct variable is printed (True or False)

if correct:
print("Success! Signature Verified")
else:
print("Error! Signature not verified")

➢ If value of correct is true then we print("Success! Signature Verified")


➢ else if the value of correct is false then we print("Error! Signature not verified").

You might also like