简体   繁体   English

CMSSignedData中的分离签名使用Bouncy Castle进行验证,但不使用java.security.Signature进行验证

[英]Detached signature in CMSSignedData verifies using Bouncy Castle but NOT using java.security.Signature

I have been traversing the web for some days now to find the cause of this but without success. 我已经在网上浏览了几天,以找到造成这种情况的原因,但没有成功。

I am doing this : 我正在这样做

  • generating fresh certificates (signer cert and his parent) 生成新证书(签名证书及其父母)
  • creating signature using CMSSignedDataGenerator ( bouncycastle 1.45 ) 使用CMSSignedDataGenerator创建签名(bouncycastle 1.45
  • verifying it by java.security.Signature -> FAILS 通过java.security.Signature-> FAILS进行验证
  • verifying it by BC classes -> SUCCESS 通过BC类进行验证-> SUCCESS

Facts I found out : 我发现的事实

  • Creation and verification of signature uses same algorithm - SHA1withRSA. 签名的创建和验证使用相同的算法-SHA1withRSA。
  • No exception is thrown anywhere in the process. 在该过程的任何地方都不会引发异常。

Questions : 问题

  • Any idea what I am doing wrong? 知道我在做什么错吗?

Ideas to consider : 要考虑的想法

I think that the cause lies in the generation of the signature but I suppose I am doing it according to docs. 我认为原因在于签名的生成,但我想我根据文档进行了签名。 Or - the validation process may expect some kind of different structure like PKCS1 while the generator provides PKCS7. 或-验证过程可能期望生成器提供PKCS7的某种不同结构,例如PKCS1。 Nothing actually tells me what to search next. 实际上没有什么告诉我下一步要搜索什么。

Here is a piece of code which should illustrate the problem and output (after providing your own certificates and a private key): 这是一段代码,应该说明问题和输出(在提供您自己的证书和私钥之后):

import org.bouncycastle.cms.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertStore;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import static org.bouncycastle.cms.CMSSignedGenerator.DIGEST_SHA1;
import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;


public final class VerifyCMSSignedData {

    private static final byte[] DATA_TO_BE_SIGNED = "data".getBytes();
    private static final String SHA_1_WITH_RSA = "SHA1WithRSA";
    private static final boolean DATA_NOT_ATTACHED = false;
    private static final String COLLECTION_STORE_TYPE = "Collection";

    private static X509Certificate signer;
    private static PrivateKey signerPrivateKey;
    private static X509Certificate parent;

    private VerifyCMSSignedData() {
    }

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

        setUpCertificates();

        shouldVerifySignature();
    }

    public static void shouldVerifySignature() throws Exception {
        CMSSignedData signature = createSignature();

        shouldVerifyBySignature(signature.getEncoded());

        shouldVerifyByBC(signature.getEncoded());
    }

    private static void shouldVerifyByBC(byte[] signatureBytes) {
        boolean verified = false;

        try {
            CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(DATA_TO_BE_SIGNED), signatureBytes);
            CertStore certStore = cms.getCertificatesAndCRLs(COLLECTION_STORE_TYPE, PROVIDER_NAME);
            SignerInformationStore signers = cms.getSignerInfos();
            Collection c = signers.getSigners();
            for (Object aC : c) {
                SignerInformation signer = (SignerInformation) aC;
                Collection certCollection = certStore.getCertificates(signer.getSID());
                Iterator certIt = certCollection.iterator();
                X509Certificate cert = (X509Certificate) certIt.next();
                verified = signer.verify(cert, PROVIDER_NAME);
            }
        } catch (Exception e) {
            e.printStackTrace();
            verified = false;
        }

        System.out.println(verified ? "VERIFIED BY BC" : "! Not verified through BC !");
    }

    private static void shouldVerifyBySignature(byte[] signatureBytes) throws Exception {
        Signature signatureVerifier = Signature.getInstance(SHA_1_WITH_RSA, PROVIDER_NAME);
        signatureVerifier.initVerify(signer.getPublicKey());
        signatureVerifier.update(DATA_TO_BE_SIGNED);
        boolean verified = signatureVerifier.verify(signatureBytes);

        System.out.println(verified ? "VERIFIED BY SIGNATURE CLASS" : "! Not verified by Signature class !");
    }

    private static CMSSignedData createSignature() throws Exception {
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

        gen.addSigner(signerPrivateKey, signer, DIGEST_SHA1);

        List<X509Certificate> allCerts = new ArrayList<>();
        if (parent != null) {
            allCerts.add(parent);
        }
        allCerts.add(signer);

        CertStore store = CertStore.getInstance(
                COLLECTION_STORE_TYPE,
                new CollectionCertStoreParameters(allCerts),
                PROVIDER_NAME
        );

        gen.addCertificatesAndCRLs(store);

        return gen.generate(new CMSProcessableByteArray(DATA_TO_BE_SIGNED), DATA_NOT_ATTACHED, PROVIDER_NAME);
    }

    private static void setUpCertificates() throws Exception {
        // TODO setup your certificates here
    }
}

Output : 输出

! Not verified by Signature class ! 未经签名类验证!

VERIFIED BY BC 由BC验证

Process finished with exit code 0 流程结束,退出代码为0

You are verifying different things. 您正在验证不同的事物。 The verification can never be succesful because Java SHA1withRSA verification requires a RSA PKCS#1_v15 signature, but you have generated a CMS signature 验证永远不会成功,因为Java SHA1withRSA验证需要RSA PKCS#1_v15签名,但是您已生成CMS签名。

CMS encapsulates digital signatures or encrypted messages and some additional elements such as certificates. CMS封装数字签名或加密的消息以及一些其他元素,例如证书。 Your CMS message includes a PCKS#1 signature, but if if you want to verify it directly using the Java Api, note that the signed hash is not computed on the data to be signed. CMS消息包含PCKS#1签名,但是,如果要直接使用Java Api对其进行验证,请注意,不会对要签名的数据计算已签名的哈希。 It contains some additional elements such as the reference to signing certificate in ASN.1 syntax, so you would need to compute the hash in the same way 它包含一些其他元素,例如对ASN.1语法中的签名证书的引用,因此您需要以相同的方式计算哈希

You was wrong here: 您在这里错了:

signatureVerifier.update(DATA_TO_BE_SIGNED); signatureVerifier.update(DATA_TO_BE_SIGNED);

and here: 和这里:

boolean verified = signatureVerifier.verify(signatureBytes); 布尔值已验证= signatureVerifier.verify(signatureBytes);

See pedrofb's answer for more details. 有关更多详细信息,请参见pedrofb的答案。

Modify your shouldVerifyBySignature method to work: 修改您的shouldVerifyBySignature方法以使其工作:

private static void shouldVerifyBySignature(byte[] signatureBytes) throws Exception {
    boolean verified = false;
    CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(DATA_TO_BE_SIGNED), signatureBytes);
    CertStore certStore = cms.getCertificatesAndCRLs(COLLECTION_STORE_TYPE, PROVIDER_NAME);
    SignerInformationStore signers = cms.getSignerInfos();
    Collection c = signers.getSigners();
    for (Object aC : c) {
        SignerInformation si = (SignerInformation) aC;
        Signature signatureVerifier = Signature.getInstance(SHA_1_WITH_RSA, PROVIDER_NAME);
        signatureVerifier.initVerify(signer.getPublicKey());


        signatureVerifier.update(si.getEncodedSignedAttributes());
        verified = signatureVerifier.verify(si.getSignature());
    }

    System.out.println(verified ? "VERIFIED BY SIGNATURE CLASS" : "! Not verified by Signature class !");
}

I hope this will help you! 我希望这能帮到您!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM