验证签名时出错:SigningCertificate属性包含一个或多个不属于证书路径的证书

时间:2016-11-29 13:13:05

标签: java xades4j

我正在使用xades4j,我在尝试验证签名时遇到此异常:

xades4j.verification.SigningCertificateCertsNotInCertPathException: Verification failed for property 'SigningCertificate': SigningCertificate property contains one or more certificates that are not part of the certification path.

以下是我要签名的代码:

public File sign(final X509Certificate x509, final PrivateKey priv, final Element elemToSign, final Document doc, final String fileName, final com.softexpert.crypto.document.Document document, List<X509Certificate> chain) throws Exception {
    final KeyingDataProvider kp = new SEDirectKeyingDataProvider(x509, priv, chain);
    XadesSigningProfile profile = new XadesBesSigningProfile(kp);
    final SESignaturePropertiesProvider propProv = this.getPropertiesProvider(document);

    profile = profile.withSignaturePropertiesProvider(propProv);
    profile = profile.withAlgorithmsProvider(AlgorithmsProvider.class);
    profile = profile.withTimeStampTokenProvider(TimeStampTokenProvider.class);

    final SignerBES signer = (SignerBES) profile.newSigner();
    final IndividualDataObjsTimeStampProperty dataObjsTimeStamp = new IndividualDataObjsTimeStampProperty();
    final DataObjectDesc obj = new EnvelopedXmlObject(elemToSign.getFirstChild()).withDataObjectTimeStamp(dataObjsTimeStamp);

    AllDataObjsCommitmentTypeProperty commitment = null;
    if (document.isProofOfOrigin() != null && document.isProofOfOrigin()) {
        commitment = AllDataObjsCommitmentTypeProperty.proofOfOrigin();
    } else {
        commitment = AllDataObjsCommitmentTypeProperty.proofOfReceipt();
    }

    SignedDataObjects dataObjs = new SignedDataObjects(obj).withCommitmentType(commitment);
    dataObjs = dataObjs.withDataObjectsTimeStamp();

    signer.sign(dataObjs, elemToSign);
    return this.outputDocument(doc, fileName);
}

private SESignaturePropertiesProvider getPropertiesProvider(com.softexpert.crypto.document.Document document) {
    SESignaturePropertiesProvider propertiesProvider = new SESignaturePropertiesProvider();

    if (document.getRole() != null) {
        final SignerRoleProperty signerRole = new SignerRoleProperty().withClaimedRole(document.getRole());
        propertiesProvider.setSignerRole(signerRole);
    }
    final SigningTimeProperty signingTime = new SigningTimeProperty();
    propertiesProvider.setSigningTime(signingTime);

    if (document.getLocalityName() != null && document.getCountry() != null) {
        final SignatureProductionPlaceProperty signatureProductionPlaceProperty = new SignatureProductionPlaceProperty(document.getLocalityName(), document.getCountry());
        propertiesProvider.setSignatureProductionPlaceProperty(signatureProductionPlaceProperty);
    }

    return propertiesProvider;
}

private File outputDocument(final Document doc, String fileName) throws Exception {
    if (!fileName.endsWith(".dsg")) {
        fileName += ".dsg";
    }

    FileOutputStream out = null;
    File f = null;
    try {
        final TransformerFactory tf = TransformerFactory.newInstance();
        f = new File(fileName);
        if (!f.exists()) {
            new File(f.getParent()).mkdirs();
            f.createNewFile();
        }
        out = new FileOutputStream(f);
        tf.newTransformer().transform(new DOMSource(doc), new StreamResult(out));
    } finally {
        if(out != null) {
            try {
                out.close();
            } catch(IOException e) {}
        }
    }

    return f;
}

这是我的验证代码:

try {
    final org.w3c.dom.Document doc = this.getDomDocument();
    doc.getDocumentElement().normalize();

    // Find Signature element
    final NodeList nl = doc.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "Signature");
    final CertStore crls = ... // get CRLS
    final CertStore certs = ... // get intermediate certs                
    final KeyStore ks = ... // get KS from Windows-ROOT
    final PKIXCertificateValidationProvider cvp = new PKIXCertificateValidationProvider(ks, false, certs, crls);
    final XadesVerificationProfile p = new XadesVerificationProfile(cvp);
    p.withTimeStampTokenVerifier(SETimeStampTokenProvider.class);
    final Element signatureElemntNode = (Element) nl.item(0);
    final XadesVerifier verifier = p.newVerifier();
    XAdESVerificationResult verificationResult = verifier.verify(signatureElemntNode, null); // exception is thrown here
}

我已经搜索过此错误,但找不到任何可以帮助我的内容。我该如何解决这个错误?

提前致谢。

2 个答案:

答案 0 :(得分:1)

正如错误消息所述,您的SigningCertificate属性可能具有不属于证书链的证书。此规则在XAdES规范中定义。

关于签名操作:

  • 您从自定义getSigningCertificateChain的{​​{1}}方法返回哪些证书?
  • SEDirectKeyingDataProvider属性的最终XML上的证书元素是什么?

返回的证书应该都是唱歌证书链的一部分,但您不需要返回完整的链。您甚至可以返回仅包含签名证书的列表,只要您使所有中间证书可用于验证(您似乎正在进行)。如果这样做,SigningCertificate属性的验证应该通过。

答案 1 :(得分:0)

基于lgoncalve's answer,我已经改变了我的代码:

签名:我没有使用整个证书链进行签名,而只使用了签名证书,因此SEDirectKeyingDataProvider.getSigningCertificateChain()仅返回签名证书。使用这种方法,我的xml现在只有一个属性<SigningCertificate>,而不是整个链。

验证:使用我上面评论过的方法,我在验证证书时遇到了问题:在我的xml中,只有属性<ds:X509Certificate>中引用了签名证书,所以我无法验证整个链条。要解决这个问题,我必须在调用XAdESVerificationResult result = verifier.verify(signatureElemntNode, null);后使用此代码

for(X509Certificate cert : chain) {         
   result.getSignature().addKeyInfo(cert);
}

使用此代码,链的每个证书都由属性<ds:X509Certificate>引用,我可以获取整个链以验证它是否值得信任。