1

On the example given at ECDSA sign with BouncyCastle and verify with Crypto++, the DSAConvertSignatureFormat() needs byte[] as arguments. But what I got from my Java code, I got a signature like this which is a String:

302e021500f16529dcaddd3cec7616a3f94e157d1c28df8ea9021500997de4ae5497268c4f8eb3129abb11ca2abea9c1

How do I use the Java signature in the Crypto++ code?


Here is the Crypto++ code in the question:

bool VerifyMessage( const ECDSA<ECP, SHA256>::PublicKey& key, const string& message, const string& signature )
{
   bool result = false;
   string signatureFromJava("302e021500cb3333768bbe3f26d7a58388015d6110c1dbad5f021500dc2ee848c72deee1542939b3e5eb2816e71bf895");
   SecByteBlock signatureFromJavaByte((byte *)signatureFromJava.data(), signatureFromJava.size());

   byte finalSignature[0x40];
   DSAConvertSignatureFormat(finalSignature, sizeof(finalSignature), DSA_P1363,
                             signatureFromJavaByte, sizeof(signatureFromJavaByte), DSA_DER);

   // TODO convert finalSignature to std::string


   // Hexa encoding version, more readable
   std::string decodedSignature;
   StringSource(signature, true,
                  new HexDecoder(
                    new StringSink(decodedSignature)));

   StringSource(decodedSignature+message, true,
                  new SignatureVerificationFilter(ECDSA<ECP,SHA256>::Verifier(key),
                    new ArraySink((byte*)&result,
                      sizeof(result))));

   return result;
}

Here is the Java code in question:

public static String sign(String data) throws Exception {
    KeyPair keyPair = loadKeyPair(System.getProperty("user.dir"),"ECDSA");
    Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
    signature.initSign(keyPair.getPrivate(), new SecureRandom());
    byte[] message = data.getBytes();
    signature.update(message);
    byte[] sigBytes = signature.sign();
    //verify("TEST", sigBytes);
    String signatureStr = new BigInteger(1, sigBytes).toString(16);
    return signatureStr;
}

jww
  • 97,681
  • 90
  • 411
  • 885
zoraj
  • 73
  • 1
  • 4
  • I call crypto++ by linking with the static lib. nor JNI or other IPC mechanism. I fired up my executable on terminal then pass the signature generated from bouncycastle. the c++ program receive this signature via main(arg) method , so I have to transform the signature to P1363 right ? – zoraj Feb 23 '18 at 12:41

1 Answers1

1

Comment: it crashes on DSAConvertSignatureFormat() call

byte finalSignature[0x40];
DSAConvertSignatureFormat(finalSignature, sizeof(finalSignature), DSA_P1363,
                          signatureFromJavaByte, sizeof(signatureFromJavaByte), DSA_DER);

The crash is because sizeof(signatureFromJavaByte) is the size of a std::string, and not the string's length. A std::string is 16 or so bytes. That consists of 8 bytes for a pointer, and 8 bytes to hold the size of the data pointed to. Instead of passing 66 to 72 bytes to the function, only 16 or so bytes are being provided.

I think the function has other issues so you might want to head in another direction.


How do I use the Java signature in the Crypto++ code?

You can use code similar to below.

You should use the pattern &str[0] to get a non-const pointer to the first element of the string. As far as I know, it is the only well defined way to get the non-const pointer. Everything else can lead to undefined behavior.

#define LOG_TAG "MY_PRODUCT"
#define LOG_DEBUG(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LOG_INFO(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOG_WARN(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#define LOG_ERROR(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))

bool VerifyMessage( const ECDSA<ECP, SHA256>::PublicKey& key, const string& message, const string& javaSignature )
{
    ECDSA<ECP, SHA256>::Verifier verifier(key);
    string ieeeSignature(0x40, '\0');

    size_t size = DSAConvertSignatureFormat(
                    reinterpret_cast<byte*>(&ieeeSignature[0]), ieeeSignature.size(), DSA_P1363,
                    reinterpret_cast<const byte*>(&javaSignature[0]), javaSignature.size(), DSA_DER);
    ASSERT(size == 0x40);

    bool result = verifier.VerifyMessage(
                    reinterpret_cast<const byte*>(&message[0]), message.size(),
                    reinterpret_cast<const byte*>(&ieeeSignature[0]), ieeeSignature.size());

    if (result)
        LOG_INFO("VerifyMessage: verified message");
    else
        LOG_WARN("VerifyMessage: failed to verify message");

    return result;
}

Call it like the following.

string javaSignature("302e021500cb3333768bbe3f26d7a58388015d6110c1dbad5f021500dc2ee848c72deee1542939b3e5eb2816e71bf895");
string derSignature;  // Hex decoded signature

StringSource(javaSignature, true, new HexDecoder(new StringSink(derSignature)));

bool verified = VerifyMessage(key, message, derSignature);

You could also add a function like the following to convert arbitrary signatures from DER to P1363:

std::string DER2P1363(const std::string& signature)
{
    std::string result;
    result.resize(256);

    size_t size = DSAConvertSignatureFormat(
                    reinterpret_cast<byte*>(&result[0]), result.size(), DSA_P1363,
                    reinterpret_cast<const byte*>(&signature[0]), signature.size(), DSA_DER);

    result.resize(size);
    return result;
}

The 256 is a maximum string length. It will be resized after converting the DER signature.

jww
  • 97,681
  • 90
  • 411
  • 885