Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / Security / August 2007

Tip: Looking for answers? Try searching our database.

Unable to verify signature with Sun JSSE

Thread view: 
Michael@Welch.net - 09 Aug 2007 16:58 GMT
Situation: In a nutshell I cannot get signature verification to work,
and I'm not sure what I'm doing wrong.

Background: I'm trying to recreate functionality in Java that
currently exists in C/C++, using the OpenSSL vxcrypto library. I've
written code to demonstrate the problem. The code below demonstrates
what works in C, and what doesn't work in Java. In both cases, I read
all of the input data (the public key, the SHA-1 hashed data that was
previously signed by my private key, and the signature) as raw bytes
from binary files. As you can see in the code, the Java program is
reading this data from the exact same files as is the C program.

Signature: 128 bytes
Signed data: SHA-1 hashed to produce 20 bytes, with those 20 bytes
being what was signed by my private key.
Public key: 162 bytes

This works fine:
------------------------------------------------------------------

/*
* OpenSSLDemo.cpp
*/

#include "stdafx.h"
#include <stdio.h>

#include <openssl/rsa.h>
#include <openssl/x509.h>

#define HASHED_DATA_SIZE   20
#define SIGNATURE_SIZE     128
#define PUBLIC_KEY_SIZE    162

/*
* Proper error-checking and buffer guarding omitted for simplicity.
*/
int main(int argc, char * argv[])
{
   /*
    * Allocate arrays to store the raw input bytes.
    */
   unsigned char hashed_data[HASHED_DATA_SIZE];
   unsigned char signature[SIGNATURE_SIZE];
   unsigned char public_key[PUBLIC_KEY_SIZE];

   /*
    * Read the binary files into their respective arrays.
    */
   FILE * fp = fopen("c:\\hasheddata.bin", "rb");

   if (HASHED_DATA_SIZE != fread(hashed_data, 1, HASHED_DATA_SIZE +
1, fp))
   {
       printf("Read of hashed data was NOT exactly 20 bytes.\n");
   }

   fclose(fp);

   fp = fopen("c:\\signature.bin", "rb");

   if (SIGNATURE_SIZE != fread(signature, 1, SIGNATURE_SIZE + 1, fp))
   {
       printf("Read of hashed data was NOT exactly 128 bytes.\n");
   }

   fclose(fp);

   fp = fopen("c:\\pubkey.bin", "rb");

   if (PUBLIC_KEY_SIZE != fread(public_key, 1, PUBLIC_KEY_SIZE + 1,
fp))
   {
       printf("Read of hashed data was NOT exactly 162 bytes.\n");
   }

   fclose(fp);

   /*
    * Create OpenSSL RSA public key structure from my raw public key
    * bytes.
    */
   RSA * rsa_struct_ptr = NULL;
   const unsigned char * public_key_pointer = public_key;

   if (NULL == d2i_RSA_PUBKEY(&rsa_struct_ptr, &public_key_pointer,
                              PUBLIC_KEY_SIZE))
   {
       printf("Creation of RSA public key structure FAILED!\n");
       return -1;
   }

   /*
    * Verify the signature against the hashed data that was
previously
    * signed with my private key.
    */
   if (!RSA_verify(NID_sha1, hashed_data, HASHED_DATA_SIZE,
                   signature, SIGNATURE_SIZE, rsa_struct_ptr))
   {
       printf("Signature verification FAILED!\n");
       return 1;
   }

   printf("Signature successfully verified.\n");
   return 0;
}

---------------------------------------------------------------------

I have translated the above program to Java as follows:
--------------------------------------------------------------------

import java.io.File;
import java.io.RandomAccessFile;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;

public class JSSEDemo
{
   public JSSEDemo()
   {
   }

   public static final void main(String [] args)
   {
       //
       // Thorough exception handling omitted for simplicity.
       //
       try
       {
           //
           // Read raw data from files into respective byte arrays
           //
           byte[] hashedData =
               new byte[(int) (new File("c:\
\hasheddata.bin").length())];
           new RandomAccessFile("c:\\hasheddata.bin",

"rw").readFully(hashedData);

           assert(hashedData.length == 20);

           byte[] signature =
               new byte[(int) (new File("c:\
\signature.bin").length())];
           new RandomAccessFile("c:\\signature.bin",

"rw").readFully(signature);

           assert(signature.length == 128);

           byte[] publicKey =
               new byte[(int) (new File("c:\\pubkey.bin").length())];
           new RandomAccessFile("c:\\pubkey.bin",
"rw").readFully(publicKey);

           assert(publicKey.length == 162);

           //
           // Create java.security.PublicKey object from the
publicKey
           // byte array.
           //
           X509EncodedKeySpec x509PublicKeySpec =
                                          new
X509EncodedKeySpec(publicKey);
           KeyFactory keyFactory = KeyFactory.getInstance("RSA");
           PublicKey pubKey =
keyFactory.generatePublic(x509PublicKeySpec);

           assert(pubKey.getEncoded().length == 162);

           //
           // Create java.security.Signature object and verify
signature.
           //
           Signature sig = Signature.getInstance("SHA1withRSA");
           sig.initVerify(pubKey);
           sig.update(hashedData);

           if (sig.verify(signature))
           {
               System.out.println("Signature successfully
verified.");
           }
           else
           {
               System.out.println("Signature verification FAILED!");
           }
       }
       catch(Exception exception)
       {
           System.out.println(exception.toString());
       }
   }
}

--------------------------------------------------------------

And this Java one does not work, as the call to sig.verify(signature)
always returns false. Can anyone tell what I'm doing wrong?

- Michael
Michael@Welch.net - 17 Aug 2007 02:06 GMT
On Aug 9, 8:58 am, Mich...@Welch.net wrote:
> Situation: In a nutshell I cannot get signature verification to work,
> and I'm not sure what I'm doing wrong.
[quoted text clipped - 204 lines]
>
> - Michael

OK, I thought I'd post the answer in case anyone (most likely, OpenSSL
users) ever has a similar problem and comes across this message while
searching the web or something:

Note that in the code samples I used the variable name hashed_data/
hashedData to describe the actual data against which my signature
needs to be verified.  I called them that because the data in that
variable is *not* the original text string, but rather the 20-byte
result of SHA1 hashing that original text string.  Of course, it is
this 20-byte hash result that is the thing that *actually* gets
signed, and then against which the signature is later verified.

Because of that, when you call RSA_verify in OpenSSL, you pass it
*not* the original text string bytes, but rather the 20-byte SHA1 hash
of that text string.  That hash is what is actually going to be
checked in the end, so that's what it expects as input.   I was
(erroneously) expecting the same thing in JSSE. Thus, when I called
Signature.update() to give it the data to be verified, I gave it *not*
the original text string bytes, but rather the 20-byte SHA1 hash of
that text string, paralleling what I do in OpenSSL.

However, in JSSE, since you create your Signature object with
"SHA1withRSA", it's smart enough to be able to accept the actual
original text bytes and know to do the SHA1 hash on that *for* you.
So since I had SHA1 hashed my string myself first, then passed that
hash result into update(), my data ultimately got hashed *twice*, and
that's why the signature verification didn't work.

I don't see anything in the Signature javadoc that makes it explicitly
clear that the SHA1 hash will be taken care of for you.  However, I'll
grant that this may simply be because doing it that way is actually
what is intuitive.  Had I not been coming out of a "this is how I did
it in OpenSSL" background, I probably would have fully expected it to
work exactly the way it does in JSSE, and never would have had a
problem.

Oh well, at least the problem is solved.  So I can say that I did get
my $50 worth from Sun's paid support service.
8-)

- Michael


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.