Przemyslaw Iwanczak
Przemyslaw Iwanczak

Reputation: 71

Java-produced signature does not verify with openssl

I am probably missing something very simple and stupid - I'd greatly appreciate any and all hints.

What I am trying to achieve is to produce a signature as per Apple's doc

  1. "CLI-like" setup

Sign.java contents:

import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;

public class Sign {
    public static final String SKAD_SIGNING_ALGORITHM = "SHA256withECDSA";

    public static void main(String[] args) throws Exception {
        byte[] keyBytes = Files.readAllBytes(Path.of(args[0]));

        KeyFactory kf = KeyFactory.getInstance("EC");
        PKCS8EncodedKeySpec pkcs8EncodedKey = new PKCS8EncodedKeySpec(keyBytes);
        PrivateKey key = kf.generatePrivate(pkcs8EncodedKey);

        Signature ecdsa = Signature.getInstance(SKAD_SIGNING_ALGORITHM);
        ecdsa.initSign(key);
        ecdsa.update(System.in.readAllBytes());

        byte[] realSig = ecdsa.sign();
        System.out.write(realSig);
    }
}

Produce the signature w/ openssl:

$ cat $(test_doc) | java -cp src Sign <(openssl pkcs8 -in $(private_key) -inform PEM outform DER -nocrypt -topk8) >$(java_signature)

... and verify the sig:

$ openssl dgst -sha256 -verify $(public_key) -signature $(java_signature) $(test_doc)

$(test_doc) - being simple message to be signed\
$(private_key) - pkcs#8 private key\
$(public_key) - pkcs#8 public key

Works fine!

  1. Java without CLI setup

When I try to produce a signature like this:

@Test
public void SimplestSkadSignTest()
            throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeySpecException,
            InvalidKeyException {
        String SKAD_SIGNING_ALGORITHM = "SHA256withECDSA";
        String MESSAGE = "hello world";

        byte[] keyBytes = SKAdHelper.class
                .getResourceAsStream("/private_key_in_der_format.der").readAllBytes();

        KeyFactory kf = KeyFactory.getInstance("EC");
        PKCS8EncodedKeySpec pkcs8EncodedKey = new PKCS8EncodedKeySpec(keyBytes);
        PrivateKey key = kf.generatePrivate(pkcs8EncodedKey);

        Signature ecdsa = Signature.getInstance(SKAD_SIGNING_ALGORITHM);
        ecdsa.initSign(key);
        ecdsa.update(MESSAGE.getBytes(StandardCharsets.UTF_8));

        byte[] realSig = ecdsa.sign();

        System.out.print(Base64.getEncoder().encodeToString(realSig));
        
        ByteSink byteSink = MoreFiles.asByteSink(Path.of("/dump/anywhere/sig.bin"));
        byteSink.write(realSig);
    }

OpenSSL won't validate the produced sig.bin

$ openssl dgst -sha256 -verify $(public_key) -signature sig.bin $(test_doc)
Verification Failure

Both ways seem the same to me - what am I missing here folks?

Upvotes: 1

Views: 163

Answers (1)

Przemyslaw Iwanczak
Przemyslaw Iwanczak

Reputation: 71

Tldr: both work. Problem was between my chair and the keyboard :-)

@dave_thompson_085 was on point, to be precise:

  1. I've been creating cleartext message "by hand", in VIM. Byte-structure looks like this:
$ hexdump -C message.txt 
00000000  68 65 6c 6c 6f 20 77 6f  72 6c 64 0a              |hello world.|
0000000c
  1. In java, there is a simple String w/ seemingly identical contents:
String MESSAGE = "hello world";

... but it is not. Dumped from java to file:

ByteSink byteSink = MoreFiles.asByteSink(Path.of("/cleartext_message.txt"));
byteSink.write(MESSAGE.getBytes(StandardCharsets.UTF_8));

internally looks like this:

$ hexdump -C message_java.txt 
00000000  68 65 6c 6c 6f 20 77 6f  72 6c 64                 |hello world|
0000000b

Upvotes: 1

Related Questions