3

Given an X509 certificate object of a person. (The object's type is sun.security.x509.X509CertImpl). This person signed a String with his private key. Given the signature that was made by this person, when he signed the above mentioned String object.

My task is to verify this signature, but have difficulties with it.

When I try to verify the signature with the below code:

    ...
    X509Certificate x509Certificate = getCertificate(certificate);

    Signature signature = Signature.getInstance("SHA256withECDSA");

    signature.initVerify(x509Certificate.getPublicKey());

    signature.update(unsignedData);
    boolean bool = signature.verify(signatureToVerify);
    System.out.println("The signature is " + (bool ? "" : "NOT") + " valid");

I get java.security.SignatureException: Could not verify signature

Do you have an idea, how can I make it working?

Edited: At the end, I managed to make it working, but do not understand the reason yet: Before passing the signature to the verify method, i needed to do the following modification on it:

    byte[] rBytes = Arrays.copyOfRange(signatureHash, 0, 32);
    byte[] sBytes = Arrays.copyOfRange(signatureHash, 32, 64);

    BigInteger r = new BigInteger(1, rBytes);
    BigInteger s = new BigInteger(1, sBytes);

    ASN1Integer asn1R = new ASN1Integer(r);
    ASN1Integer asn1S = new ASN1Integer(s);

    DERSequence seq = new DERSequence(new ASN1Integer[]{asn1R, asn1S});
    byte[] signatureToVerify2 = seq.getEncoded();
    // verifying the signatureToVerify2 instead of the original brings success
    boolean bool = signature.verify(signatureToVerify2);
SaWo
  • 1,515
  • 2
  • 14
  • 32
  • The `Signature.initVerify()` method has an overload where you can use the certificate object (see https://docs.oracle.com/javase/7/docs/api/java/security/Signature.html#initVerify(java.security.cert.Certificate)). Does this method work? Please [edit] your question to include your full source code you have as a [mcve], which can be compiled and tested by others. Also include the actual certificate you are using for testing and the signed message (with the signature) you want to check. – Progman Oct 13 '19 at 08:52
  • 2
    `certificate.getSigAlgName()` is the the algorithm used to sign _the certificate by the CA_, NOT the algorithm used to sign data by the subject. Type 'EC' key is used for ECDSA, with about a dozen different hashes, which is a critical part of the signature algorithm and unless you know or guess which hash you can't verify it. **The correct algorithm name will be of the form `SHA1withECDSA SHA256withECDSA SHA384withECDSA` or similar.** ... – dave_thompson_085 Oct 13 '19 at 11:38
  • 1
    ... If you look at the `ECPublicKey` object's curve and specifically `.getParams().getCurve().getField().getFieldSize()` it is _likely_ (but not required or guaranteed) the hash used will be the same size or very near. – dave_thompson_085 Oct 13 '19 at 11:41
  • @dave_thompson_085: thanks for the advice. I am one step closer now :) The field-size of the elliptic curve is 256 as I see it. I have tested the app with all three algorithms you have mentioned (SHA1withECDSA, SHA256withECDSA, SHA384withECDSA), but none of them works. I always get "java.security.SignatureException: error decoding signature bytes.". Is there maybe a bigger list of algorithms I can try out with? Unfortunately asking the inventor is not an option. I use the signing procedure via a web-service, which is a black-box thing. – SaWo Oct 13 '19 at 12:38
  • 'decoding signature' is a different problem. What format are these signatures? Are they all exactly 64 bytes, or do they vary in length from 70 to 72 and have first bytes 0x30 (decimal 48) and 0x44-46 (decimal 68-70)? – dave_thompson_085 Oct 14 '19 at 06:33
  • @dave_thompson_085 the signatures are always exactly 64 bytes long. – SaWo Oct 14 '19 at 07:10
  • I have edited the question with the missing piece, however, I do not understand why it helped – SaWo Oct 14 '19 at 13:08
  • 1
    Yes, the fixed 32+32-byte format defined by the P1363 standard and some others differs from the ASN.1 format in the X9.62 standard used by Java with the standard (Sun/Oracle) providers, and your conversion to a DER SEQUENCE of two INTEGERs is a correct solution to that. An alternative is to use the _BouncyCastle_ provider instead, which does support this format using names like `SHAXwithPLAIN-ECDSA` or `SHAXwithCVC-ECDSA`. Cf https://stackoverflow.com/q/36542645/ https://crypto.stackexchange.com/q/59982/ https://crypto.stackexchange.com/q/33095/ https://crypto.stackexchange.com/q/57731/ – dave_thompson_085 Oct 14 '19 at 19:16
  • On further checking, in Java 9 up SunEC also handles the P1363 fixed format using names like SHAXwithECDSAinP1363format; see also https://stackoverflow.com/questions/48177791/how-to-specify-signature-length-for-java-security-signature-sign-method https://stackoverflow.com/questions/52122705/java-ecdsawithsha256-signature-with-inconsistent-length – dave_thompson_085 Oct 16 '19 at 13:03

1 Answers1

0

Here is a (semi)working app for further reference that verifies a signature, when ECDSA is involved in the story:

import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERSequence;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;

public class SignatureTest {

    public static void main(String[] args) throws CertificateException, InvalidKeyException, SignatureException, NoSuchAlgorithmException, IOException {
        byte[] certificateAsByteArray = ...;
        byte[] dataToVerifyAsByteArray = ...;
        byte[] signatureHashAsByteArray = ...;

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream in = new ByteArrayInputStream(certificateBytes);
        X509Certificate x509Certificate = (X509Certificate) certFactory.generateCertificate(in);

        Signature signature = Signature.getInstance("SHA256withECDSA");

        signature.initVerify(x509Certificate.getPublicKey());

        signature.update(dataToVerifyAsHexaString);

        byte[] rBytes = Arrays.copyOfRange(signatureHash, 0, 32);
        byte[] sBytes = Arrays.copyOfRange(signatureHash, 32, 64);

        ASN1Integer asn1R = new ASN1Integer(rBytes);
        ASN1Integer asn1S = new ASN1Integer(sBytes);

        DERSequence seq = new DERSequence(new ASN1Integer[] {asn1R, asn1S});

        boolean isSignatureOK = signature.verify(seq.getEncoded());

        System.out.println("The signature is " + (isSignatureOK ? "" : "NOT ") + "VALID");
    }
}
SaWo
  • 1,515
  • 2
  • 14
  • 32