2015-05-11 8 views
7

Przetestowałem rozwiązanie w celu zweryfikowania podpisu ECDSA (How can I get a PublicKey object from EC public key bytes?), który działa idealnie z podanymi danymi.Błąd podczas sprawdzania podpisu ECDSA w Javie za pomocą BouncyCastle

To dane:

byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e3789c36a7a"); 
byte[] message = DatatypeConverter.parseHexBinary("54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29"); 
byte[] signature = DatatypeConverter.parseHexBinary("304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654"); 

I to jest kod (który drukuje prawda):

private static boolean isValidSignature(byte[] pubKey, byte[] message,byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException { 
    Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider()); 
    ecdsaVerify.initVerify(getPublicKeyFromBytes(pubKey)); 
    ecdsaVerify.update(message); 
    return ecdsaVerify.verify(signature); 
} 

private static PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException { 
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1"); 
    KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider()); 
    ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN()); 
    ECPoint point = ECPointUtil.decodePoint(params.getCurve(), pubKey); 
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params); 
    ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec); 
    return pk; 
} 

public static void main (String[] args) { 
    System.out.println(isValidSignature(pubKey, message, signature)); 
} 

Mój problem pojawia się, gdy mogę zmienić podpis i dane do przykładu dane wejściowe z już wdrożonego systemu:

final static byte[] pubKey = DatatypeConverter.parseHexBinary("0447303876C6FED5550DF3EE1136989FCD87293D54A5D8E2F2F6D7FBE9A81089B889A5917443AF33E696178CEF4C9D6A4288B2745B29AF6C8BCAD1348F78EB9F9B"); 
final static byte[] message = DatatypeConverter.parseHexBinary("02158001f53611a06e2d1a270000013ed9305dc2780524015110500000002d0100140092569202017aa00c5dd30000000000000000000000000000000007d1000001020001b20788b80059f48d95cdefc8c6000200200030d41e0000012016840310a50733a9870fffd0430100"); 
final static byte[] signature = DatatypeConverter.parseHexBinary("531F8918FF250132959B01F7F56FDFD9E6CA3EC2144E12A6DA37C281489A3D96"); 

Nowe wyjścia danych ten błąd:

java.security.SignatureException: error decoding signature bytes. 
    at org.bouncycastle.jcajce.provider.asymmetric.util.DSABase.engineVerify(Unknown Source) 
    at java.security.Signature$Delegate.engineVerify(Signature.java:1178) 
    at java.security.Signature.verify(Signature.java:612) 
    at its.sec.exec.TestProgram.isValidSignature(TestProgram.java:168) 
    at its.sec.exec.TestProgram.execution(TestProgram.java:101) 
    at its.sec.exec.TestProgram.main(TestProgram.java:55) 

Zakładam, że problem jest o podpis, który pochodzi z zabezpieczonej wiadomości ponieważ:

  • Kluczem para jest taka sama długość i formatowanie że przykład. I są poprawne, ponieważ pochodzą z certyfikatu, który podpisuje wiadomość.
  • Sam komunikat (ładunek) nie powinien wpływać na proces bezpieczeństwa.

Ostatnią rzeczą warto wspomnieć, że moja dokumentacja mówi, że podpis musi być poprzedzone przez pole o nazwie „R”, który „zawiera współrzędna x eliptycznej punktu krzywej wynikającej z pomnożenia element generatora przez efemeryczny prywatny klucz ", a jego długość musi być taka sama jak podpis (32 bajty).

Czy ktoś może wskazać mi, czego tu brakuje?

EDIT: Rozwiązanie

Jak Peter Dettman wskazał w swojej odpowiedzi, signature nie została prawidłowo sformatowana (również zawartość była nieprawidłowa też), aby obliczyć metodą verify(). Here jest dobrym wyjaśnieniem, że głównie mówi, że:

When encoded in DER, this (signature) becomes the following sequence of bytes:

0x30 b1 0x02 b2 (vr) 0x02 b3 (vs)

where:

  • b1 is a single byte value, equal to the length, in bytes, of the remaining list of bytes (from the first 0x02 to the end of the encoding);
  • b2 is a single byte value, equal to the length, in bytes, of (vr);
  • b3 is a single byte value, equal to the length, in bytes, of (vs);
  • (vr) is the signed big-endian encoding of the value "r", of minimal length;
  • (vs) is the signed big-endian encoding of the value "s", of minimal length.

Stosując tę ​​zmianę, signature rośnie do 70 bajtów i wyjść wykonania żadnego błędu.

+0

Uwaga na różnicę między ASN.1 liczba całkowita zakodowana w DER i liczba całkowita bez znaku o tym samym rozmiarze, co klucz - łatwe do popełnienia błędy w odniesieniu do lewego marginesu z bajtami o zerowej wartości! –

Odpowiedz

6

Oczekiwany format podpisu ECDSA, z którym współpracują implementacje BC (i innych dostawców), to sekwencja ASN.1 kodowana przez DER, zawierająca dwie wartości całkowite r i s. Ten format podpisu został określony w ANSI X9.62. Jest to format w pierwszym zestawie danych, które podajesz (zwróć uwagę, że signature to w sumie 70 bajtów).

W drugim zbiorze danych signature ma tylko 32 bajty i wcale nie jest sekwencją ASN.1. Domyślam się, że ta wartość jest tylko wartością s, i brakuje jej wartości r i kodowania ASN.1 INTEGER dla nich obu, zamiast kodowania wartości jako niepodpisanej dużej liczby całkowitej o tym samym rozmiarze co klucz.

Powiązane problemy