尝试从证书中获取签名时格式错误的内容异常

时间:2016-03-08 11:49:50

标签: java security certificate bouncycastle sign

我编写了以下代码,以使用证书存储区中的证书验证文件的签名。但是当我尝试获取它的签名并将其传递给SignedData方法时,我得到了以下异常。

org.bouncycastle.cms.CMSException: Malformed content.
    at org.bouncycastle.cms.CMSUtils.readContentInfo(Unknown Source)
    at org.bouncycastle.cms.CMSUtils.readContentInfo(Unknown Source)
    at org.bouncycastle.cms.CMSSignedData.<init>(Unknown Source)
    at VerifyFinal.main(VerifyFinal.java:65)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.IllegalArgumentException: unknown object in getInstance: org.bouncycastle.asn1.DERApplicationSpecific
    at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source)
    at org.bouncycastle.asn1.cms.ContentInfo.getInstance(Unknown Source)
    ... 9 more

以下是我用来验证文件签名的代码。

Security.addProvider(new BouncyCastleProvider());

            KeyStore msCertStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
            msCertStore.load(null, null);
            X509Certificate cer = ((X509Certificate) msCertStore.getCertificate("Software View Certificate Authority"));
            PublicKey pubKey = cer.getPublicKey();

            byte[] sigToVerify = cer.getSignature();
            Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
            signature.initVerify(pubKey);

            CMSSignedData cms = new CMSSignedData(cer.getSignature());
            Store store = cms.getCertificates();
            SignerInformationStore signers = cms.getSignerInfos();
            Collection c = signers.getSigners();
            Iterator it = c.iterator();
            while (it.hasNext()) {
                SignerInformation signer = (SignerInformation) it.next();
                Collection certCollection = store.getMatches(signer.getSID());
                Iterator certIt = certCollection.iterator();
                X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
                X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
                if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
                    System.out.println("verified");
                }
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }

如果您需要,下面是我签名文件的方式。

 File file = new File("G:\\Projects\\test.zip");
        fin = new FileInputStream(file);
        byte fileContent[] = new byte[(int) file.length()];


        Security.addProvider(new BouncyCastleProvider());

        KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
        ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
        Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());

        //Sign
        PrivateKey privKey = (PrivateKey) key;
        Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        signature.initSign(privKey);
        signature.update(fileContent);

        //Build CMS
        X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
        List certList = new ArrayList();
        CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
        certList.add(cert);
        Store certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(msg, true);

        BASE64Encoder encoder = new BASE64Encoder();

        String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
        System.out.println("Signed content: " + signedContent + "\n");

        String envelopedData = encoder.encode(sigData.getEncoded());
        System.out.println("Enveloped data: " + envelopedData);

来自VOLKERK的评论:

我如何生成签名+数据文件:

public static void main(String[] args) throws Exception {

        // String text = "This is a message";

        // File file = new
        // File("C:\\Users\\mayooranM\\Desktop\\SignatureVerificationTest\\ProcessExplorer.zip");
        // fin = new FileInputStream(file);
        // byte fileContent[] = new byte[(int) file.length()];

        Path filepath = Paths.get("G:\\IntelliJTestProjects\\googleplaces.zip");
        byte[] fileContent = Files.readAllBytes(filepath);

        Security.addProvider(new BouncyCastleProvider());

        KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
        ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
        Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());

        // Sign
        PrivateKey privKey = (PrivateKey) key;
        Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        signature.initSign(privKey);
        signature.update(fileContent);

        // Build CMS
        X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
        List certList = new ArrayList();
        CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
        certList.add(cert);
        Store certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
        gen.addSignerInfoGenerator(
                new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
                        .build(sha1Signer, cert));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(msg, true);

        BASE64Encoder encoder = new BASE64Encoder();

        String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
        System.out.println("Signed content: " + signedContent + "\n");

        String envelopedData = encoder.encode(sigData.getEncoded());
        System.out.println("Enveloped data: " + envelopedData);

        FileOutputStream fos = new FileOutputStream(
                "G:\\IntelliJTestProjects\\SignedZip.zip");
        fos.write(envelopedData.getBytes());
        fos.close();

    }

我如何验证数据:

public static void main(String[] args) {
        try {
            Security.addProvider(new BouncyCastleProvider());

            Path path = Paths
                    .get("G:\\IntelliJTestProjects\\SignedZip.zip");
            byte[] signedContent = Files.readAllBytes(path);

            String output = new String(signedContent);

            System.out.println("output: " + output);

            CMSSignedData cms = new CMSSignedData(Base64.decode(signedContent));
            Store store = cms.getCertificates();
            SignerInformationStore signers = cms.getSignerInfos();
            Collection c = signers.getSigners();
            Iterator it = c.iterator();
            while (it.hasNext()) {
                SignerInformation signer = (SignerInformation) it.next();
                Collection certCollection = store.getMatches(signer.getSID());
                Iterator certIt = certCollection.iterator();
                X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
                X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
                if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
                    System.out.println("verified");
                }
            }

            CMSProcessable origData = cms.getSignedContent() ;
            byte[] originalContent  = (byte[]) origData.getContent();

             ZipInputStream zipStream = new ZipInputStream(new ByteArrayInputStream(originalContent));
                ZipEntry entry = null;
                while ((entry = zipStream.getNextEntry()) != null) {

                    String entryName = entry.getName();

                    FileOutputStream out = new FileOutputStream(entryName);

                    byte[] byteBuff = new byte[4096];
                    int bytesRead = 0;
                    while ((bytesRead = zipStream.read(byteBuff)) != -1)
                    {
                        out.write(byteBuff, 0, bytesRead);
                    }

                    out.close();
                    zipStream.closeEntry();
                }
                zipStream.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

2 个答案:

答案 0 :(得分:6)

第2部分

好的,既然您已经收到了包含pkcs7签名数据的文件,那么让我们尝试检索内容并验证其完整性和有效性。
目标是再次不将整个事物加载到内存中。看起来CMSSignedDataParser可以做到这一点 由于文档说

注意:因为我们处于流模式[...],所以解析器上的方法以适当的顺序调用是很重要的。

所以,让我们先来看看到目前为止我们实际得到了什么。为此,我使用包含行Mary had a little lamb作为输入文件的文本文件,而不是.m4v(或您的情况下为.zip)并将结果传递给http://lapo.it/asn1js/(您必须喜欢这个工具....)

The asn1-structure of the result

因此,实际内容出现在签名数据之前,我们必须按照它们在文件中出现的顺序读取条目。反过来会更容易,但...... 我们的想法是将内容写入目标文件,无论它是否签出。如果它不是只删除文件。 (缺点:如果它包含例如病毒,则可能会触发病毒扫描程序......太糟糕了。我会将处理权交给您。)

public class SignedDataTest {
  ... see Part 1

    private static void verify(Path signedFile, Path extractToFile) throws Exception {
        FileInputStream fis = new FileInputStream(signedFile.toFile());

        DigestCalculatorProvider build = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
        CMSSignedDataParser sp = new CMSSignedDataParser(build, fis);

        // we have to read the whole stream sp.getSignedContent().getContentStream()
        // just copy it to the target file
        Files.copy(sp.getSignedContent().getContentStream(), extractToFile, StandardCopyOption.REPLACE_EXISTING);
        // now we can go on with the other stuff.....

        Store certStore = sp.getCertificates();
        // the examples create a new instance of this for each certificate. 
        // I don't think that's necessary, but you might want to look into that...
        JcaSimpleSignerInfoVerifierBuilder verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC");

        for (Object objSigner : sp.getSignerInfos().getSigners()) {
            SignerInformation signer = (SignerInformation) objSigner;
            // as I understand it, there should be only one match ....but anyways....
            for (Object objMatch : certStore.getMatches(signer.getSID())) {
                X509CertificateHolder certHolder = (X509CertificateHolder) objMatch;
                System.out.print("verifying against " + certHolder.getSubject().toString());
                if (signer.verify(verifier.build(certHolder))) {
                    System.out.println(": verified");
                } else {
                    System.out.println(": no match");
                }
            }
        }
    }
}

那么,这实际上做了什么/测试?它从pkcs7 signedData中获取签名者信息,然后再检查哈希和签名,再次检查 signedData中包含的证书。不够好,我和任何其他攻击者都可以将任何证书放在那里;所以我创建了一个新的KeyPiar为该密钥对生成一个自签名证书,并将任何zip文件放在我喜欢的地方,最好是一个讨厌的网络钓鱼工具。
这很可能是您在代码中使用KeyStore.getInstance("Windows-MY", "SunMSCAPI")的原因;你暗示信任的KeyStore。所以,让我们这样做 我们不是从signedData文件中的数据构建S​​ignerInformationVerifier,而是将现成的verfier传递给该方法。此验证程序使用来自Windows&#34; KeyStore&#34;的证书。顺便说一下:你不能随意混合BC和SunMSCAPI供应商;但是你可以用这种方式混合它们,即让BC检查数据完整性,SunMSCAPI检查散列是否已经被认为值得信任的东西签名。

(对不起,我得走了。我只会发布完整的样本课程;虽然有很多可以说的内容......实际上有人可以写一些关于这个的书......其实就是书籍撰写有关该主题的文章;-))

public class SignedDataTest {

    private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
    private static final String KEYSTORE_TYPE = "pkcs12";
    private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
    private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";

    private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\test.txt");
    private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\test-retrieved.txt");
    private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\test.txt.signed.pkcs7");

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        doForth();
        andBack();
    }

    private static void doForth() throws Exception {
        KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
        ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
        X500PrivateCredential creds = new X500PrivateCredential(
                (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS),
                (PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD)
        );
        createSignature(CONTENT_SRC_PATH, creds, new FileOutputStream(SIGNEDDATA_TARGET_PATH.toFile()));
    }

    private static void andBack() throws Exception {
        KeyStore msCertStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
        msCertStore.load(null, null);
        SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("SunMSCAPI")
                .build(((X509Certificate) msCertStore.getCertificate("Software View Certificate Authority")));
        verify(SIGNEDDATA_TARGET_PATH, CONTENT_TARGET_PATH, verifier);
    }

    private static void verify(Path signedFile, Path extractToFile, SignerInformationVerifier verifier) throws Exception {
        FileInputStream fis = new FileInputStream(signedFile.toFile());

        DigestCalculatorProvider build = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
        CMSSignedDataParser sp = new CMSSignedDataParser(build, fis);

        // we have to read the whole stream sp.getSignedContent().getContentStream()
        // just copy it to the target file
        Files.copy(sp.getSignedContent().getContentStream(), extractToFile, StandardCopyOption.REPLACE_EXISTING);
        // now we can go on with the other stuff.....

        Store certStore = sp.getCertificates();
        // the examples create a new instance of this for each certificate. 
        // I don't think that's necessary, but you might want to look into that...

        for (Object objSigner : sp.getSignerInfos().getSigners()) {
            SignerInformation signer = (SignerInformation) objSigner;
            if (signer.verify(verifier)) {
                System.out.println("verified");
                // now(!) you want to keep the target content file
            } else {
                // actually a "org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value"
                // exception will be thrown in case the contents has been altered
                // So, you will need a try-catch(-finally?) construct to delete the target contents file in such cases....
                System.out.println("no match");
            }
        }
    }

    private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator() {
            {
                addSignerInfoGenerator(
                        new JcaSignerInfoGeneratorBuilder(
                                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
                        ).build(
                                new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
                                creds.getCertificate()
                        )
                );
                addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
                    {
                        add(creds.getCertificate());
                    }
                }));
            }
        };
        try (OutputStream sigOut = gen.open(target, true)) {
            Files.copy(srcfile, sigOut);
        }
    }
}

答案 1 :(得分:3)

第1部分

让我们从代码的一些转换开始。 (我想知道答案能持续多久......)
第1步:这里没有太多进展;只是设置&#34;框架&#34; - 正如你所看到的:是的,我真的正在运行/测试代码; - )

public class SignedDataTest {
    private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
    private static final String KEYSTORE_TYPE = "pkcs12";
    private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
    private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";

    private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\Londo Buttons are melting.m4v");
    private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v");
    private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v.signed.pkcs7");

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        createSignature();
    }

    private static void createSignature() throws Exception {
        byte[] fileContent = Files.readAllBytes(CONTENT_SRC_PATH);

        KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
        ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
        Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD);

        // Sign
        PrivateKey privKey = (PrivateKey)key;
        Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        signature.initSign(privKey);
        signature.update(fileContent);

        // Build CMS
        X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
        List certList = new ArrayList();
        CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
        certList.add(cert);
        Store certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
        gen.addSignerInfoGenerator(
                new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
                .build(sha1Signer, cert));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(msg, true);

        BASE64Encoder encoder = new BASE64Encoder();
        String signedContent = encoder.encode((byte[]) sigData.getSignedContent().getContent());
        System.out.println("Signed content: " + signedContent + "\n");

        String envelopedData = encoder.encode(sigData.getEncoded());
        System.out.println("Enveloped data: " + envelopedData);

        FileOutputStream fos = new FileOutputStream(SIGNEDDATA_TARGET_PATH.toString());
        fos.write(envelopedData.getBytes());
        fos.close();
    }
}

第2步:这可能是最难的;使代码看起来与您的代码不同的转换。花点时间了解我在这里做的事情。我想摆脱一些不必要的东西(例如Base64编码器)并获得更多精简代码。这使调试变得更难,因为我删除了大部分临时变量,&#34;隐藏&#34;它们在初始化程序块中 - doh,该功能的正确名称是什么?)

public class SignedDataTest {

    private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
    private static final String KEYSTORE_TYPE = "pkcs12";
    private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
    private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";

    private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\Londo Buttons are melting.m4v");
    private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v");
    private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v.signed.pkcs7");

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        doForth();
        // doBack();
    }

    private static void doForth() throws Exception {
        KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
        ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
        X500PrivateCredential creds = new X500PrivateCredential(
                (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS),
                (PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD)
        );
        createSignature(CONTENT_SRC_PATH, creds, new FileOutputStream(SIGNEDDATA_TARGET_PATH.toFile()));
    }

    private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
        byte[] fileContent = Files.readAllBytes(CONTENT_SRC_PATH);
        // Sign
        Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        signature.initSign(creds.getPrivateKey());
        signature.update(fileContent);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator() {
            {
                addSignerInfoGenerator(
                        new JcaSignerInfoGeneratorBuilder(
                                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
                        ).build(
                                new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
                                creds.getCertificate()
                        )
                );
                addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
                    {
                        add(creds.getCertificate());
                    }
                }));
            }
        };
        // Build CMS
        CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
        CMSSignedData sigData = gen.generate(msg, true);
        // write raw data instead of base64
        target.write(sigData.getEncoded());
    }
}

步骤3:这可能是最重要的一步:它改变了#34的代码;无法在基本级别上按预期工作&#34;原则上这可能会起作用&#34;。您手动创建签名,然后将该签名作为消息传递给CMSSignedDataGenerator。实际上,您正在创建签名的签名; &#34;真实&#34;内容丢失了。你真正想要做的是创建(文件)内容的签名:

private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {
    byte[] fileContent = Files.readAllBytes(CONTENT_SRC_PATH);

    CMSSignedDataGenerator gen = new CMSSignedDataGenerator() {
            ...
    };
    // Build CMS
    CMSTypedData msg = new CMSProcessableByteArray(fileContent);
    CMSSignedData sigData = gen.generate(msg, true);
    // write raw data instead of base64
    target.write(sigData.getEncoded());
}

步骤4:您将完整内容读入内存。对于大型输入文件,这可能不是一个好主意。

private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {                
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator() {
        ...
        };
        // see https://www.bouncycastle.org/docs/pkixdocs1.4/org/bouncycastle/cms/CMSProcessableFile.html
        CMSProcessableFile msg = new CMSProcessableFile(srcfile.toFile());
        CMSSignedData sigData = gen.generate(msg, true);
        // write raw data instead of base64
        target.write(sigData.getEncoded());
    }

步骤5:内存使用次数:gen.generate(msg, true)true参数表示完整的msg包含在asn1-structure中。当你调用.getEncoded()时,你得到一个完整的asn1结构的字节数组,即你再次在内存中有完整的文件。 RAM很便宜,但无论如何我们都试图避免这种情况。还有另一个名为CMSSignedDataStreamGenerator的生成器似乎提供了我们需要的东西。您可以为它提供一个可以将结果写入的OutputStream,而不是处理字节数组,您可以将OutputStream写入以下内容:

private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {                
    CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator() {
        {
            addSignerInfoGenerator(
                    new JcaSignerInfoGeneratorBuilder(
                            new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
                    ).build(
                            new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
                            creds.getCertificate()
                    )
            );
            addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
                {
                    add(creds.getCertificate());
                }
            }));
        }
    };
    try (OutputStream sigOut = gen.open(target, true)) {
        Files.copy(srcfile, sigOut);
    }
}

创建已签名的邮件非常重要。我将在另一个答案中发布验证部分 - 但必须为真实/生活工作一段时间......

编辑:可能仍有空间发布完整/最终样本类

public class SignedDataTest {

    private static final File KEYSTORE_FILE = new File("c:\\temp\\Software_View_Certificate_Authority.p12");
    private static final String KEYSTORE_TYPE = "pkcs12";
    private static final char[] KEYSTORE_PWD = "foobar".toCharArray();
    private static final String KEYSTORE_ALIAS = "Software View Certificate Authority";

    private static final Path CONTENT_SRC_PATH = Paths.get("c:\\temp\\Londo Buttons are melting.m4v");
    private static final Path CONTENT_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v");
    private static final Path SIGNEDDATA_TARGET_PATH = Paths.get("c:\\temp\\Londo Buttons are melting-retrieved.m4v.signed.pkcs7");

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        doForth();
        //doBack();
    }

    private static void doForth() throws Exception {
        KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE, "BC");
        ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD);
        X500PrivateCredential creds = new X500PrivateCredential(
                (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS),
                (PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD)
        );
        createSignature(CONTENT_SRC_PATH, creds, new FileOutputStream(SIGNEDDATA_TARGET_PATH.toFile()));
    }

    private static void createSignature(Path srcfile, X500PrivateCredential creds, FileOutputStream target) throws Exception {                
        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator() {
            {
                addSignerInfoGenerator(
                        new JcaSignerInfoGeneratorBuilder(
                                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()
                        ).build(
                                new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(creds.getPrivateKey()),
                                creds.getCertificate()
                        )
                );
                addCertificates(new JcaCertStore(new ArrayList<X509Certificate>() {
                    {
                        add(creds.getCertificate());
                    }
                }));
            }
        };
        try (OutputStream sigOut = gen.open(target, true)) {
            Files.copy(srcfile, sigOut);
        }
    }
}