Friday 3 November 2023

Java Utility Class for Digital Signature with RSA and DSA Support

In this post, I am going to develop a Java utility class that provides the following functionalities.

a.   Generate key pairs for RSA, DSA algorithms.

b.   Generate a digital signature for a string message using both RSA and DSA.

c.    Validate the digital signature for a string message using the corresponding public key.

d.   Support parameterization of the algorithm (e.g., "SHA256withRSA", "SHA256withDSA"), key size, and key pair generation algorithms (RSA and DSA).

 

Let’s define enums to represent key pair algorithms and their key sizes.


KeyPairAlgorithm.java

package com.sample.app.security;

public enum KeyPairAlgorithm {
    RSA_1024("RSA", 1024), RSA_2048("RSA", 2048), RSA_3072("RSA", 3072), RSA_4096("RSA", 4096), DSA_1024("DSA", 1024),
    DSA_2048("DSA", 2048), DSA_3072("DSA", 3072), ECDSA_256("ECDSA", 256), ECDSA_384("ECDSA", 384),
    ECDSA_521("ECDSA", 521);

    private final String algorithm;
    private final Integer keyLength;

    private KeyPairAlgorithm(String algorithm, int keyLength) {
        this.algorithm = algorithm;
        this.keyLength = keyLength;
    }

    public String getAlgorithm() {
        return algorithm;
    }

    public Integer getKeyLength() {
        return keyLength;
    }

}

 

Let’s define SignatureAlgorithm that define different algorithms that support digital signature generation.

 

SignatureAlgorithm.java

 

package com.sample.app.security;

public enum SignatureAlgorithm {
    SHA1withRSA("SHA1withRSA"), SHA256withRSA("SHA256withRSA"), SHA384withRSA("SHA384withRSA"),
    SHA512withRSA("SHA512withRSA"), SHA1withDSA("SHA1withDSA"), SHA256withDSA("SHA256withDSA"),
    SHA384withDSA("SHA384withDSA"), SHA512withDSA("SHA512withDSA"), SHA1withECDSA("SHA1withECDSA"),
    SHA256withECDSA("SHA256withECDSA"), SHA384withECDSA("SHA384withECDSA"), SHA512withECDSA("SHA512withECDSA");

    private final String algorithm;

    private SignatureAlgorithm(String algorithm) {
        this.algorithm = algorithm;
    }

    public String getAlgorithm() {
        return algorithm;
    }
}

 

Let’s define an utility class to generate keypair, digital signature, validate digital signature etc.,

 

SignatureUtil.java

package com.sample.app.security;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SignatureUtil {

    public static KeyPair generateKeyPair(String algorithm, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        keyPairGenerator.initialize(keySize);
        return keyPairGenerator.generateKeyPair();
    }

    public static String privateKeyToString(PrivateKey privateKey) {
        byte[] privateKeyBytes = privateKey.getEncoded();
        return Base64.getEncoder().encodeToString(privateKeyBytes);
    }

    public static String publicKeyToString(PublicKey publicKey) {
        byte[] publicKeyBytes = publicKey.getEncoded();
        return Base64.getEncoder().encodeToString(publicKeyBytes);
    }

    public static PrivateKey privateKeyFromString(String privateKeyStr, String algorithm) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        return keyFactory.generatePrivate(keySpec);
    }

    public static PublicKey publicKeyFromString(String publicKeyStr, String algorithm) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        return keyFactory.generatePublic(keySpec);
    }

    public static String signMessage(String message, PrivateKey privateKey, String algorithm) throws Exception {
        Signature signature = Signature.getInstance(algorithm);
        signature.initSign(privateKey);
        signature.update(message.getBytes("UTF-8"));
        byte[] signatureBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signatureBytes);
    }

    public static boolean verifySignature(String message, String signatureStr, PublicKey publicKey, String algorithm) throws Exception {
        Signature signature = Signature.getInstance(algorithm);
        signature.initVerify(publicKey);
        signature.update(message.getBytes("UTF-8"));
        byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
        return signature.verify(signatureBytes);
    }

   
}

Let’s define the demo class to validate the same.

 

SignatureUtilDemo.java

package com.sample.app;

import java.security.KeyPair;

import com.sample.app.security.KeyPairAlgorithm;
import com.sample.app.security.SignatureAlgorithm;
import com.sample.app.security.SignatureUtil;

public class SignatureUtilDemo {
    public static void main(String[] args) throws Exception {
        KeyPairAlgorithm keyPairAlgorithm = KeyPairAlgorithm.RSA_4096;
        String algorithmToGenerateKeyPair = keyPairAlgorithm.getAlgorithm();
        int keySize = keyPairAlgorithm.getKeyLength();

        String signatureAlgorithm = SignatureAlgorithm.SHA512withRSA.getAlgorithm();

        KeyPair keyPair = SignatureUtil.generateKeyPair(algorithmToGenerateKeyPair, keySize);

        String privateKeyStr = SignatureUtil.privateKeyToString(keyPair.getPrivate());
        String publicKeyStr = SignatureUtil.publicKeyToString(keyPair.getPublic());

        System.out.println("Private Key: " + privateKeyStr);
        System.out.println("\nPublic Key: " + publicKeyStr);

        String message = "Hello, world!";
        String signature = SignatureUtil.signMessage(message, keyPair.getPrivate(), signatureAlgorithm);

        System.out.println("\nMessage: " + message);
        System.out.println("\nSignature: " + signature);

        boolean signatureValid = SignatureUtil.verifySignature(message, signature, keyPair.getPublic(),
                signatureAlgorithm);

        System.out.println("\nSignature Valid: " + signatureValid);
    }
}



 

Previous                                                 Next                                                 Home

No comments:

Post a Comment