GoogleのKMSとエントラスト証明書を使用してPDF文書への署名

ghovat:

私はGoogleのKMS(秘密鍵がKMSから出て行くことはありません)から秘密鍵を用いて生成されたCAから証明書(トラスト)を使用してPDFドキュメント内の有効な署名を作成しようとしています。[entrustCert、中間、rootCert]:証明書チェーンが行われます

私はこれを実現するために使用していたコードの一部を次に示します。

String DEST = "/tmp/test_file.pdf";
OutputStream outputFile = new FileOutputStream(DEST);

CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate[] chain = new X509Certificate[3];
chain[0] = (X509Certificate) certificateFactory.generateCertificate(entrustCert);
chain[1] = (X509Certificate) certificateFactory.generateCertificate(intermediateCert);
chain[2] = (X509Certificate) certificateFactory.generateCertificate(rootCert);
int estimatedSize = 8192;

PdfReader reader = new PdfReader(contract);

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

PdfStamper stamper = PdfStamper.createSignature(reader, outputStream, '\0');

PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(“reason”);
appearance.setLocation("Amsterdam");
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
appearance.setCertificate(chain[0]);

PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(appearance.getReason());
dic.setLocation(appearance.getLocation());
dic.setContact(appearance.getContact());
dic.setDate(new PdfDate(appearance.getSignDate()));
appearance.setCryptoDictionary(dic);

HashMap<PdfName, Integer> exc = new HashMap<>();
exc.put(PdfName.CONTENTS, (estimatedSize * 2 + 2));
appearance.preClose(exc);

String hashAlgorithm = DigestAlgorithms.SHA256;

BouncyCastleDigest bcd = new BouncyCastleDigest();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, bcd, false);

InputStream data = appearance.getRangeStream();
byte[] hash = DigestAlgorithms.digest(data, MessageDigest.getInstance("SHA-256"));
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);

// Creating signature with Google Cloud KMS
KeyManagementServiceClient client = KeyManagementServiceClient.create();
AsymmetricSignRequest request = AsymmetricSignRequest.newBuilder()
        .setName("path/of/the/key/in/kms")
        .setDigest(Digest.newBuilder().setSha256(ByteString.copyFrom(hash))) 
        .build();
AsymmetricSignResponse r = client.asymmetricSign(request);
byte[] extSignature = r.getSignature().toByteArray();

// Checking if signature is valid
verifySignatureRSA("path/of/the/key/in/kms", hash, extSignature);

sgn.setExternalDigest(extSignature, null, "RSA");
TSAClient tsaClient = new TSAClientBouncyCastle("http://timestamp.entrust.net/...");
estimatedSize += 4192;
byte[] encodedSig = sgn.getEncodedPKCS7(sh, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);
byte[] paddedSig = new byte[estimatedSize];
System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, (new PdfString(paddedSig)).setHexWriting(true));
appearance.close(dic2);

outputStream.writeTo(outputFile);

これはから関数で作成およびデジタル署名を検証する- Googleクラウド:署名検証のために

public static boolean verifySignatureRSA(String keyName, byte[] message, byte[] signature)
    throws IOException, GeneralSecurityException {

  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    com.google.cloud.kms.v1.PublicKey pub = client.getPublicKey(keyName);
    String pemKey = pub.getPem();
    pemKey = pemKey.replaceFirst("-----BEGIN PUBLIC KEY-----", "");
    pemKey = pemKey.replaceFirst("-----END PUBLIC KEY-----", "");
    pemKey = pemKey.replaceAll("\\s", "");
    byte[] derKey = BaseEncoding.base64().decode(pemKey);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
    PublicKey rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);

    Signature rsaVerify = Signature.getInstance("SHA256withRSA");
    rsaVerify.initVerify(rsaKey);
    rsaVerify.update(message);
    return rsaVerify.verify(signature);
  }
}

現在、私は次の問題に実行しています:

  1. すべての署名が無効です。署名が適用されたため、文書が変更または破損しています。
  2. Googleからの署名検証は常にfalseです。
MKL:

壊れたメッセージダイジェスト値

分析ファイル署名-failed.pdf

含まれる署名コンテナ1号のASN.1ダンプでは、すぐに目を打つ:messageDigest彼らがあるべきな属性は、署名された属性のコピーが含まれている、すなわち適切とmessageDigest属性:

    <30 5C>
4172   92: . . . . . . SEQUENCE {
    <06 09>
4174    9: . . . . . . . OBJECT IDENTIFIER messageDigest (1 2 840 113549 1 9 4)
         : . . . . . . . . (PKCS #9)
    <31 4F>
4185   79: . . . . . . . SET {
    <04 4D>
4187   77: . . . . . . . . OCTET STRING, encapsulates {
    <31 4B>
4189   75: . . . . . . . . . SET {
    <30 18>
4191   24: . . . . . . . . . . SEQUENCE {
    <06 09>
4193    9: . . . . . . . . . . . OBJECT IDENTIFIER
         : . . . . . . . . . . . . contentType (1 2 840 113549 1 9 3)
         : . . . . . . . . . . . . (PKCS #9)
    <31 0B>
4204   11: . . . . . . . . . . . SET {
    <06 09>
4206    9: . . . . . . . . . . . . OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
         : . . . . . . . . . . . . . (PKCS #7)
         : . . . . . . . . . . . . }
         : . . . . . . . . . . . }
    <30 2F>
4217   47: . . . . . . . . . . SEQUENCE {
    <06 09>
4219    9: . . . . . . . . . . . OBJECT IDENTIFIER
         : . . . . . . . . . . . . messageDigest (1 2 840 113549 1 9 4)
         : . . . . . . . . . . . . (PKCS #9)
    <31 22>
4230   34: . . . . . . . . . . . SET {
    <04 20>
4232   32: . . . . . . . . . . . . OCTET STRING    
         : . . . . . . . . . . . . . 40 76 BC 3F 05 25 E4 C3    @v.?.%..
         : . . . . . . . . . . . . . 27 AD 78 FA 73 31 4C 1B    '.x.s1L.
         : . . . . . . . . . . . . . 82 97 3D AA 4E 81 72 D6    ..=.N.r.
         : . . . . . . . . . . . . . 23 3C DD 59 D2 82 81 55                            
         : . . . . . . . . . . . . }
         : . . . . . . . . . . . }
         : . . . . . . . . . . }
         : . . . . . . . . . }
         : . . . . . . . . }
         : . . . . . . . }

そして実際、あなたのコードでその理由が明らかになりました。

byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);
[...]
byte[] encodedSig = sgn.getEncodedPKCS7(sh, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);

これら2つの呼び出しがなければならない同じパラメータを有する(追加以外tsaClient最終署名容器が同じ入力を使用して作成されなければならないので、最初のコールで取得した認証された属性(別名署名された属性)が実際に署名されたバイトがあるとされているため、第2の位置)有効。

(あなたの変数名が明確にされていた場合、その問題は、以前に発見されている場合があります。)

このように、署名の属性を修正するために、交換してください

byte[] encodedSig = sgn.getEncodedPKCS7(sh, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);

沿って

byte[] encodedSig = sgn.getEncodedPKCS7(hash, tsaClient, null, null, MakeSignature.CryptoStandard.CMS);

RSAパディング

上記の問題を修正するに働いた、新しい問題が、上がって「内部暗号化ライブラリエラーコードエラー:0x2726」を

分析test_file.pdf

署名者証明書の公開RSAキーを使用してバイト署名を復号することになりました

2D9B224E0894E73B1D3EDEE43E5C34A152057B008518538F3D6DA9C5AC73B54AEF33EB165ED0815F2E7851C86308AAFEC3FC0CD5CA77D7A745C056CB37783B7B51484D9B6C1F6D7E42C2B1C49127CD7D1C3A371D943A5C6F5DDA47C758493D2D3CA7D165B35A1BE4FA590911E801D7026822A9B9D202AE9A671DF4F36D42AAD712D43506EC3607E5AC7CCE23389BE288DD32C9C45B92CAA7225897EFD9F8ECFE2A40007FD6AC8B625239E6E529B7521E2EB652659A8F8B3F7262D46E8A0207A3004FEF48C87FC8A52B632268FDD0888A00AE6A3B303A138B18F28A66108467BFF743A859ECD193ADB52268B1FC531690B99D35D5E68BF804B59E24FCB180FABC

これは明らかにPKCS1v1.5パッド入りのハッシュ値のようには見えません。このように、疑惑の署名者証明書が間違っていると、私たちは基本的にごみを参照するか、署名がすべてではなく、PSSパディングでPKCS1v1.5パディングを使用しませんか。末尾は、BC後者の指標であるが、ごみはまた、終了してもよいですBC

一方、しかし、OPは確認しています:

GoogleのKMSで生成された秘密鍵は、2048ビットのRSAキーPSSパディングである - SHA256ダイジェスト

これは確かに署名を使用して問題を説明する:iTextの5.xがないではない RSASSA-PSSをサポートしています。RSA署名を作成する際に、自動的にPKCS1v1.5パディングを前提。特にそれが生成するCMS署名容器では、署名アルゴリズムはRSASSA-PKCS1-v1_5のあることを示します。このように、任意のバリデータは、署名の検証に失敗します。

明白な選択肢があるに

  • iTextの売春または置き換えることによって、RSASSA-PSSのサポートを追加するいずれかのPdfPKCS7クラスを
  • またはRSASSA-PKCS1-v1_5のキーに切り替えます。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=335022&siteId=1