'Bouncycastle how to envelop the signed digest it in the original document with java?
In connection with this issue: https://github.com/bcgit/bc-java/issues/1021
I am looking for the best method to envelope the signed hash of a document inside the original document.
Here I try to describe as clearly as possible the use case I am encountering with the CADES type signature:
Here the code i use, but the the validation of the signature is always "The signature is not valid or corrupted":
public class EnvelopedHashToFileCades {
public static ASN1ObjectIdentifier SIGNATURE_ALGORITHM_OID = PKCSObjectIdentifiers.sha256WithRSAEncryption;
private static Provider provider;
private static JcaX509CertificateConverter certHolderConverter = new JcaX509CertificateConverter().setProvider ("BC");
private static CertificateFactory certFactory;
private static ASN1ObjectIdentifier signatureAlgorithmOid;
static {
provider = new BouncyCastleProvider();
Security.addProvider(provider);
certHolderConverter = new JcaX509CertificateConverter().setProvider("BC");
signatureAlgorithmOid = PKCSObjectIdentifiers.sha256WithRSAEncryption;
try {
certFactory = CertificateFactory.getInstance("X.509", "BC");
} catch (CertificateException | NoSuchProviderException e) {
System.err.println(e.getMessage());
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
Paths.get("target/output").toFile().mkdirs();
java.nio.file.Path pdf = Paths.get("pom.xml");
java.nio.file.Path p7m = Paths.get("target/output", String.format("pom-%s.p7m", System.currentTimeMillis()));
byte[] data = Files.readAllBytes(pdf);
boolean detached = false;
//CadesSignature obj = new CadesSignature();
//byte[] pkcs7 = obj.sign(data, detached);
String encodedCertificateBase64 = args[0];
String encodedSignatureBase64 = args[1];
byte[] pkcs7 = signCADES(data, detached, encodedCertificateBase64, encodedSignatureBase64);
FileOutputStream fos = new FileOutputStream(p7m.toFile());
fos.write(pkcs7);
fos.close();
System.out.println("Done. Result written to " + p7m.toFile());
}
/**
* @param data
* @param detached
* @param encodedCertificateBase64 e' il certificato in base64
* @return
* @throws CMSException
* @throws OperatorCreationException
* @throws IOException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws CertificateException
*/
public static byte[] signCADES(byte[] data, boolean detached, String encodedCertificateBase64, String encodedSignatureBase64) throws CMSException, OperatorCreationException, IOException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException {
/*
* "encodedCert" e' il certificato in base64, come recuperato dalla chiamata GET /certificates/{certificateId}
*/
String encodedCert = "-----BEGIN CERTIFICATE-----\n" + encodedCertificateBase64 + "\n-----END CERTIFICATE-----";
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate signingCertificate = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(encodedCert.getBytes()));
/*
* create the signer
*/
ContentSigner contentSigner = new MyContentSigner(SIGNATURE_ALGORITHM_OID, getDigestAlgorithm(SIGNATURE_ALGORITHM_OID), encodedSignatureBase64);
CMSSignedData signedData = doCadesSign(data, signingCertificate, contentSigner, detached);
System.out.println("\nDetached? : " + signedData.isDetachedSignature());
return signedData.getEncoded();
}
/**
* Builds a CAdES signature for the relevant content according to https://tools.ietf.org/html/rfc3852 and https://tools.ietf.org/html/rfc5126
*
* signedAttrs field must contain 5 attributes:
*
* - contentType (1.2.840.113549.1.9.3) with value 1.2.840.113549.1.7.1 (data)
* - messageDigest (1.2.840.113549.1.9.4)
* - signingCertificateV2 (1.2.840.113549.1.9.16.2.47)
* - signingTime (1.2.840.113549.1.9.5)
* - cmsAlgorithmProtection (1.2.840.113549.1.9.52), see RFC6211
*
* e.g. https://lapo.it/asn1js/#MYIBbzAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTAxMDEyMDI5MzJaMC0GCSqGSIb3DQEJNDEgMB4wDQYJYIZIAWUDBAIBBQChDQYJKoZIhvcNAQELBQAwLwYJKoZIhvcNAQkEMSIEIJgNoeVtRG13TQ4LVsbwRg5rL4iNPHlxQMoDOtrL2nQJMIHUBgsqhkiG9w0BCRACLzGBxDCBwTCBvjCBuwQgGL-vc5NGF_AYwdnVdyQpHSELDEuDzxkX9GCjiIhI-A0wgZYwgY6kgYswgYgxCzAJBgNVBAYTAklUMRUwEwYDVQQKDAxJTkZPQ0VSVCBTUEExIjAgBgNVBAsMGUNlcnRpZmljYXRvcmUgQWNjcmVkaXRhdG8xFDASBgNVBAUTCzA3OTQ1MjExMDA2MSgwJgYDVQQDDB9JbmZvQ2VydCBGaXJtYSBRdWFsaWZpY2F0YSAyIENMAgMC17w
*
*
* @param data
* @param signingCertificate
* @param contentSigner
* @param detached
* @return
* @throws CMSException
* @throws CertificateEncodingException
* @throws OperatorCreationException
* @throws IOException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
*/
private static CMSSignedData doCadesSign(byte[] data, X509Certificate signingCertificate, ContentSigner contentSigner, boolean detached) throws CMSException, CertificateEncodingException, OperatorCreationException, IOException, NoSuchAlgorithmException, NoSuchProviderException {
List<X509Certificate> certs = Arrays.asList(signingCertificate);
DigestCalculatorProvider digestCalculatorProvider = new BcDigestCalculatorProvider();
/*
* The DefaultSignedAttributeTableGenerator build a signedAttributes object with:
*
* - contentType (1.2.840.113549.1.9.3)
* - messageDigest (1.2.840.113549.1.9.4)
* - signingTime (1.2.840.113549.1.9.5)
* - cmsAlgorithmProtection (1.2.840.113549.1.9.52)
*
* We need to add the signingCertificateV2 attribute
*
*/
Hashtable<ASN1ObjectIdentifier, Attribute> attributes = new Hashtable<>();
ASN1ObjectIdentifier digestAlgorithm = getDigestAlgorithm(contentSigner.getAlgorithmIdentifier().getAlgorithm());
Attribute sc = getSigningCertificateAttribute(digestAlgorithm, signingCertificate);
attributes.put(sc.getAttrType(), sc);
CMSAttributeTableGenerator signedAttributeTableGenerator = new DefaultSignedAttributeTableGenerator(new AttributeTable(attributes));
SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculatorProvider).
setSignedAttributeGenerator(signedAttributeTableGenerator).
build(contentSigner, signingCertificate);
// CMSSignedDataGenerator handles the generation of a pkcs7-signature message
CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator();
cmsGenerator.addCertificates(new JcaCertStore(certs));
cmsGenerator.addSignerInfoGenerator(signerInfoGenerator);
CMSTypedData cmsData= new CMSProcessableByteArray(data);
CMSSignedData signedData = cmsGenerator.generate(cmsData, !detached);
return signedData;
}
/**
* Method to build signing-certificate signed attribute.
*
* @param digestAlgorithm
* the digest algorithm to be used
* @param certificate
* The signing certificate to be append
* @throws IOException
* @throws CertificateEncodingException
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
*/
public static Attribute getSigningCertificateAttribute(ASN1ObjectIdentifier digestAlgorithm, X509Certificate certificate) throws CertificateEncodingException, IOException, NoSuchAlgorithmException, NoSuchProviderException {
X509CertificateHolder certHolder = new X509CertificateHolder(certificate.getEncoded());
X500Name issuerX500Name = certHolder.getIssuer();
GeneralName generalName = new GeneralName(issuerX500Name);
GeneralNames generalNames = new GeneralNames(generalName);
BigInteger serialNumber = certHolder.getSerialNumber();
IssuerSerial issuerSerial = new IssuerSerial(generalNames, serialNumber);
byte[] certDigest = MessageDigest.getInstance(digestAlgorithm.getId(), "BC").digest(certificate.getEncoded());
ESSCertIDv2 essCertIdv2 = new ESSCertIDv2(new AlgorithmIdentifier(digestAlgorithm), certDigest, issuerSerial);
SigningCertificateV2 signingCertificateV2 = new SigningCertificateV2(essCertIdv2);
return new Attribute(id_aa_signingCertificateV2, new DERSet(signingCertificateV2));
}
/**
* @param signatureAlgorithm
* @return
*/
public static ASN1ObjectIdentifier getDigestAlgorithm(ASN1ObjectIdentifier signatureAlgorithm) {
if (PKCSObjectIdentifiers.sha256WithRSAEncryption.equals(signatureAlgorithm)) {
return NISTObjectIdentifiers.id_sha256;
}
throw new IllegalArgumentException("unsupported signature algorithm " + signatureAlgorithm);
}
/**
* An implementation of the BouncyCastle ContentSigner interface
*
*
* @author YYI9386
*
*/
private static class MyContentSigner implements ContentSigner {
private AlgorithmIdentifier signatureAlgorithm;
private MessageDigest messageDigest;
private String encodedSignatureBase64;
private ByteArrayOutputStream dataToSign;
/**
*
*/
public MyContentSigner(ASN1ObjectIdentifier signatureAlgorithmId, ASN1ObjectIdentifier digestAlorithmId, String encodedSignatureBase64) {
String signatureAlgorithmName = new DefaultAlgorithmNameFinder().getAlgorithmName(signatureAlgorithmId);
signatureAlgorithm = (new DefaultSignatureAlgorithmIdentifierFinder()).find(signatureAlgorithmName);
try {
messageDigest = MessageDigest.getInstance(digestAlorithmId.getId(), "BC");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new IllegalArgumentException("Unsupported digest algorithm " + digestAlorithmId.getId());
}
this.encodedSignatureBase64 = encodedSignatureBase64;
}
@Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return signatureAlgorithm;
}
@Override
public OutputStream getOutputStream() {
dataToSign = new ByteArrayOutputStream();
return dataToSign;
}
@Override
public byte[] getSignature() {
byte[] data = dataToSign.toByteArray();
System.out.println("\n*** The dataToSign are:\n" + Base64.getEncoder().encodeToString(data));
byte[] hash = messageDigest.digest(data);
System.out.println("\n*** The hash to sign is:\n" + Base64.getEncoder().encodeToString(hash) + "\n");
/*
* "encoded" e' il valore della firma in base64, come recuperato dalla chiamata POST /certificates/{certificateId}/sign
*/
//String encoded = CadesSignature.readValue("Enter base64 encoding of the signature value");
String encoded = this.encodedSignatureBase64;
return Base64.getDecoder().decode(encoded);
}
}
}
any advise o suggestion i smore than welcome.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|