x509certificatedigital-signaturebouncycastlepkikey-management

Verifying Signed Hash using Digital Signature


I am implementing a interface that takes 3 things as input

  1. X509 certificate

  2. A signedHash that was signed with that certificate’s private key

  3. The initial hash

It must perform the following actions:

  1. Confirm that this hash is signed using the provided certificate.
  2. Decrypt the signed hash using the public key of the certificate provided as input, and check that it matches the provided hash.

I have implemented the following for verifying the Digital signature :-

 public static boolean verifySignedHash(String X509Certificate, String hash,
            String signedHash) {

        boolean isVerified = false;

        ByteArrayInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        ByteArrayOutputStream byo = null;


        try {

            outputStream = new ByteArrayOutputStream();

            byte[] data = Base64.decodeBase64(X509Certificate);

            /* writing decoded X509 certificate to the ByteArrayOutputStream */

            outputStream.write(data);

            byte[] inp = outputStream.toByteArray();
            inputStream = new ByteArrayInputStream(inp);

            /* Getting the certificate from the Input */

            CertificateFactory cf = CertificateFactory.getInstance("X.509");

            X509Certificate certs = (X509Certificate) cf
                    .generateCertificate(inputStream);

            /* import encoded public key */

            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(certs
                    .getPublicKey().getEncoded());

            /* Instantiating KeyFactory for accesing the Keys as Object */

            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            /*
             * using the KeyFactory object to generate a PublicKey from the key
             * specification.
             */

            publickKey = keyFactory.generatePublic(pubKeySpec);

            byte[] signHash = Base64.decodeBase64(signedHash);

            byo = new ByteArrayOutputStream();

            byo.write(signHash);

            byte[] signChar = byo.toByteArray();

            ByteArrayInputStream byi = new ByteArrayInputStream(signChar);

            /* Next, input the signature bytes from the file specified */

            byte[] sigToVerify = new byte[byi.available()];
            byi.read(sigToVerify);
            byi.close();

            /* Instantiating Signature */
            Signature signature = Signature.getInstance(certs.getSigAlgName());

            /* Initializing the Public Key in Signature */

            signature.initVerify(publickKey);

            /* Supply the Signature Object With the Data to be Verified */

            BufferedInputStream bufin = new BufferedInputStream(byi);

            byte[] buffer = new byte[1024];
            int len;
            while (bufin.available() != 0) {
                len = bufin.read(buffer);
                signature.update(buffer, 0, len);
            };

            bufin.close();

            /* Verify the Signature */

            isVerified = signature.verify(sigToVerify);

        } catch (Exception e) {
            System.err.println("Caught exception " + e.toString());
        }

        return isVerified;
    }

I am getting the Result as False

**Am i Missing something or is this piece of code correct ?**

I really appreciate your help. Thanks a lot.


Solution

  • I've copied original code and placed my comments in the code

    // X509Certificate is class name, it cannot be variable name
    public static boolean verifySignedHash(String X509Certificate, String hash,
            String signedHash) {
    
        // Java is not C89 you don't have to declare variables in the beginning
        // of function, this reduces readability of the code and allows misuse
        boolean isVerified = false;
    
        ByteArrayInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        ByteArrayOutputStream byo = null;
    
        try {
    
            outputStream = new ByteArrayOutputStream();
    
            byte[] data = Base64.decodeBase64(X509Certificate);
    
            /* writing decoded X509 certificate to the ByteArrayOutputStream */
    
            outputStream.write(data);
    
            byte[] inp = outputStream.toByteArray();
    
            // at this point inp is the same array as data, this makes no sence
            // and reduce performance
    
            inputStream = new ByteArrayInputStream(inp);
    
            /* Getting the certificate from the Input */
    
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
    
            X509Certificate certs = (X509Certificate) cf
                    .generateCertificate(inputStream);
    
            /* import encoded public key */
            // certs.getPublicKey() returns publicKey immediately, why to do
            // all these conversions?
    
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(certs
                    .getPublicKey().getEncoded());
    
            /* Instantiating KeyFactory for accesing the Keys as Object */
    
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
            /*
             * using the KeyFactory object to generate a PublicKey from the key
             * specification.
             */
    
            publickKey = keyFactory.generatePublic(pubKeySpec);
    
            byte[] signHash = Base64.decodeBase64(signedHash);
    
            byo = new ByteArrayOutputStream();
    
            byo.write(signHash);
    
            byte[] signChar = byo.toByteArray();
    
            // Again, signChar is the same as signHash, why would you do that?
    
            ByteArrayInputStream byi = new ByteArrayInputStream(signChar);
    
            /* Next, input the signature bytes from the file specified */
    
            byte[] sigToVerify = new byte[byi.available()];
            byi.read(sigToVerify);
            byi.close();
    
            // And now sigToVerify is the same as signChar and signHash.
    
            /* Instantiating Signature */
            Signature signature = Signature.getInstance(certs.getSigAlgName());
    
            /* Initializing the Public Key in Signature */
    
            signature.initVerify(publickKey);
    
            /* Supply the Signature Object With the Data to be Verified */
    
            // byi is already closed, you will get java.io.IOException: Stream closed
            BufferedInputStream bufin = new BufferedInputStream(byi);
    
            byte[] buffer = new byte[1024];
            int len;
            while (bufin.available() != 0) {
                len = bufin.read(buffer);
                signature.update(buffer, 0, len);
                // Any way bufin contained signature, while you need feed
                // plaintext to signature.update()
                // I could assume unused parameter hash has something to do
                // with plaintext
            };
    
            bufin.close();
    
            /* Verify the Signature */
    
            isVerified = signature.verify(sigToVerify);
    
        } catch (Exception e) {
            System.err.println("Caught exception " + e.toString());
        }
    
        return isVerified;
    }