2015-12-03 11 views
5

Ich entwickle eine Android-Anwendung, die ein HTML-Dokument digital signieren muss. Das Dokument befindet sich in der Datenbank in einem JSON-Formular. Ich bin die Unterzeichnung das Dokuments lokal einen Bash-Skript verwende ich auf einer anderen SO Frage gefunden:Digitale Signatur unter Android

openssl dgst -sha1 someHTMLDoc.html > hash openssl rsautl -sign -inkey privateKey.pem -keyform PEM -in hash > signature.bin

private Schlüssel verwendet hat generiert:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:3 -out privateKey.pem 

Public-Key wurde mit erstellt:

openssl pkey -in privateKey.pem -out publicKey.pem -pubout 

Ich möchte die Signatur überprüfen, die in Signature.bin zusammen mit den Daten in someHTMLDoc.html erstellt wurde, zurück in th die Anwendung.

Ich sende sowohl die HTML-und Signatur als JSON-Objekt ab:

{ "data" : "<html><body></body></html>", "signature":"6598 13a9 b12b 21a9 ..... " } 

Die Android-Anwendung die PublicKey in gemeinsamen Prefs hält wie folgt:

-----BEGIN PUBLIC KEY----- MIIBIDANBgkqhkiG9w0AAAEFAAOCAQ0AvniCAKCAQEAvni/NSEX3Rhx91HkJl85 \nx1noyYET ......

Beachten Sie die "\ n" (Newline) darin (wurde automatisch hinzugefügt, wenn die Zeichenfolge von publicKey.pem in Android Gradle Config kopiert wurde.

Ok, nach allen Vorbereitungen, jetzt die Frage. Ich versuche, den Schlüssel ohne Erfolg zu validieren.

ich den folgenden Code verwenden:

private boolean verifySignature(String data, String signature) { 
    InputStream is = null; 
    try { 
     is = new ByteArrayInputStream(Config.getDogbarPublic().getBytes("UTF-8")); //Read DogBar Public key 

     BufferedReader br = new BufferedReader(new InputStreamReader(is)); 
     List<String> lines = new ArrayList<String>(); 
     String line; 
     while ((line = br.readLine()) != null) 
      lines.add(line); 

     // removes the first and last lines of the file (comments) 
     if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size() - 1).startsWith("-----")) { 
      lines.remove(0); 
      lines.remove(lines.size() - 1); 
     } 

     // concats the remaining lines to a single String 
     StringBuilder sb = new StringBuilder(); 
     for (String aLine : lines) 
      sb.append(aLine); 
     String key = sb.toString(); 

     byte[] keyBytes = Base64.decode(key.getBytes("utf-8"), Base64.DEFAULT); 
     X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); 
     KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
     PublicKey publicKey = keyFactory.generatePublic(spec); 

     Signature signCheck = Signature.getInstance("SHA1withRSA"); //Instantiate signature checker object. 
     signCheck.initVerify(publicKey); 
     signCheck.update(data.getBytes()); 
     return signCheck.verify(signature.getBytes()); //verify signature with public key 
    } catch (Exception e) { 
     e.printStackTrace(); 
     return false; 
    } 
} 

Kann jemand helfen? Was mache ich falsch ?

Fehle ich einige Byte-Konvertierung? Vielleicht beeinflusst das JSON-Objekt die Signatur?

Sollte eine Signatur die \ n (Zeilenumbrüche) enthalten, die die Originaldatei enthält oder in der JSON-Datei enthalten sollte?

Vielen Dank im Voraus für alle Hilfe, es ist sehr geschätzt.

+0

Ich versuche verschiedene Methoden ohne Erfolg, habe versucht, die Zeilenumbrüche aus dem JSON-Objekt zu entfernen, habe getBytes ("ASCII") und getBytes ("UTF-8") versucht. – Noxymon

+1

Haben Sie eine Ausnahme erhalten? Welcher? – Henry

+0

Ich habe keine Ausnahme erhalten, es gibt nur False für 'signCheck.verify()' – Noxymon

Antwort

3

Digitale Signatur ist ein Prozess, Digest of Computing (Funktion H) von Daten (C) und mit asymmetrischem Verschlüsselungsalgorithmus (Funktion E) Verschlüsseln Chiffretext (S) zu erzeugen:

S = E(H(C)) 

Signaturprüfung erfolgt die Signatur entschlüsselt die gegebene Signatur (Funktion D) - was nur dann zu H (C) führt, wenn der bei der Entschlüsselung verwendete öffentliche Schlüssel mit dem bei der Verschlüsselung verwendeten privaten Schlüssel gepaart ist, und berechnet den Digest der Daten, um zu überprüfen, ob die zwei Digests übereinstimmen:

H(C) == D(E(H(C))) 

Es ist klar von diesem t Die Bytes, die der Hash-Funktion (C) übergeben werden, müssen genau gleich sein, damit die Signatur validiert werden kann.

In Ihrem Fall sind sie nicht, denn wenn man den Digest sind Rechen openssl dgst mit dem Ausgang (H (C) auf der rechten Seite) ist buchstäblich so etwas wie:

SHA1(someHTMLDoc.html)= 22596363b3de40b06f981fb85d82312e8c0ed511 

Und dies ist der Eingang die RSA-Verschlüsselung.

Und wenn Sie die Signatur sind die Überprüfung, die Ausgabe des Digest (H (C) auf der linken Seite) ist der rohe Bytes, zum Beispiel in hex:

22596363b3de40b06f981fb85d82312e8c0ed511 

So Sie am Ende Bytes verschlüsselt bis (H (C) auf der rechten Seite) zu erzeugen:

0000000: 5348 4131 2873 6f6d 6548 746d 6c44 6f63 SHA1(someHtmlDoc 
0000010: 2e68 746d 6c29 3d20 3232 3539 3633 3633 .html)= 22596363 
0000020: 6233 6465 3430 6230 3666 3938 3166 6238 b3de40b06f981fb8 
0000030: 3564 3832 3331 3265 3863 3065 6435 3131 5d82312e8c0ed511 
0000040: 0a          . 

und Vergleichen gegen Bytes (H (C) auf der linken Seite):

0000000: 2259 6363 b3de 40b0 6f98 1fb8 5d82 312e "[email protected]].1. 
0000010: 8c0e d511        .... 

Sie müssen auch -sign mit openssl dgst verwenden, um das richtige Ausgabeformat zu haben (siehe Difference between openSSL rsautl and dgst).

Also auf der OpenSSL Seite tun:

openssl dgst -sha1 -sign privateKey.pem someHTMLDoc.html > signature.bin 

Auf der Java-Seite tun:

import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.security.KeyFactory; 
import java.security.Signature; 
import java.security.interfaces.RSAPublicKey; 
import java.security.spec.X509EncodedKeySpec; 

import org.spongycastle.util.io.pem.PemObject; 
import org.spongycastle.util.io.pem.PemReader; 

public class VerifySignature { 
    public static void main(final String[] args) throws Exception { 
     try (PemReader reader = publicKeyReader(); InputStream data = data(); InputStream signatureData = signature()) { 
      final PemObject publicKeyPem = reader.readPemObject(); 
      final byte[] publicKeyBytes = publicKeyPem.getContent(); 
      final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
      final X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes); 
      final RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(publicKeySpec); 

      final Signature signature = Signature.getInstance("SHA1withRSA"); 
      signature.initVerify(publicKey); 

      final byte[] buffy = new byte[16 * 1024]; 
      int read = -1; 
      while ((read = data.read(buffy)) != -1) { 
       signature.update(buffy, 0, read); 
      } 

      final byte[] signatureBytes = new byte[publicKey.getModulus().bitLength()/8]; 
      signatureData.read(signatureBytes); 

      System.out.println(signature.verify(signatureBytes)); 
     } 
    } 

    private static InputStream data() throws FileNotFoundException { 
     return new FileInputStream("someHTMLDoc.html"); 
    } 

    private static PemReader publicKeyReader() throws FileNotFoundException { 
     return new PemReader(new InputStreamReader(new FileInputStream("publicKey.pem"))); 
    } 

    private static InputStream signature() throws FileNotFoundException { 
     return new FileInputStream("signature.bin"); 
    } 
} 

I Spongy Castle für PEM-Dekodierung des öffentlichen Schlüssels verwendet habe, die Dinge ein bisschen besser lesbar zu machen und einfacher zu benutzen.

+0

Ich bin mir nicht sicher, ob ich völlig verstehe .. Wie könnten Sie über die Verschlüsselung gehen? soll ich nur den '22596363b3de40b06f981fb85d82312e8c0ed511' Teil der dgst Funktion nehmen und damit signieren? Könntest du mir bitte erklären, was ich tun sollte, anstatt was ist der Fehler, den ich gemacht habe? Mir fällt es schwer zu verstehen, wie man vorgeht, was geschrieben wurde. – Noxymon

+1

In meiner bearbeiteten Antwort sollten Sie 'openssl dgst' mit der Option' -sign' verwenden, um eine binäre Ausgabe zu erzeugen, die von der Signature-Implementierung in JCE benötigt wird. –

+0

Gib diesem Mann einen Keks! Sie sind ein Genie! Ich danke dir sehr. – Noxymon

Verwandte Themen