2015-12-03 2 views
5

Я разрабатываю приложение для Android, которое требует цифровой подписи документа html. Документ находится в БД в форме JSON. Я подписывая документ локально с помощью Баш скрипт я нашел на каком-то другом С.О. вопрос:Проверка цифровой подписи на Android

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

Закрытый ключ был сгенерирован с помощью:

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

Открытый ключ был создан с помощью:

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

Я хочу проверить подпись, созданную в Signature.bin, вместе с данными в someHTMLDoc.html, назад e приложение.

Я посылаю как HTML и подпись как JSON экс объекта:

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

андроид приложение держит ОткрытыйКлюч в общих префов следующим образом:

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

Обратите внимание на «\ n» (новая строка) там (была автоматически добавлена ​​при копировании строки из publicKey.pem в Android Gradle Config.

Хорошо, все приготовления, сейчас вопрос. Я пытаюсь проверить ключ без успеха.

Я использую следующий код:

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; 
    } 
} 

Может кто-нибудь помочь? Что я делаю не так ?

Am i отсутствует какой-нибудь байт изменение? возможно, объект JSON влияет на подпись?

Должна ли подпись содержать \ n (linebreak), чтобы исходный файл содержал или должен быть без него в файле JSON?

Заранее благодарим за помощь, ее высоко оценили.

+0

Я продолжаю пробовать разные методы без успеха, попытался удалить разрывы строк из объекта JSON, попробовал getBytes («ASCII») и getBytes («UTF-8»). – Noxymon

+1

Вы получили какое-то исключение? Который из? – Henry

+0

Я не получал никакого исключения, он просто возвращает False для 'signCheck.verify()' – Noxymon

ответ

3

Цифровая подпись представляет собой процесс вычисления дайджеста (функция H) данных (C) и шифрования с помощью алгоритма асимметричного шифрования (функция Е) для получения CYPHER текста (S):

S = E(H(C)) 

Подписи проверка принимает подпись расшифровывает данную подпись (функция D), которая приводит к H (C), только если открытый ключ, используемый в дешифровке, сопряжен с закрытым ключом, используемым в шифровании, и вычисляет дайджест данных, чтобы проверить, соответствуют ли эти два дайджестова:

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

Это ясно из этого х, байты, заданные хэш-функции (C), должны быть точно такими же, чтобы подпись проверялась.

В вашем случае это не так, потому что, когда вы вычисления дайджеста, используя openssl dgst выход (H (C) справа) буквально что-то вроде:

SHA1(someHTMLDoc.html)= 22596363b3de40b06f981fb85d82312e8c0ed511 

И это вход шифрование RSA.

И когда вы проверки подписи, выход дайджеста (H (C) слева) являются сырые байт, например, в шестнадцатеричном:

22596363b3de40b06f981fb85d82312e8c0ed511 

Таким образом, вы в конечном итоге шифрования байт для получения (Н (с) справа):

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          . 

и сравнение против байтов (Н (с) на левой стороне):

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

Также необходимо использовать -sign с openssl dgst, чтобы иметь правильный формат вывода (см. Difference between openSSL rsautl and dgst).

Так на стороне OpenSSL сделать:

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

На стороне Java сделать:

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"); 
    } 
} 

Я использовал Spongy Castle для PEM декодирования открытого ключа, чтобы сделать вещи немного более удобным для чтения и проще в использовании.

+0

. Я не уверен, что полностью понимаю .. как вы собираетесь шифровать? должен ли я взять только часть 2259363b3de40b06f981fb85d82312e8c0ed511' функции dgst и подписать с этим? не могли бы вы объяснить, что я должен делать, а не какая ошибка, которую я сделал? я с трудом понимаю, как идти о том, что было написано. – Noxymon

+1

См. Мой отредактированный ответ, вы должны использовать 'openssl dgst' с опцией' -sign' для создания двоичного вывода, необходимого для реализации Signature в JCE. –

+0

Дайте этому человеку печенье! Вы, сэр, гений! Огромное спасибо. – Noxymon

Смежные вопросы