In the following piece of code I sign a message using Bouncy Castle:
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
public class Sign {
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String certPath = "certPath";
FileInputStream inPublic = new FileInputStream(certPath);
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) factory.generateCertificate(inPublic);
String keyPrivatePath = "keyPath";
Path path = Paths.get(keyPrivatePath);
Files.readAllBytes(Paths.get(keyPrivatePath));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Files.readAllBytes(Paths.get(keyPrivatePath)));
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);
CMSProcessableByteArray msg = new CMSProcessableByteArray("My message".getBytes());
CMSSignedDataGenerator sGen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privateKey);
sGen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
).build(sha1Signer, cert)
);
CMSSignedData sd = sGen.generate(msg);
CMSTypedData cmsBytes = new CMSProcessableByteArray(sd.getEncoded());
// How to reconstruct a CMSSignedData from cmsBytes again?
byte[] bytes = (byte[]) cmsBytes.getContent();
CMSSignedData retrieved = new CMSSignedData(bytes);
System.out.println(retrieved.getSignedContent()); // Doesn't work, is null
}
}
My question is how to retrieve the original CMSSignedData
(wanting to read the original message, and verify it), using only the byte array of the ASN.1 encoding of this object.
The reason I am asking this, is that I want to decrypt a certain encrypted and signed message. I am able to decrypt this message, but it results in an ASN.1 encoded byte array (which does correspond to my original message), but I am not able to process this decrypted message any further.
You can use the classes org.bouncycastle.asn1.cms.ContentInfo
and org.bouncycastle.asn1.ASN1Sequence
:
CMSTypedData cmsBytes = new CMSProcessableByteArray(sd.getEncoded());
byte[] bytes = (byte[]) cmsBytes.getContent();
// reconstruct CMSSignedData from the byte array
ContentInfo ci = ContentInfo.getInstance(ASN1Sequence.fromByteArray(bytes));
CMSSignedData sig = new CMSSignedData(ci);
Also note that you must create a CMSSignedData
with the content encapsulated in the signature, so you must change this:
CMSSignedData sd = sGen.generate(msg);
To this:
CMSSignedData sd = sGen.generate(msg, true);