2017-01-20 25 views
2

現在、私は、PDFファイルを指定して(サーバ証明書を使用して)署名を付け、署名を元のファイルに添付してクライアントに戻すクライアント/サーバーアプリケーションを持っていますこれはPDFBoxで実現します)。
私はそれが正常に動作しますが、私は考えていた私の外部署名のサポートで署名ハンドラ、(内容はPDFファイルです)ファイルダイジェストからpkcs7シグネチャを作成する

public byte[] sign(InputStream content) throws IOException { 
    try { 
     System.out.println("Generating CMS signed data"); 
     CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); 
     ContentSigner sha1Signer = new JcaContentSignerBuilder("Sha1WithRSA").build(privateKey); 
     generator.addSignerInfoGenerator(
       new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()) 
         .build(sha1Signer, new X509CertificateHolder(certificate.getEncoded()))); 
     CMSTypedData cmsData = new CMSProcessableByteArray(IOUtils.toByteArray(content)); 
     CMSSignedData signedData = generator.generate(cmsData, false); 

     return signedData.getEncoded(); 
    } catch (GeneralSecurityException e) { 
     throw new IOException(e); 
    } catch (CMSException e) { 
     throw new IOException(e); 
    } catch (OperatorCreationException e) { 
     throw new IOException(e); 
    } 
} 

を持っている - PDFファイルをアップロードするには大きすぎる何場合? ex:100MB ...それは永遠にかかります! 私は、PDFファイルに署名するのではなく、そのファイルのハッシュ(ex SHA1)に署名するだけで、クライアントが最後にすべてをまとめているのではないかということを考えていますか?

アップデート:私はこれを理解しようとしている、そして今、私の署名方法がある

@Override 
public byte[] sign(InputStream content) throws IOException { 
    // testSHA1WithRSAAndAttributeTable 
    try { 
     MessageDigest md = MessageDigest.getInstance("SHA1", "BC"); 
     List<Certificate> certList = new ArrayList<Certificate>(); 
     CMSTypedData msg = new CMSProcessableByteArray(IOUtils.toByteArray(content)); 

     certList.add(certificate); 

     Store certs = new JcaCertStore(certList); 

     CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); 

     Attribute attr = new Attribute(CMSAttributes.messageDigest, 
       new DERSet(new DEROctetString(md.digest(IOUtils.toByteArray(content))))); 

     ASN1EncodableVector v = new ASN1EncodableVector(); 

     v.add(attr); 

     SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider()) 
       .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v))); 

     AlgorithmIdentifier sha1withRSA = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA"); 

     CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 
     InputStream in = new ByteArrayInputStream(certificate.getEncoded()); 
     X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in); 

     gen.addSignerInfoGenerator(builder.build(
       new BcRSAContentSignerBuilder(sha1withRSA, 
         new DefaultDigestAlgorithmIdentifierFinder().find(sha1withRSA)) 
           .build(PrivateKeyFactory.createKey(privateKey.getEncoded())), 
       new JcaX509CertificateHolder(cert))); 

     gen.addCertificates(certs); 

     CMSSignedData s = gen.generate(new CMSAbsentContent(), false); 
     return new CMSSignedData(msg, s.getEncoded()).getEncoded(); 

    } catch (Exception e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
     throw new IOException(e); 
    } 

} 

と私はpdfboxでPDFに署名をマージしています

  ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(output); 
     byte[] cmsSignature = sign(externalSigning.getContent()); 
     externalSigning.setSignature(cmsSignature); 

問題は、「文書が署名されてから変更または破損している」ため、署名が無効であるということです。 誰も助けることができますか?

+1

これは可能です(そして分かりやすい)。誰もすぐに答えなければ、来週これを調べます。 – mkl

+0

@mkl大変ありがとうございます。それまでは私は自分の研究を続けるつもりです(現時点では可能です) – Snox

+0

あなたの更新に関して:明らかに問題の1つは、あなたが 'InputStream'を2回読むことです。 2回目の試みは常に空の配列になります。私はまだこれを調べるつもりです。 ;) – mkl

答えて

3

OPはほぼ右のそれを持っている彼のアップデートでは、単に2個のエラーがあります。

  • 彼が二度InputStreamパラメータ内容を読み取ろうとします:

    CMSTypedData msg = new CMSProcessableByteArray(IOUtils.toByteArray(content)); 
    [...] 
    Attribute attr = new Attribute(CMSAttributes.messageDigest, 
         new DERSet(new DEROctetString(md.digest(IOUtils.toByteArray(content))))); 
    

    はこのように、すべてのデータが既に持っていました2回目の試行の前にストリームから読み込まれ、結果として空のbyte[]が返されました。したがって、メッセージダイジェスト属性には間違ったハッシュ値が含まれていました。

  • 彼は複雑な方法で、最終的なCMSのコンテナを作成します。

    return new CMSSignedData(msg, s.getEncoded()).getEncoded(); 
    

実際に必要とされるものに、後者を減らすには、それはCMSTypedData msgの必要はもはや存在しないことが判明しました。したがって、前者は暗黙のうちに解決されます。

ダイジェスト計算をメソッドの先頭に再配置し、さらにSHA256に切り替えると(SHA1は多くのコンテキストで廃止されていますので、別のハッシュアルゴリズムを使用することをお勧めします)、証明書としてchaincertificateは、この方法は、次のようになります。

// Digest generation step 
MessageDigest md = MessageDigest.getInstance("SHA256", "BC"); 
byte[] digest = md.digest(IOUtils.toByteArray(content)); 

// Separate signature container creation step 
List<Certificate> certList = Arrays.asList(chain); 
JcaCertStore certs = new JcaCertStore(certList); 

CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); 

Attribute attr = new Attribute(CMSAttributes.messageDigest, 
     new DERSet(new DEROctetString(digest))); 

ASN1EncodableVector v = new ASN1EncodableVector(); 

v.add(attr); 

SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider()) 
     .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v))); 

AlgorithmIdentifier sha256withRSA = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA"); 

CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 
InputStream in = new ByteArrayInputStream(chain[0].getEncoded()); 
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in); 

gen.addSignerInfoGenerator(builder.build(
     new BcRSAContentSignerBuilder(sha256withRSA, 
       new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA)) 
         .build(PrivateKeyFactory.createKey(pk.getEncoded())), 
     new JcaX509CertificateHolder(cert))); 

gen.addCertificates(certs); 

CMSSignedData s = gen.generate(new CMSAbsentContent(), false); 
return s.getEncoded(); 

CreateSignature方法signWithSeparatedHashing

かなり最小限の署名コードフレームで使用

void sign(PDDocument document, OutputStream output, SignatureInterface signatureInterface) throws IOException 
{ 
    PDSignature signature = new PDSignature(); 
    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); 
    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); 
    signature.setName("Example User"); 
    signature.setLocation("Los Angeles, CA"); 
    signature.setReason("Testing"); 
    signature.setSignDate(Calendar.getInstance()); 
    document.addSignature(signature); 
    ExternalSigningSupport externalSigning = 
      document.saveIncrementalForExternalSigning(output); 
    byte[] cmsSignature = signatureInterface.sign(externalSigning.getContent()); 
    externalSigning.setSignature(cmsSignature); 
} 

CreateSignature方法sign)この

try ( InputStream resource = getClass().getResourceAsStream("test.pdf"); 
     OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "testSignedWithSeparatedHashing.pdf")); 
     PDDocument pdDocument = PDDocument.load(resource) ) 
{ 
    sign(pdDocument, result, data -> signWithSeparatedHashing(data)); 
} 

CreateSignature試験方法testSignWithSeparatedHashing

適切に署名されたPDFファイルに

結果、適切と少なくとも同じ様

証明書と秘密鍵(que)手の手のためのものです。


つ備考:

OPはIOUtils.toByteArray(content))を使用(と私は上記のコードで行います)。しかし、OPの発言を考慮して

PDFファイルが大きすぎてアップロードできない場合はどうすればよいですか? ex:100mb

これは、ハッシュのために一度に大きなファイルをメモリに読み込むようなものではありません。実際にアプリケーションのリソース占有量を考慮する必要がある場合は、一度に数KBをストリームに読み込み、最後にMessageDigest.updateを使用してデータをダイジェストし、最後にMessageDigest.digestを使用して結果のハッシュ値を取得する必要があります。

+0

ありがとうございました! :-) – Snox

関連する問題