提问者:小点点

使用手动签名输入创建XAdES


我试图创建一个数字签名的XML文件使用签名从我的ID卡。

我有两部分程序。第一个是从ID获取文件的证书和签名。为此,我使用python PKCS11库,如下所示:

with open("input.xml", "rb") as f:
    data = f.read()

lib = lib('path/to/pkcs11/lib.dylib')
token = lib.get_token('name of token')

with token.open(PIN) as session:
    certificate = None

    for obj in session.get_objects({Attribute.CLASS: ObjectClass.CERTIFICATE}):
        certificate = obj
        der_bytes = certificate[Attribute.VALUE]

    with open('certificate.der', "wb") as f:
        f.write(der_bytes)

    # calculate SHA256 of data
    digest = session.digest(data, mechanism=Mechanism.SHA256)

    for obj in session.get_objects({Attribute.CLASS: ObjectClass.PRIVATE_KEY}):
        private_key = obj

    signature = private_key.sign(digest, mechanism=Mechanism.RSA_PKCS)

    with open('signature', "wb") as f:
        f.write(signature)

生成证书. der签名文件并且工作正常(至少我认为)

对于XML代部分,我使用欧洲的DSS库,Java如下:

DSSDocument toSignDocument = new FileDocument("input.xml");

// Preparing parameters for the XAdES signature
XAdESSignatureParameters parameters = new XAdESSignatureParameters();

// We choose the level of the signature (-B, -T, -LT, -LTA).     
parameters.setSignatureLevel(SignatureLevel.XAdES_BASELINE_B);

// We choose the type of the signature packaging (ENVELOPED, ENVELOPING, DETACHED).
parameters.setSignaturePackaging(SignaturePackaging.ENVELOPED);

// We set the digest algorithm to use with the signature algorithm. You must use the
// same parameter when you invoke the method sign on the token. The default value is SHA256 parameters.setDigestAlgorithm(DigestAlgorithm.SHA256);

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new FileInputStream("certificate.der");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);

// We set the signing certificate
parameters.setSigningCertificate(new CertificateToken(cert));

// Create common certificate verifier
CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();

// Create XAdES service for signature
XAdESService service = new XAdESService(commonCertificateVerifier);

// Get the SignedInfo XML segment that need to be signed.
ToBeSigned dataToSign = service.getDataToSign(toSignDocument, parameters);

File file = new File("signature");
SignatureValue signatureValue = new SignatureValue(SignatureAlgorithm.RSA_SHA256, Files.readAllBytes(file.toPath()));

// We invoke the service to sign the document with the signature value obtained in
// the previous step.
DSSDocument signedDocument = service.signDocument(toSignDocument, parameters, signatureValue);

File signedFile = new File("output.xml");
signedFile.createNewFile();
signedDocument.writeTo(new FileOutputStream(signedFile, false));

这会创建XAdES文件,但当我尝试验证签名(例如使用它)时,它失败了,说签名不完整。

我做错了什么?


共1个答案

匿名用户

您根本不使用dataToSign变量来创建签名值。

您应该做的是使用与创建的证书对应的私钥对消化的dataToSign进行实际签名。即,而不是:

File file = new File("signature");
SignatureValue signatureValue = new SignatureValue(SignatureAlgorithm.RSA_SHA256, Files.readAllBytes(file.toPath()));

你应该做这样的事情(使用上面的示例):

# calculate SHA256 of data
digest = session.digest(dataToSign, mechanism=Mechanism.SHA256)

for obj in session.get_objects({Attribute.CLASS: ObjectClass.PRIVATE_KEY}):
    private_key = obj

signatureValue = private_key.sign(digest, mechanism=Mechanism.RSA_PKCS)

请注意,您不应签署原始文档,而是dataToSign,因为它包含对原始文档的引用(其摘要),而且还签署了参数,以确保符合AdES格式。

我希望这对你有帮助。

问好Aleksandr