2015-12-03 20 views
5

私は、HTML文書に電子署名する必要があるAndroidアプリケーションを開発しています。 ドキュメントは、JSON形式のDBに存在します。 私はいくつかの他のSOの質問で発見BASHスクリプトを使用してローカルで文書に署名しています: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のデータとともに検証したい場合は、アプリケーション。

私はJSONオブジェクトの元としてhtmlと署名の両方を送信しています:

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

次のようにアンドロイドのアプリケーションが共有環境設定でのPublicKeyを保持している:

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

は誰でも助けることができます:

私は、次のコードを使用していますか?私は間違って何をしていますか?

いくつかのバイト変換がありませんか?多分JSONオブジェクトが署名に影響を与えているのでしょうか?

オリジナルファイルに含まれる\ n(改行)の署名が含まれている必要がありますか、それともJSONファイルに含まれていないはずですか?

ご協力いただきありがとうございます。非常に高く評価されています。

+0

私は成功のないさまざまな方法を試し続け、JSONオブジェクトから改行を取り除いてみました。getBytes( "ASCII")とgetBytes( "UTF-8")を試しました。 – Noxymon

+1

例外はありますか?どれ? – Henry

+0

私は例外を受けなかっただけで、 'signCheck.verify()'に対してFalseを返します。 – Noxymon

答えて

3

デジタル署名データのダイジェスト(関数H)(C)を計算し、CYPHERテキスト(S)を生成する非対称暗号化アルゴリズム(関数E)を用いてそれを暗号化するプロセスである:

S = E(H(C)) 

署名検証がかかり復号化に使用された公開鍵が暗号化に使用された秘密鍵と対になっている場合にのみH(C)となる所与の署名(関数D)を復号化し、2つのダイジェストが一致するかどうかを確認するためにデータダイジェストを計算する:

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

これは明らかですhatハッシュ関数(C)に与えられたバイトは、署名が検証されるためにはまったく同じでなければならない。あなたのケースでは

あなたがopenssl dgstを使用してダイジェストを計算しているときに出力(右のH(C))は、文字通りのようなものであるので、彼らは、ありません。

SHA1(someHTMLDoc.html)= 22596363b3de40b06f981fb85d82312e8c0ed511 

そして、これが入力されますRSA暗号化。

そして、あなたは、(左のH(C))ダイジェストの出力を署名を検証している六角における例えばバイト、以下のとおりです。

22596363b3de40b06f981fb85d82312e8c0ed511 

だからあなたが終わります(右H(C))を生成するバイトを暗号化:

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          . 

とバイト(左側H(C))と比較する:

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

正しい出力形式(Difference between openSSL rsautl and dgstを参照)を得るには、-signopenssl dgstとする必要があります。 OpenSSLの側でそう

は行います

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

は行います

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

私は物事が少し読みやすくするために、公開鍵のPEMのデコードにSpongy Castleを使用しました使いやすくなっています。

+0

私は完全に理解していません。どのように暗号化を行っていますか? dgst関数の '22596363b3de40b06f981fb85d82312e8c0ed511'部分だけをとり、それに署名する必要がありますか?私が作った間違いではなく、何をすべきか説明してください。私は書かれたものについてどうやって行くのかを理解するのに苦労しています。 – Noxymon

+1

私の編集した答えを見て、JCEのSignature実装が必要とするバイナリ出力を生成するには 'openssl dgst'と' -sign'オプションを使うべきです。 –

+0

この人にクッキーを渡してください!あなたは天才です!どうもありがとうございます。 – Noxymon