从 Bouncy Castle 中的 ASN.1 编码检索 CMSSignedData
Retrieving CMSSignedData from ASN.1 encoding in Bouncy Castle
在以下代码中,我使用 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
}
}
我的问题是如何仅使用此对象的ASN.1编码的字节数组来检索原始CMSSignedData
(想读取原始消息并进行验证)。
我问这个问题的原因是我想解密某个加密和签名的消息。我能够解密这条消息,但它会生成一个 ASN.1 编码的字节数组(它确实对应于我的原始消息),但我无法进一步处理这条解密消息。
您可以使用 类 org.bouncycastle.asn1.cms.ContentInfo
和 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);
另请注意,您必须创建一个CMSSignedData
,并将内容封装在签名中,因此您必须更改为:
CMSSignedData sd = sGen.generate(msg);
为此:
CMSSignedData sd = sGen.generate(msg, true);
在以下代码中,我使用 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
}
}
我的问题是如何仅使用此对象的ASN.1编码的字节数组来检索原始CMSSignedData
(想读取原始消息并进行验证)。
我问这个问题的原因是我想解密某个加密和签名的消息。我能够解密这条消息,但它会生成一个 ASN.1 编码的字节数组(它确实对应于我的原始消息),但我无法进一步处理这条解密消息。
您可以使用 类 org.bouncycastle.asn1.cms.ContentInfo
和 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);
另请注意,您必须创建一个CMSSignedData
,并将内容封装在签名中,因此您必须更改为:
CMSSignedData sd = sGen.generate(msg);
为此:
CMSSignedData sd = sGen.generate(msg, true);