PDF firma de documentos con Google KMS y certificado de Entrust

Ghovat:

Estoy tratando de hacer una firma válida en un documento PDF mediante el uso de un certificado de CA (Entrust) generado con una clave privada de Google KMS (clave privada nunca se apaga desde el KMS). La cadena de certificados se hace como: [entrustCert, intermedio, rootCert]

Después de la parte del código que estoy utilizando para hacer que esto ocurra:

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

Esta es la función de la nube de Google - Creación y validación de firmas digitales para la verificación de la firma:

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

Estoy actualmente en ejecución en las siguientes cuestiones:

  1. Cada firma no es válida: El documento ha sido alterado o dañado desde que se aplicó la firma.
  2. La verificación de la firma de Google siempre es falsa.
MKL:

mensaje roto valor de resumen

El análisis de archivo de firma-failed.pdf

En un volcado ASN.1 de la cuestión recipiente una firma contenida enseguida llama la atención: El messageDigestatributo contiene una copia de los atributos firmados como debe ser, es decir, con una adecuada messageDigestatributo:

    <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                            
         : . . . . . . . . . . . . }
         : . . . . . . . . . . . }
         : . . . . . . . . . . }
         : . . . . . . . . . }
         : . . . . . . . . }
         : . . . . . . . }

Y de hecho, en el código de la razón se convierte en claro:

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

Estas dos llamadas deben tener los mismos parámetros (excepto el añadido tsaClienten segunda posición), porque los atributos autentificados (atributos aka firmados) recuperados en la primera convocatoria están los bytes realmente firmados, por lo que el contenedor de la firma final debe ser creado con las mismas entradas que sean válido.

(Si los nombres de las variables habían sido más claro, el problema podría haber sido visto antes.)

Por lo tanto, para fijar los atributos firmados, reemplace

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

por

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

acolchado de RSA

Después de haber trabajado en solucionar el problema anterior, un nuevo problema se presentó, "error de biblioteca criptográfica interna Código de error:. 0x2726"

Análisis de test_file.pdf

Descifrar el bytes de firma utilizando la clave RSA pública del certificado del firmante dio lugar a

2D9B224E0894E73B1D3EDEE43E5C34A152057B008518538F3D6DA9C5AC73B54AEF33EB165ED0815F2E7851C86308AAFEC3FC0CD5CA77D7A745C056CB37783B7B51484D9B6C1F6D7E42C2B1C49127CD7D1C3A371D943A5C6F5DDA47C758493D2D3CA7D165B35A1BE4FA590911E801D7026822A9B9D202AE9A671DF4F36D42AAD712D43506EC3607E5AC7CCE23389BE288DD32C9C45B92CAA7225897EFD9F8ECFE2A40007FD6AC8B625239E6E529B7521E2EB652659A8F8B3F7262D46E8A0207A3004FEF48C87FC8A52B632268FDD0888A00AE6A3B303A138B18F28A66108467BFF743A859ECD193ADB52268B1FC531690B99D35D5E68BF804B59E24FCB180FABC

Es claro que esto no se ve como un valor hash acolchada PKCS1v1.5. Por lo tanto, ya sea el presunto certificado del firmante está mal y vemos esencialmente de basura o la firma no utiliza el relleno PKCS1v1.5 en absoluto, pero en lugar de relleno PSS. El arrastre BCes un indicador para la última pero basura también puede terminar en BC.

Mientras tanto, sin embargo, el PO ha confirmado:

la clave privada generada en Google KMS es la clave RSA de 2048 bits de relleno PSS - SHA256 Digesto

Este hecho explica el problema con la firma: iText 5.x no no admite RSASSA-PSS. Al crear una firma RSA asume automáticamente el relleno PKCS1v1.5; en particular, en el recipiente de la firma CMS genera denota que el algoritmo de firma es RSASSA PKCS1-v1_5. Por lo tanto, cualquier validador fallará la validación de la firma.

Las opciones son obvias a

  • o bien añadir soporte RSASSA-PSS por proxenetismo o reemplazar la iText PdfPKCS7clase
  • o cambie a una clave RSASSA PKCS1-v1_5.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=335025&siteId=1
Recomendado
Clasificación