PKCS7验证错误:ASN1数据损坏

时间:2018-10-15 20:31:26

标签: java digital-signature pkcs#7

我正在开发一个Java类,以PKCS7格式签名文本文件。我发现可以使用Sun库而不是BouncyCastle进行此操作,因此我开发了以下方法:

For i = 0 to 20
  Graph1Bar(i).Height = 200
Next

当我尝试在在线验证器https://www.receita.fazenda.gov.br/Aplicacoes/SSL/ATBHE/assinadoc/ValidadorAssinaturas.app/valida.aspx中验证结果文件时,收到以下错误消息:

ASN1损坏的数据

我检查了签名,她很好。我相信错误在于在生成PKCS7结果时进行som数据转换。

有人遇到过这个问题吗?以及如何在Java代码中验证PCKS7结果?

1 个答案:

答案 0 :(得分:0)

我不知道您如何检查签名,除了您的代码中无意义的检查会重复错误之外,但它是错误的数据,并且由于您没有向我们展示AlgoritmoAssinatura也许是错误的方法。您还有其他一些错误。而不是详细说明所有示例,下面是一个产生有效结果并带有注释注释的示例:

// use test data for example
KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream (args[0]), args[1].toCharArray());
PrivateKey PrivKey = (PrivateKey) ks.getKey (args[2], args[1].toCharArray());
X509Certificate Certif = (X509Certificate) ks.getCertificate(args[2]);
String Message = "test";
String ArquivoAssinar = args[3];
String Charset = "ASCII"; // no idea, see below

String SrtResultPKCS7 = "";
byte[] Conteudo = Message.getBytes(Charset);
byte[] Hash;
//String DadosArq = "";
//String Linha = "";
//boolean AssinValid = false;

try {
    // the name in SignerInfo is the _Issuer_ name NOT the Subject
    X500Name xName = X500Name.asX500Name(Certif.getIssuerX500Principal());
    BigInteger serial = Certif.getSerialNumber();
    AlgorithmId digestAlgorithmId = new AlgorithmId(AlgorithmId.SHA_oid);
    AlgorithmId signAlgorithmId = new AlgorithmId(AlgorithmId.RSAEncryption_oid);

    MessageDigest MessDig = MessageDigest.getInstance("SHA1");
    Hash = MessDig.digest(Conteudo);

    PKCS9Attribute Atributo1 = new PKCS9Attribute(PKCS9Attribute.CONTENT_TYPE_OID, ContentInfo.DATA_OID);
    PKCS9Attribute Atributo2 = new PKCS9Attribute(PKCS9Attribute.MESSAGE_DIGEST_OID, Hash); 
    PKCS9Attributes ConjuntoAtrib = new PKCS9Attributes(new PKCS9Attribute[] {Atributo1, Atributo2}); 

    // when using signedattrs, signature is of the encoded attrs 
    // (without the context-implicit tag used when embedded in SignerInfo)
    Signature Sign = Signature.getInstance("SHA1withRSA");
    Sign.initSign(PrivKey);
    Sign.update(ConjuntoAtrib.getDerEncoding());
    byte[] ResultadoAssinatura = Sign.sign();

    SignerInfo sInfo = new SignerInfo(xName, serial, digestAlgorithmId, ConjuntoAtrib, signAlgorithmId, ResultadoAssinatura, null);
    // contenttype inside signed-data is data not digested-data  
    ContentInfo cInfo = new ContentInfo(ContentInfo.DATA_OID, new DerValue(DerValue.tag_OctetString, Conteudo));

    PKCS7 p7 = new PKCS7(new AlgorithmId[] { digestAlgorithmId }, cInfo, new java.security.cert.X509Certificate[] { Certif }, new SignerInfo[] { sInfo });

    ByteArrayOutputStream bOut = new DerOutputStream();
    p7.encodeSignedData(bOut);
    byte[] encoded = bOut.toByteArray();
    // Java doesn't define a class 'Encoder' so I assume this is base64
    SrtResultPKCS7 = DatatypeConverter.printBase64Binary(encoded); // gone in 11!

    FileOutputStream Saida = new FileOutputStream(ArquivoAssinar);
    OutputStreamWriter Escritor = new OutputStreamWriter(Saida, Charset);
    BufferedWriter BuffWriter = new BufferedWriter(Escritor);
    // this was correct for base64 (although the buffering is wasted)
    BuffWriter.write(SrtResultPKCS7);
    // this was nonsense -- it decodes the DER bytes as if they were characters,
    // which they aren't, and then OSW re-encodes them to probably wrong bytes
    //BuffWriter.write(bOut.toString());
    BuffWriter.close();
    // alternatively could write DER/binary with a Stream (NOT a Writer)
}
catch (Exception E) {
    E.printStackTrace();
}

我不清楚您(或链接的网站)需要哪种输出格式。 使用二进制/ DER非常普遍,但是不能被剪切和粘贴,并且更难使用。 DER的Base64很少见,但并非未知。如果您或他们想要由许多软件使用的标准PEM格式,那不只是DER的base64;它是DER PLUS换行符的base64,每64个字符换行,并添加了破折号-BEGIN和破折号-END行。

此外,SHA-1因碰撞而被打破了一年多;请参阅https://shattered.io以及有关密码学,安全性和安全性的大量问答。甚至在此之前,自2014年或2015年以来,包括NIST(美国政府)和CABforum(公共网络证书)在内的许多机构都禁止签名。我对您正在签名的数据一无所知,但是如果它在任何方面都很重要或有价值,并且您可以选择在签名中使用更好的哈希,则应该这样做。

添加:另外,我想您也意识到sun.*类没有记录,不能保证,并且可能会在Oracle感到满意时停止工作。