Sunday 5 July 2015

Digital Signature


Digital signature is used to confirm the identity of the digital document. By using digital signature, recipient makes sure that the document is authenticated (Sent by correct person). Digital signature gives the receiver reason to believe the claimed sender sent the message.

A typical digital signature process comprises of three algorithms.
1.   Key Generation algorithm: Generates public and private key pair.
2.   Signature algorithm: Takes given message and private key as input and generates signature.
3.   Signature verifying algorithm: Takes received message, public key and signature as input and verifies the message authenticity.

In this post, I am going to explain, how to generate digital signature and verify it. In brief following are the steps.

1.   Sender generates Public and private key pair.
2.   Sender generates digital signature for given data using private key generated in step 1.
3.   Sender sends the message, signature, and public key to receiver.
4.   Receiver receives the data, signature, and public key. Verifies the signature using public key and data sent by sender

Section 1 explains, how to generate digital signature and section 2 explains how to verify digital signature. Final section gives complete program to generate and verify digital signature.

Generate Digital signature
Step 1: To create digital signature, you need a private key, and to verify digital signature, you need a public key. So First we need to generate key pair (public key, private key). A key pair is generated by using the KeyPairGenerator class. Go through following post, to get detailed information on how to generate key pair.

public static KeyPair getKeyPair(String algorithm) throws NoSuchAlgorithmException {
 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
 return keyPairGenerator.generateKeyPair();
}


Above snippet generates KeyPair specific to given algorithm.
Step 2: You can create digital signature using Signature class. Signing data using Signature class is a four-step process.

a. Get Signature instance:
You can get the Signature instance using getInstance method of Signature class. Signature class provides 3 variants of getInstance method to get an instance of Signature class.

getInstance(String algorithm)
Returns a Signature object that implements the specified signature algorithm.

getInstance(String algorithm, Provider provider)
getInstance(String algorithm, String provider)
Returns a Signature object that implements the specified signature algorithm using specified provider.

Following are the digital signature algorithms provided by Java.
Algorithm
Description
NONEwithRSA
The RSA signature algorithm, which does not use a digesting algorithm before performing the RSA operation.
MD2withRSA
MD5withRSA
Uses the MD2/MD5 digest algorithm and RSA to create and verify RSA digital signatures
SHA1withRSA SHA256withRSA
SHA384withRSA
SHA512withRSA
USE SHA1/SHA256/SHA384/SHA512 digest algorithm and RSA to create and verify RSA digital signatures.
NONEwithDSA
Implementation of digital signature algorithm defined in http://csrc.nist.gov/publications/PubsFIPS.html.

SHA1withDSA
Uses the SHA-1 digest algorithm and DSA to create and verify DSA digital signatures
NONEwithECDSA
SHA1withECDSA
SHA256withECDSA
SHA384withECDSA
SHA512withECDSA
(ECDSA)
Uses the SHA digest algorithm and ECDSA to create and verify ECDSA digital signatures.

b.Initialize signature object
signature.initSign(privateKey);

c. Supply the Signature Object the Data to Be Signed
/* Supply the Signature Object the Data to Be Signed  */
FileInputStream fis = new FileInputStream(fileName);
BufferedInputStream bufin = new BufferedInputStream(fis);
byte[] buffer = new byte[1024];
int len;
while ((len = bufin.read(buffer)) >= 0) {
 signature.update(buffer, 0, len);
}
bufin.close();


d. Generate signature
byte[] finalSig = signature.sign();

Step 3: Save the signature and public key in files.
public static void saveKey(Key key, String fileName) {
 byte[] keyBytes = key.getEncoded();
 File keyFile = new File(fileName);
 FileOutputStream fOutStream = null;
 try {
  fOutStream = new FileOutputStream(keyFile);
  fOutStream.write(keyBytes);
 } catch (Exception e) {
  e.printStackTrace();
 } finally {
  if (fOutStream != null) {
   try {
    fOutStream.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }
}

public static void saveSignature(String fileName, byte[] signature)
   throws Exception {
 FileOutputStream sigfos = new FileOutputStream(fileName);
 sigfos.write(signature);
 sigfos.close();
}


Verify digital signature
To verify the signature we need three components.
         a. Data (File contents which are signed)
         b. Signature
         c. Public key corresponding to the private key used to sign the data
        
Step 1: Get the public key instance from the key stored in a file.
public static byte[] readKeyFromFile(String fileName) throws Exception {
 FileInputStream keyfis = new FileInputStream(fileName);
 byte[] key = new byte[keyfis.available()];
 keyfis.read(key);
 keyfis.close();
 return key;
}


Step 2: Read the signature from file.

Step 3: Verify the signature.
a.   Initialize the Signature Object for Verification
Signature verifySignature = Signature.getInstance(sigAlgorithm);

b.   Initialize signature object with the public key.
verifySignature.initVerify(pubKey);

c.    Supply the Signature Object With the Data to be Verified.
public static void feedData(Signature signature, String fileName)
   throws Exception {
 /* Supply the Signature Object the Data to Be Signed */
 FileInputStream fis = new FileInputStream(fileName);
 BufferedInputStream bufin = new BufferedInputStream(fis);
 byte[] buffer = new byte[1024];
 int len;
 while ((len = bufin.read(buffer)) >= 0) {
  signature.update(buffer, 0, len);
 }
 bufin.close();
}

d.   Verify the signature
verifySignature.verify(receivedSignature);

Complete Application

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.PrivateKey;
import java.security.Signature;

public class SignatureUtil {

 /**
  * Generates signature by taking file, PrivateKey and algorithm as input.
  * 
  * @param fileName
  *            : Generate signature for this file.
  * @param privateKey
  * @param algorithm
  * @return
  * @throws Exception
  */
 public static byte[] getSignature(String fileName, PrivateKey privateKey,
   String algorithm) throws Exception {

  /* Get instance of Signature object */
  Signature signature = Signature.getInstance(algorithm);

  /* Initialize Signature object */
  signature.initSign(privateKey);

  /* Feed data */
  feedData(signature, fileName);

  /* Generate signature */
  byte[] finalSig = signature.sign();

  return finalSig;
 }

 /**
  * Save signature to a file
  * 
  * @param fileName
  *            : Signature saved here
  * @param signature
  * @throws Exception
  */
 public static void saveSignature(String fileName, byte[] signature)
   throws Exception {
  FileOutputStream sigfos = new FileOutputStream(fileName);
  sigfos.write(signature);
  sigfos.close();
 }

 /**
  * Read signature from a file and convert it into byte array.
  * 
  * @param fileName
  *            : contains signature information
  * @return signature as byte array
  * @throws Exception
  */
 public static byte[] readSignatureFromFile(String fileName)
   throws Exception {
  return PublicKeyUtil.readKeyFromFile(fileName);
 }

 /**
  * Feed data to Signature instance
  * 
  * @param signature
  * @param fileName
  * @throws Exception
  */
 public static void feedData(Signature signature, String fileName)
   throws Exception {
  /* Supply the Signature Object the Data to Be Signed */
  FileInputStream fis = new FileInputStream(fileName);
  BufferedInputStream bufin = new BufferedInputStream(fis);
  byte[] buffer = new byte[1024];
  int len;
  while ((len = bufin.read(buffer)) >= 0) {
   signature.update(buffer, 0, len);
  }
  bufin.close();
 }

}


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class PublicKeyUtil {

 /**
  * Generates KeyPair specific to given algorithm
  * 
  * @param algorithm
  * @return
  * @throws NoSuchAlgorithmException
  */
 public static KeyPair getKeyPair(String algorithm)
   throws NoSuchAlgorithmException {
  KeyPairGenerator keyPairGenerator = KeyPairGenerator
    .getInstance(algorithm);
  return keyPairGenerator.generateKeyPair();
 }

 /**
  * Return PublicKey from given KeyPair
  * 
  * @param keyPair
  * @return
  */
 public static PublicKey getPublicKey(KeyPair keyPair) {
  return keyPair.getPublic();
 }

 /**
  * Return PrivateKey from given KeyPair
  * 
  * @param keyPair
  * @return
  */
 public static PrivateKey getPrivateKey(KeyPair keyPair) {
  return keyPair.getPrivate();
 }

 /**
  * Convert key to string.
  * 
  * @param key
  * 
  * @return String representation of key
  */
 public static String keyToString(Key key) {
  /* Get key in encoding format */
  byte encoded[] = key.getEncoded();

  /*
   * Encodes the specified byte array into a String using Base64 encoding
   * scheme
   */
  String encodedKey = Base64.getEncoder().encodeToString(encoded);

  return encodedKey;
 }

 /**
  * Save key to a file
  * 
  * @param key
  *            : key to save into file
  * @param fileName
  *            : File name to store
  */
 public static void saveKey(Key key, String fileName) {
  byte[] keyBytes = key.getEncoded();
  File keyFile = new File(fileName);
  FileOutputStream fOutStream = null;
  try {
   fOutStream = new FileOutputStream(keyFile);
   fOutStream.write(keyBytes);
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (fOutStream != null) {
    try {
     fOutStream.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
 }

 /**
  * Returns the key stored in a file.
  * 
  * @param fileName
  * @return
  * @throws Exception
  */
 public static byte[] readKeyFromFile(String fileName) throws Exception {
  FileInputStream keyfis = new FileInputStream(fileName);
  byte[] key = new byte[keyfis.available()];
  keyfis.read(key);
  keyfis.close();
  return key;
 }

 /**
  * Generates public key from encoded byte array.
  * 
  * @param encoded
  * @param algorithm
  * @return
  * @throws Exception
  */
 public static PublicKey convertArrayToPubKey(byte encoded[],
   String algorithm) throws Exception {
  X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encoded);
  KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
  PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);

  return pubKey;
 }

 /**
  * Generates private key from encoded byte array.
  * 
  * @param encoded
  * @param algorithm
  * @return
  * @throws Exception
  */
 public static PrivateKey convertArrayToPriKey(byte encoded[],
   String algorithm) throws Exception {
  PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
  KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
  PrivateKey priKey = keyFactory.generatePrivate(keySpec);
  return priKey;
 }

}


import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;

import com.sample.PublicKeyUtil;
import com.sample.SignatureUtil;

public class TestDigitalSignature {
 public static void main(String args[]) throws Exception {
  String file = "/Users/harikrishna_gurram/emp.txt";

  /* Public key stored in this file */
  String publicKeyFile = "/Users/harikrishna_gurram/publicKey.txt";

  /* Signature of given file stored here */
  String signatureFile = "/Users/harikrishna_gurram/siganture.txt";

  /* Signature algorithm to get Signature instance */
  String sigAlgorithm = "SHA1withDSA";

  /* generate public and private keys */
  KeyPair keyPair = PublicKeyUtil.getKeyPair("DSA");
  PublicKey publicKey = keyPair.getPublic();
  PrivateKey privateKey = keyPair.getPrivate();

  /* Generate signature for given file */
  byte signature[] = SignatureUtil.getSignature(file, privateKey,
    sigAlgorithm);

  /* Save public key */
  PublicKeyUtil.saveKey(publicKey, publicKeyFile);

  /* Save signature */
  SignatureUtil.saveSignature(signatureFile, signature);

  // Verify Signature

  /* Read public key from file */
  byte[] pubKeyBytes = PublicKeyUtil.readKeyFromFile(publicKeyFile);

  /* Convert publick key bytes into PublicKey object */
  PublicKey pubKey = PublicKeyUtil.convertArrayToPubKey(pubKeyBytes,
    "DSA");

  /* Read signature from file */
  byte[] receivedSignature = SignatureUtil
    .readSignatureFromFile(signatureFile);

  /* Verify signature */
  Signature verifySignature = Signature.getInstance(sigAlgorithm);

  /* initialize signature object */
  verifySignature.initVerify(pubKey);

  /* Feed data */
  SignatureUtil.feedData(verifySignature, file);

  /* Verify signature */
  boolean isAuthenticated = verifySignature.verify(receivedSignature);

  if (isAuthenticated) {
   System.out.println("Data is authenticated");
  } else {
   System.out.println("Data is not from expected sender");
  }

 }
}


Referred Articles


No comments:

Post a Comment