Signing Contents Digitally: An Email Implementation

“I was happy when I design my own signature. Also, deciding to put my signature under this job makes me happier. Characteristic of signatures transforms in time. However, it would be remain same in a day. Letters takes the form of yourself, they makes your name official on a paper”. That’s the cover text of Turkish Singer Sila‘s album named as signature.

new-york-one-way-1024x768
One way function is important for signatures


🙋‍♂️ You may consider to enroll my top-rated cryptography course on Udemy

Public Key Cryptography From Scratch

Handwritten signatures proves identity of signer on a marked document. Characteristics of letter formation are unique for every person like finger prints. Also, one’s fine motor skills might affect his handwriting. This leaves clues about signatory’s idendity. So, signatures can be verified by Questioned document examination.

Digital signatures are like handwriten signatures. They demonstrates authenticity of digital content and they can be verified too.

Digital signatures include two different cryptography concepts: cryptographic hash functions and public key cryptography.

Hash functions are resposible for digesting messages. They are one way irreversible functions. A function has to be one-to-one and onto to be irreversible. In contrast, hash functions are many to one. That’s why, they are irreversible in theory. Calculating remainder of a division operation is a basic primitive hash function example. For instance, remainder is 1 when dividend is 7 and divisor is 3. This operation can be calculated easily. However, dividend cannot be calculated for known divisor and remainder. Because, there are infinite numbers whose remainder is 1 when it is divided by 3 (e.g. 7, 10, 13, …). This explains the irreversiblity. Of course, modern hash functions should not have collisions.

Today, MD5 collision resistance can be broken in 30 seconds with a smartphone. Recently, Google engineers announce first practical SHA-1 collision attack. So, the both MD5 and SHA-1 algorithms are considered to be insecure anymore. Now, SHA-256 and SHA-3 are still thought to be safe.

On the other hand, private and public keys complement each other in public key cryptography. A message, encrypted by public key, can be decrypted by only correlated private key. And vice versa. Generating private – public key pair process is fairly easy whereas extracting private key from known public key is quite hard. For example, RSA algorithms is based on finding prime numbers, multiplying them and getting a large number. Today’s computation power handles it easily. However, you need to find its factors to attack. That’s not a easy job.

usb stick on white
Public and private keys complement each other in public key cryptography

Signing a document digitally such as mail or file requires following steps for sender and receiver.

Suppose that Alice sends Bob digitally signed mail.





Alice applies hash algorithm for content and retrieves hash value. Secondly, she uses her private key and encrypts hash value. This encrypted hash value is also called as signature or signed message. Finally, she sends both the content and signature to the Bob.

Bob applies same hash function for received content and he retrieves hash value, too. Then, he decrypts received signature with Alice’s public key (he knows Alice’s public key because it is public). Thus, he can compare decrypted signature and calculated hash. He would verify that content is unchanged if decryptred signature and hash of received content are same. Otherwise, he would be aware of invalidation.

digital-signature-flow
Digital Signature Flow

Now, we would implement these instructions into a real world example. The following code block would be used by sender party. In our example, Alice consumes the following code block for sending mail to Bob.

public static void main(String[] args) throws Exception{
 //crypto variables
 byte[] mySecretKey = {48, -126, 4, -65, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 4, -87, 48, -126, 4, -91, 2, 1, 0, 2, -126, 1, 1, 0, -95, -31, 108, -69, 59, 77, -11, 43, -116, 118, 74, -75, -123, 117, -44, 39, 121, 89, 67, 10, -57, -27, 25, 110, -86, 28, -57, -116, 119, -93, 40, -84, 123, -105, 30, -66, 69, -67, 104, 121, -68, 102, 71, -44, 87, -3, -45, -21, -88, 59, 64, 88, -124, -20, -90, 21, 52, -115, 125, -33, 93, 0, 43, -67, -6, 76, 22, -123, 88, -91, -43, 63, 32, 74, 107, 12, -69, -80, -80, 124, -79, 16, 0, -55, -113, 84, -43, 17, -15, 26, -29, -2, 124, 0, -26, 25, 97, -64, 32, -22, 99, 91, 91, -42, -85, 93, -52, 76, -71, -53, 45, 38, 52, -28, -6, 92, 108, -105, -84, -53, 29, 80, 60, -98, -103, 63, 71, 3, -30, -118, -94, 76, 39, 46, -112, -65, 14, 112, -82, -38, 31, 20, -10, -53, 101, -52, 37, 3, -20, -84, -83, -128, -88, 27, -109, -5, 69, -18, -42, 123, -31, 85, -101, -4, -93, -95, 13, -8, 49, -120, 90, -42, -102, -73, 85, -59, -56, 69, -79, 106, 32, 51, -77, -3, 108, -101, -99, -96, 33, 63, -66, -82, 16, 60, -45, 52, -125, -114, -23, -84, -47, -19, 75, -24, 67, -62, 34, 124, -18, -38, 7, -24, -126, 123, 80, 34, 104, -107, -85, -11, -6, -8, -81, -59, -121, 81, 99, -90, -27, 0, 80, 30, -122, 71, 112, 116, 114, -79, -45, 3, -40, -119, 119, -61, 19, 16, -95, -28, 64, -73, -87, 4, 62, -70, 54, 17, 2, 3, 1, 0, 1, 2, -126, 1, 0, 103, -34, 95, -55, 7, 53, 111, 65, -95, 31, -23, -79, -19, 79, 124, 112, 83, -95, -99, -83, -42, 51, 63, 13, 77, -29, -89, 122, -114, -19, 70, -44, -35, 124, 74, -62, -91, -74, -15, -55, 98, -60, 114, 4, 98, -19, 64, 68, 46, 46, -50, -117, -67, -58, -90, -114, 102, -88, -1, 2, 10, 7, 105, 9, -66, 7, -126, 79, -49, 96, -96, -94, -97, -110, -128, 123, -84, 22, -92, 55, 109, -39, 41, -40, 42, -70, -80, -8, -111, 46, -62, -23, -16, 33, 23, -125, 99, -64, 70, -98, 126, 96, 9, 94, -123, -104, 106, -43, 52, 69, -108, -45, -74, 14, -46, 65, -23, 127, -84, -28, -85, 11, 103, -54, 15, -28, -107, 40, 117, 100, -3, 87, 31, -31, -9, 90, -40, 10, -72, -3, 64, -44, 90, -16, -113, -118, 93, -107, -1, 12, -72, 88, 100, -10, 49, 119, -6, 8, -29, -48, 83, -122, 53, 16, 54, -28, -1, 33, -33, -63, 75, 42, -55, 3, -63, 48, -91, -67, -21, 116, 60, 74, -111, 66, 60, 9, 10, -12, 55, -107, 104, -123, -100, -48, -84, -4, -40, 109, 65, -78, 104, -69, -76, -42, -22, -111, 122, -70, 97, 37, -102, 115, -120, -22, -57, 4, 25, 121, 127, -98, 63, -48, 125, 19, 126, 95, 55, -7, 45, 24, 12, 40, 40, 62, -94, 72, 35, -122, 60, -72, -109, -4, -40, -13, 0, 33, 101, -124, 106, -108, 97, -11, -58, -112, -46, -83, 2, -127, -127, 0, -18, -29, 44, -70, 107, 16, -29, -89, -95, -108, -51, 86, -119, 78, 1, -5, 66, -73, -98, -19, -36, -79, -125, -100, -14, -23, 1, -6, 37, -59, 46, -50, 111, -7, -77, -124, -77, -70, -117, 76, 81, -116, -78, 51, -83, 45, -9, -47, -20, 36, 93, -56, 69, 75, 126, -105, -60, -96, -1, -15, 6, -101, -99, -110, -109, 98, -107, -19, -48, -112, 27, 114, 47, -14, -15, 65, 102, -108, 44, 43, -20, 9, 35, 111, 70, 86, 27, 100, -106, 47, 101, -30, -8, -106, -1, 66, -3, 4, 54, 96, -73, 78, 101, 93, -53, 42, -56, 74, 70, 55, 39, -52, -54, 121, -25, -123, -34, -127, 84, -75, -22, 38, 95, 58, -88, -77, 104, -9, 2, -127, -127, 0, -83, 122, 16, 85, 115, -99, 126, -66, -14, -70, 40, -1, 101, 10, 78, -69, 36, -87, -40, 126, -63, -32, 50, -70, 116, 102, -87, 81, -33, 43, 112, -116, -2, 22, -89, -66, -3, 21, -34, 28, -110, -27, -48, -26, -91, -31, 29, -81, -44, 32, -106, -65, -14, -23, -94, -56, -30, -30, 30, 87, -53, -123, 9, -67, -7, -15, -100, -98, -17, 20, -19, -107, 94, -108, 37, -38, -82, 101, -21, 123, 31, 19, 108, 67, 27, 12, -58, -95, -95, -43, 71, 104, -74, 27, -53, -84, -117, 44, 125, 54, 19, 99, 86, -13, 109, -108, 18, -89, -101, -32, -35, 45, -37, -93, 127, -101, -90, -45, -125, 9, 4, -31, -51, -125, -23, -35, 95, 55, 2, -127, -127, 0, -92, -85, -124, -111, -100, 108, 48, 1, -59, -88, 69, 67, 121, -78, -124, 59, 39, 106, 91, -21, -85, 77, -46, 99, -58, 46, 72, 102, -98, -46, -91, -55, 55, -10, -16, -128, 113, 68, 13, -15, -75, -27, 62, -111, -48, -74, -9, 53, -123, -118, 43, -5, 121, -120, -24, -30, -59, 112, 21, -3, -105, -120, 125, -66, 36, 74, -72, -1, 13, -35, -56, -115, -107, -17, -23, -50, 35, 95, -48, 115, 22, 105, -42, 59, 70, 72, -28, -23, 25, 125, -30, -59, -22, -122, 107, -65, 73, -102, 53, -63, -59, -102, 12, -43, -14, -50, -78, -14, 64, 36, -107, 18, -34, 31, -38, 48, -75, -120, -127, 84, 68, -21, -34, -119, 24, -15, 83, 81, 2, -127, -127, 0, -98, 24, 85, -10, 106, 77, 40, 11, 65, 14, 34, -6, -51, -52, 92, -1, -12, 99, -51, 103, -121, -84, -104, -50, -113, -14, 87, 112, -61, -97, -59, 12, -39, 43, 48, 104, -64, 33, 67, -80, 106, -73, -126, 112, 16, -48, 93, -53, -75, -40, -107, 74, 13, 72, -101, 15, -44, -91, 25, -34, 13, 30, 11, 72, -43, 22, 58, 20, 37, -14, -66, -86, -105, -19, 15, -86, -127, -79, 100, -81, 106, 28, -69, 87, 84, -71, -119, -12, 23, -106, 85, 99, -70, 67, 14, -107, 10, -88, -38, -37, -125, 67, -49, 36, 61, -62, -22, 85, 81, -101, -42, 54, 74, -86, 112, 75, 27, -8, 58, -68, -25, 116, 25, 101, -108, -90, 40, 117, 2, -127, -127, 0, -125, -46, 114, 101, 122, -57, 48, -39, 96, 24, -28, -13, -42, -113, 29, -75, -57, 43, -76, -91, 107, 105, 107, 32, -84, -101, 112, -52, -97, -111, -23, 87, -40, 102, 73, 40, -86, 46, -114, -76, -108, 120, -47, -4, -95, 16, 109, 63, -103, -56, -42, -29, -92, 13, -90, -32, -48, 4, -15, 91, -124, -45, -124, 96, 76, -84, 51, -61, -11, 100, -33, 65, -121, -95, -123, -43, -41, -11, 34, -120, 33, -79, -37, 55, 81, -32, -103, -42, 3, -40, 66, -10, 49, 4, 23, 107, 61, 20, -106, -53, -50, 122, 17, 105, -102, 78, 35, 55, -88, 21, -53, 125, -34, 90, -100, 91, 71, 84, 43, 21, 116, 65, 32, -65, -71, 49, 11, -45};
 byte[] myPublicKey = {48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -95, -31, 108, -69, 59, 77, -11, 43, -116, 118, 74, -75, -123, 117, -44, 39, 121, 89, 67, 10, -57, -27, 25, 110, -86, 28, -57, -116, 119, -93, 40, -84, 123, -105, 30, -66, 69, -67, 104, 121, -68, 102, 71, -44, 87, -3, -45, -21, -88, 59, 64, 88, -124, -20, -90, 21, 52, -115, 125, -33, 93, 0, 43, -67, -6, 76, 22, -123, 88, -91, -43, 63, 32, 74, 107, 12, -69, -80, -80, 124, -79, 16, 0, -55, -113, 84, -43, 17, -15, 26, -29, -2, 124, 0, -26, 25, 97, -64, 32, -22, 99, 91, 91, -42, -85, 93, -52, 76, -71, -53, 45, 38, 52, -28, -6, 92, 108, -105, -84, -53, 29, 80, 60, -98, -103, 63, 71, 3, -30, -118, -94, 76, 39, 46, -112, -65, 14, 112, -82, -38, 31, 20, -10, -53, 101, -52, 37, 3, -20, -84, -83, -128, -88, 27, -109, -5, 69, -18, -42, 123, -31, 85, -101, -4, -93, -95, 13, -8, 49, -120, 90, -42, -102, -73, 85, -59, -56, 69, -79, 106, 32, 51, -77, -3, 108, -101, -99, -96, 33, 63, -66, -82, 16, 60, -45, 52, -125, -114, -23, -84, -47, -19, 75, -24, 67, -62, 34, 124, -18, -38, 7, -24, -126, 123, 80, 34, 104, -107, -85, -11, -6, -8, -81, -59, -121, 81, 99, -90, -27, 0, 80, 30, -122, 71, 112, 116, 114, -79, -45, 3, -40, -119, 119, -61, 19, 16, -95, -28, 64, -73, -87, 4, 62, -70, 54, 17, 2, 3, 1, 0, 1};

 //mail variables
 String content = "to be or not to be, that's the question";

 String hashAlgorithm = "SHA-512";
 String publicKeyAlgorithm = "RSA";

 //digest of content
 MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
 md.update(content.getBytes());
 byte[] hashValue = md.digest();

 //content digest encrypted with private key
 Cipher publicKeyEncryption = Cipher.getInstance(publicKeyAlgorithm);
 publicKeyEncryption.init(Cipher.ENCRYPT_MODE, KeyFactory.getInstance(publicKeyAlgorithm).generatePrivate(new PKCS8EncodedKeySpec(mySecretKey)));
 byte[] encryptedHash = publicKeyEncryption.doFinal(hashValue);
 String encryptedHashString = new String(Base64.getEncoder().encode(encryptedHash));

 //Alice sends Bob content and encryptedHashString
 sendMail(username, "signature sample", content, "signature", encryptedHashString);
}

public static void sendMail(String recipient, String subject, String content, String attachmentName, String attachment) throws Exception{

 Properties props = new Properties();
 props.put("mail.smtp.host", "smtp.gmail.com");
 props.put("mail.smtp.socketFactory.port", "465");
 props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
 props.put("mail.smtp.auth", "true");
 props.put("mail.smtp.port", "465");

 Session session = Session.getDefaultInstance(props,
  new javax.mail.Authenticator() {
   protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
    return new javax.mail.PasswordAuthentication(username, password);
   }
  });

 MimeMessage message = new MimeMessage(session);
 message.setFrom(new InternetAddress(username));
 message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient));
 message.setSubject(subject);

 //attachment
 Multipart multipart = new MimeMultipart();

 MimeBodyPart messageBodyPart = new MimeBodyPart();
 messageBodyPart.setText(content);
 multipart.addBodyPart(messageBodyPart);

 MimeBodyPart mailAttachment = new MimeBodyPart();
 mailAttachment.setText(attachment);
 mailAttachment.setFileName(attachmentName+".txt");
 multipart.addBodyPart(mailAttachment);

 message.setContent(multipart);

 Transport.send(message);

 System.out.println("mail delivered successfully...");
}
digital-signature-sample
Delivered mail to Bob including signature
digital-signature-content
Attached signature content

Verification

After then, Bob might run the following code to verify attached signature.

public static void main(String[] args) throws Exception {
 //crypto variables
 String hashAlgorithm = "SHA-512";
 String publicKeyAlgorithm = "RSA";
 byte[] alicePublicKey = {48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -95, -31, 108, -69, 59, 77, -11, 43, -116, 118, 74, -75, -123, 117, -44, 39, 121, 89, 67, 10, -57, -27, 25, 110, -86, 28, -57, -116, 119, -93, 40, -84, 123, -105, 30, -66, 69, -67, 104, 121, -68, 102, 71, -44, 87, -3, -45, -21, -88, 59, 64, 88, -124, -20, -90, 21, 52, -115, 125, -33, 93, 0, 43, -67, -6, 76, 22, -123, 88, -91, -43, 63, 32, 74, 107, 12, -69, -80, -80, 124, -79, 16, 0, -55, -113, 84, -43, 17, -15, 26, -29, -2, 124, 0, -26, 25, 97, -64, 32, -22, 99, 91, 91, -42, -85, 93, -52, 76, -71, -53, 45, 38, 52, -28, -6, 92, 108, -105, -84, -53, 29, 80, 60, -98, -103, 63, 71, 3, -30, -118, -94, 76, 39, 46, -112, -65, 14, 112, -82, -38, 31, 20, -10, -53, 101, -52, 37, 3, -20, -84, -83, -128, -88, 27, -109, -5, 69, -18, -42, 123, -31, 85, -101, -4, -93, -95, 13, -8, 49, -120, 90, -42, -102, -73, 85, -59, -56, 69, -79, 106, 32, 51, -77, -3, 108, -101, -99, -96, 33, 63, -66, -82, 16, 60, -45, 52, -125, -114, -23, -84, -47, -19, 75, -24, 67, -62, 34, 124, -18, -38, 7, -24, -126, 123, 80, 34, 104, -107, -85, -11, -6, -8, -81, -59, -121, 81, 99, -90, -27, 0, 80, 30, -122, 71, 112, 116, 114, -79, -45, 3, -40, -119, 119, -61, 19, 16, -95, -28, 64, -73, -87, 4, 62, -70, 54, 17, 2, 3, 1, 0, 1};

 Message[] messages = retrieveMails();

 for (int i = 0; i < messages.length; i++) {
  Message message = messages[i];

  Multipart multipart = (Multipart) message.getContent();
  BodyPart bodyPart = multipart.getBodyPart(0);

  String content = bodyPart.getContent().toString();
  String signature = ((Multipart) message.getContent()).getBodyPart(1).getContent().toString();

  //received message hash. this will be compared with received signature.
  MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
  md.update(content.getBytes());
  byte[] expectedHash = md.digest();
  String expectedHashString = new String(Base64.getEncoder().encode(expectedHash));

  try{
  	//decrypt signature with Alice public key
  	byte[] decodedSignature = Base64.getDecoder().decode(signature.getBytes());
  	Cipher publicKeyEncryption = Cipher.getInstance(publicKeyAlgorithm);
  	publicKeyEncryption.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance(publicKeyAlgorithm).generatePublic(new X509EncodedKeySpec(alicePublicKey)));
  	byte[] decryptedHash = publicKeyEncryption.doFinal(decodedSignature);
  	String decryptredHashString = new String(Base64.getEncoder().encode(decryptedHash));

  	if(decryptredHashString.equals(expectedHashString)){
  		System.out.println("Signature is valid...");
  	}
  	else{
  		throw new Exception();
  	}
  }
  catch(Exception ex){
  	//invalid signature content might cause to throw exception while decrypting
  	System.out.println("Invalid signature detected!...");
  }
 }
}

public static Message[] retrieveMails() throws Exception{
	Properties properties = new Properties();
	properties.put("mail.store.protocol", "imaps");
    Session emailSession = Session.getDefaultInstance(properties);

    Store store = emailSession.getStore("imaps");
    store.connect("imap.gmail.com", username, password);

    Folder emailFolder = store.getFolder("INBOX");
    emailFolder.open(Folder.READ_ONLY);

    Message[] messages = emailFolder.getMessages();

    emailFolder.close(false);
    store.close();

    return messages;
}

Now, Bob can verify the attached signature. Thus, he ensures that the received mail really came from Alice.

signature-validation
Attached signature is equal to expected one

So, we’ve mentioned how to implement digital signature concept in practice. Also, we’ve implement it with out of the box JCE features. Digital signatures include two main cryptographic concepts: hash functions and public key cryptography algorithms. They provide to verify delivered content is unchanged or unmodified. Finally, source code of the project is shared on GitHub, you might want to clone it.

Hopefully, you would put your signature under enjoyable things.


Like this blog? Support me on Patreon

Buy me a coffee