XMLDSIG Цифровая подпись XML. Какие байты действительно подписаны?

Добрый день всем, прежде всего ВНИМАНИЕ! Я провел много исследований и опубликую код, который, я уверен, кому-то поможет. Я выделил курсивом те части, которые были просто пустыми, а «важные» части оставил обычным текстом.

Короткий вопрос: являются ли байты узла SignedInfo (от [60] до [62]) теми, которые действительно подписываются (это означает "sha1ed", затем RSA-ed, затем B64es и инкрустированные в узел SignatureValue ?"

Длинный вопрос: Проблема заключается в том, чтобы просто выполнить стандартный XMLDSIG для XML-файла, созданного мной (это означает, что он всегда будет выглядеть одинаково). Программное обеспечение должно работать на устройствах Android и Windows Phone 6.1 (в частности, на портативном компьютере CN50 intermec). Последняя часть актуальна, потому что это означает, что я не могу использовать упрощенный способ подписания XML, который предоставляет платформа .NET (я смотрю на вас, библиотека System.Security.Cryptography.Xml). Итак, не имея возможности использовать упомянутую библиотеку, я попытался подписать документ «от руки». Наиболее очевидным доказательством и информацией, ориентированной на новичков, которую я смог найти, является эта страница: http://www.di-mgt.com.au/xmldsig2.html, где они делают именно это: полное пошаговое руководство о том, как подписать XML-документ с помощью конвертированной подписи. Часть канонизации меня не беспокоит, поскольку, как я уже сказал, документ будет создан be, и поэтому я приму меры предосторожности, чтобы не вводить элементы, которые потенциально могут испортить часть расчета хеширования. Тем не менее, я пытался выполнить канонизацию в Java с помощью библиотеки XOM, однако результаты были получены там, где… странно.

Прежде чем я продолжу, заполненный данными неподписанный документ был правильно подписан с помощью библиотеки XMLSEC (доступна здесь http://www.aleksey.com/xmlsec/) с помощью следующей команды:

xmlsec --sign --privkey-pem PEMFILE.pem,PEMFILE.pem --node-xpath "(//*[local-name()='Signature' and namespace-uri()='http://www.w3.org/2000/09/xmldsig#'])[last()]" --id-attr:Id "http://www.w3.org/2000/09/xmldsig#:Signature" --id-attr:Id "http://www.w3.org/2000/09/xmldsig#:SignatureValue" UnsignedDocument.xml > SignedDocument.xml

Кроме того, я выполнил подписание с помощью .NET framework на своем компьютере, используя стандартную .NET 4.0 framework со следующим (Примечание 1: это не настоящий производственный код, но в качестве примера это может кому-то помочь. Note2: Здесь я использовал сертификат PFX, установленный на моем компьютере)

X509Store Certificados = new X509Store(StoreName.My, StoreLocation.CurrentUser);
Certificados.Open(OpenFlags.ReadOnly);

foreach (X509Certificate2 Resultado in Certificados.Certificates)
   if (Resultado.Thumbprint == "16182C4CB0440D86DBD567B6A9C1963C02E41B9A")
     return Resultado;

throw new Exception("No hay un certificado instalado para el RFC que se indicó.");

Подписание выполняется следующим образом

public static void SignXmlFile2(string FileName, string SignedFileName, X509Certificate2 Key)
        {
            // Create a new XML document.
            XmlDocument doc = new XmlDocument();

            // Format the document to ignore white spaces.
            doc.PreserveWhitespace = true;

            // Load the passed XML file using it's name.
            doc.Load(new XmlTextReader(FileName));
            XmlNode root = doc.DocumentElement;

            // Create a SignedXml object.    
            SignedXml signedXml = new SignedXml(doc);

            // Obtenemos el objeto de llave privada del certificado
            RSACryptoServiceProvider Llave = Key.PrivateKey as RSACryptoServiceProvider;
            Llave.ExportParameters(false);
            // Add the key to the SignedXml document. 
            signedXml.SigningKey = Llave;

            // Specify a canonicalization method.
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigC14NWithCommentsTransformUrl;

            // Set the InclusiveNamespacesPrefixList property.
            XmlDsigC14NWithCommentsTransform canMethod = (XmlDsigC14NWithCommentsTransform)signedXml.SignedInfo.CanonicalizationMethodObject;

        // Create a reference to be signed.
            Reference reference = new Reference();
            reference.Uri = "";

        // Add an enveloped transformation to the reference.
            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
            reference.AddTransform(env);

        // Add the reference to the SignedXml object.
            signedXml.AddReference(reference);

            keyInfo.AddClause(new RSAKeyValue((RSA)Llave));
            signedXml.KeyInfo = keyInfo;*/

        /* Creamos el nodo <KeyInfo> con el subnodo <X509Data>, poniendo dentro de éste el 
        certificado, su número de serie y la entidad emisora del mismo (primero estos dos últimos
        como subnodo <X509IssuerSerial>), y agregando todo al objeto firmante. */
            KeyInfoX509Data NodoX509Data = new KeyInfoX509Data();
            NodoX509Data.AddCertificate(Key);
            signedXml.KeyInfo = new KeyInfo();
            signedXml.KeyInfo.AddClause(NodoX509Data);

        // Compute the signature.
            signedXml.GetHashCode();
            signedXml.ComputeSignature();

        // Get the XML representation of the signature and save 
        // it to an XmlElement object.
            XmlElement xmlDigitalSignature = signedXml.GetXml();

            // Append the element to the XML document.
            doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true));

            // Save the signed XML document to a file specified 
            // using the passed string.
            XmlTextWriter xmltw = new XmlTextWriter(SignedFileName, new UTF8Encoding(false));
            doc.WriteTo(xmltw);
            xmltw.Close();
        }

С помощью вышеизложенного я могу получить два эквивалентных документа, и оба правильно подписаны. Итак, у меня есть неподписанный и правильно подписанный документ (полученный из двух источников — C# и XMLSEC), оттуда я знаю правильное значение SHA1 Hash документа (называемое DigestValue) и правильную подпись (называемое SignatureValue). Мне есть с чем сравнивать. Как я уже сказал, я пытался выполнить канонизацию на Java, но смог правильно получить хэш, поэтому вместо того, чтобы биться головой о голову в надежде найти какое-то озарение относительно того, как я все испортил, я просто решил предположим, что входной документ будет канонизирован. И если кто-то скажет: «Это невозможно, потому что разрывы строк изменятся при канонизации», я просто отвечу: в моем входном файле не будет разрывов строк. Еще раз, документ будет создан мной. Следуя этому подходу, я могу получить правильное значение дайджеста неподписанного документа с помощью следующего кода:

ЯВА:

/**
 * Generates SHA-1 digest of the provided data.
 *
 * @param data the data to digest
 * @return SHA-1 digest of the provided data.
 */
public static byte[] sha1Digest(byte[] data) {
  MessageDigest mdSha1 = null;
  try {
    mdSha1 = MessageDigest.getInstance("SHA-1");
  } catch (NoSuchAlgorithmException e1) {
    Log.e(LOG_TAG, "Error initializing SHA1 message digest");
  /*} catch (NoSuchProviderException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();*/
}
  mdSha1.update(data);
  byte[] sha1hash = mdSha1.digest();
  return sha1hash;
}

Это означает, что входной документ канонически эквивалентен документу, подписанному указанными выше методами.

Байты Sha1

[55, -59, -1, 71, -62, 26, 57, 126, 76, 7, 120, 53, -38, -51, 8, 38, 127, -29, 5, 25]

И в B64

N8X/R8IaOX5MB3g12s0IJn/jBRk=

НАЧАЛО ПРОБЛЕМНОЙ ЧАСТИ

Согласно теории (из разных источников) мне нужно вставить дайджест-значение внутрь узла. Мой подход состоял в том, чтобы прочитать байты пустого, канонизированного узла, подобного этому

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue></DigestValue></Reference></SignedInfo><SignatureValue></SignatureValue><KeyInfo><X509Data><X509Certificate></X509Certificate></X509Data></KeyInfo></Signature>

Вышеперечисленное переводится в байты:

[60, 83, 105, 103, 110, 97, 116, 117, 114, 101, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 34, 62, 60, 83, 105, 103, 110, 101, 100, 73, 110, 102, 111, 62, 60, 67, 97, 110, 111, 110, 105, 99, 97, 108, 105, 122, 97, 116, 105, 111, 110, 77, 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 84, 82, 47, 50, 48, 48, 49, 47, 82, 69, 67, 45, 120, 109, 108, 45, 99, 49, 52, 110, 45, 50, 48, 48, 49, 48, 51, 49, 53, 35, 87, 105, 116, 104, 67, 111, 109, 109, 101, 110, 116, 115, 34, 62, 60, 47, 67, 97, 110, 111, 110, 105, 99, 97, 108, 105, 122, 97, 116, 105, 111, 110, 77, 101, 116, 104, 111, 100, 62, 60, 83, 105, 103, 110, 97, 116, 117, 114, 101, 77, 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 114, 115, 97, 45, 115, 104, 97, 49, 34, 62, 60, 47, 83, 105, 103, 110, 97, 116, 117, 114, 101, 77, 101, 116, 104, 111, 100, 62, 60, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 85, 82, 73, 61, 34, 34, 62, 60, 84, 114, 97, 110, 115, 102, 111, 114, 109, 115, 62, 60, 84, 114, 97, 110, 115, 102, 111, 114, 109, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 101, 110, 118, 101, 108, 111, 112, 101, 100, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 34, 62, 60, 47, 84, 114, 97, 110, 115, 102, 111, 114, 109, 62, 60, 47, 84, 114, 97, 110, 115, 102, 111, 114, 109, 115, 62, 60, 68, 105, 103, 101, 115, 116, 77, 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 115, 104, 97, 49, 34, 62, 60, 47, 68, 105, 103, 101, 115, 116, 77, 101, 116, 104, 111, 100, 62, 60, 68, 105, 103, 101, 115, 116, 86, 97, 108, 117, 101, 62, 60, 47, 68, 105, 103, 101, 115, 116, 86, 97, 108, 117, 101, 62, 60, 47, 82, 101, 102, 101, 114, 101, 110, 99, 101, 62, 60, 47, 83, 105, 103, 110, 101, 100, 73, 110, 102, 111, 62, 60, 83, 105, 103, 110, 97, 116, 117, 114, 101, 86, 97, 108, 117, 101, 62, 60, 47, 83, 105, 103, 110, 97, 116, 117, 114, 101, 86, 97, 108, 117, 101, 62, 60, 75, 101, 121, 73, 110, 102, 111, 62, 60, 88, 53, 48, 57, 68, 97, 116, 97, 62, 60, 88, 53, 48, 57, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 62, 60, 47, 88, 53, 48, 57, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 62, 60, 47, 88, 53, 48, 57, 68, 97, 116, 97, 62, 60, 47, 75, 101, 121, 73, 110, 102, 111, 62, 60, 47, 83, 105, 103, 110, 97, 116, 117, 114, 101, 62]

Из этого массива я должен получить байты, соответствующие узлу SignedInfo. Это следующие байты (я выделил жирным шрифтом средние байты узла SignedInfo):

[60, 83, 105, 103, 110, 101, 100, 73, 110, 102, 111, 62, 60, 67, 97, 110, 111, 110, 105, 99, 97, 108, 105, 122, 97 , 116, 105, 111, 110, 77, 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116 , 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 84, 82, 47, 50, 48, 48, 49, 47, 82, 69 , 67, 45, 120, 109, 108, 45, 99, 49, 52, 110, 45, 50, 48, 48, 49, 48, 51, 49, 53, 35, 87, 105, 116, 104, 67 , 111, 109, 109, 101, 110, 116, 115, 34, 62, 60, 47, 67, 97, 110, 111, 110, 105, 99, 97, 108, 105, 122, 97, 116, 105 , 111, 110, 77, 101, 116, 104, 111, 100, 62, 60, 83, 105, 103, 110, 97, 116, 117, 114, 101, 77, 101, 116, 104, 111, 100 , 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51 , 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 114, 115, 97, 45 , 115, 104, 97, 49, 34, 62, 60, 47, 83, 105, 103, 110, 97, 116 , 117, 114, 101, 77, 101, 116, 104, 111, 100, 62, 60, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 85, 82, 73, 61 , 34, 34, 62, 60, 84, 114, 97, 110, 115, 102, 111, 114, 109, 115, 62, 60, 84, 114, 97, 110, 115, 102, 111, 114, 109 , 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51 , 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 101, 110, 118, 101 , 108, 111, 112, 101, 100, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 34, 62, 60, 47, 84, 114, 97, 110, 115, 102 , 111, 114, 109, 62, 60, 47, 84, 114, 97, 110, 115, 102, 111, 114, 109, 115, 62, 60, 68, 105, 103, 101, 115, 116, 77 , 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119 , 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103 , 35, 115, 104, 97, 49, 34, 62, 60, 47, 68, 105, 103, 101, 115, 116, 77, 101, 116, 104, 111, 100, 62, 60, 68, 105, 103, 101, 115, 116, 86, 97, 108, 117, 101, 62 , 60, 47, 68, 105, 103, 101, 115, 116, 86, 97, 108, 117, 101, 62, 60, 47, 82, 101, 102, 101, 114, 101, 110 , 99, 101, 62, 60, 47, 83, 105, 103, 110, 101, 100, 73, 110, 102, 111, 62]

В этот массив байтов я должен вставить байты, соответствующие Sha1 документа, закодированного в B64.

Байты Sha1

[55, -59, -1, 71, -62, 26, 57, 126, 76, 7, 120, 53, -38, -51, 8, 38, 127, -29, 5, 25]

И в B64

N8X/R8IaOX5MB3g12s0IJn/jBRk=

И это обратно в байты:

[78, 56, 88, 47, 82, 56, 73, 97, 79, 88, 53, 77, 66, 51, 103, 49, 50, 115, 48, 73, 74, 110, 47, 106, 66, 82, 107, 61]

Тогда массив SignedInfo с присоединенным хэшем выглядит следующим образом:

[60, 83, 105, 103, 110, 101, 100, 73, 110, 102, 111, 62, 60, 67, 97, 110, 111, 110, 105, 99, 97, 108, 105, 122, 97 , 116, 105, 111, 110, 77, 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116 , 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 84, 82, 47, 50, 48, 48, 49, 47, 82, 69 , 67, 45, 120, 109, 108, 45, 99, 49, 52, 110, 45, 50, 48, 48, 49, 48, 51, 49, 53, 35, 87, 105, 116, 104, 67 , 111, 109, 109, 101, 110, 116, 115, 34, 62, 60, 47, 67, 97, 110, 111, 110, 105, 99, 97, 108, 105, 122, 97, 116, 105 , 111, 110, 77, 101, 116, 104, 111, 100, 62, 60, 83, 105, 103, 110, 97, 116, 117, 114, 101, 77, 101, 116, 104, 111, 100 , 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51 , 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 114, 115, 97, 45 , 115, 104, 97, 49, 34, 62, 60, 47, 83, 105, 103, 110, 97, 116 , 117, 114, 101, 77, 101, 116, 104, 111, 100, 62, 60, 82, 101, 102, 101, 114, 101, 110, 99, 101, 32, 85, 82, 73, 61 , 34, 34, 62, 60, 84, 114, 97, 110, 115, 102, 111, 114, 109, 115, 62, 60, 84, 114, 97, 110, 115, 102, 111, 114, 109 , 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51 , 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103, 35, 101, 110, 118, 101 , 108, 111, 112, 101, 100, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 34, 62, 60, 47, 84, 114, 97, 110, 115, 102 , 111, 114, 109, 62, 60, 47, 84, 114, 97, 110, 115, 102, 111, 114, 109, 115, 62, 60, 68, 105, 103, 101, 115, 116, 77 , 101, 116, 104, 111, 100, 32, 65, 108, 103, 111, 114, 105, 116, 104, 109, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119 , 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 48, 57, 47, 120, 109, 108, 100, 115, 105, 103 , 35, 115, 104, 97, 49, 34, 62, 60, 47, 68, 105, 103, 101, 115, 116, 77, 101, 116, 104, 111, 100, 62, 60, 68, 105, 103, 101, 115, 116, 86, 97, 108, 117, 101, 62 , 78, 56, 88, 47, 82, 56, 73, 97, 79, 88, 53, 77, 66, 51, 103, 49, 50, 115, 48, 73, 74, 110, 47, 106, 66 , 82, 107, 61, 60, 47, 68, 105, 103, 101, 115, 116, 86, 97, 108, 117, 101, 62, 60, 47, 82, 101, 102, 101 , 114, 101, 110, 99, 101, 62, 60, 47, 83, 105, 103, 110, 101, 100, 73, 110, 102, 111, 62]

Приведенный выше массив, преобразованный в строку:

<SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>N8X/R8IaOX5MB3g12s0IJn/jBRk=</DigestValue></Reference></SignedInfo>

Теперь это байты, которые (я думаю) должны быть зашифрованы RSA. Я применил несколько методов подписи:

  1. Java с использованием класса Cipher (Privatekey читается через файл PEM)
  2. Java с использованием класса Signature (Privatekey читается через файл PEM)
  3. C # с использованием библиотек BouncyCastle (в эмуляторе Windows Mobile) (ключ читается через CertificateStore и преобразуется в формат BC)
  4. С# с использованием RSAFormatter (ключ, полученный через хранилище сертификатов Windows)

И в каждом из них я получил один и тот же результат подписи для введенных байтов:

[2, -125, 23, -84, -28, -120, -45, -72, -73, -105, -71, -25, -100, -81, -77, 119, 98, 0, 28, -124, -92, 116, -108, -9, 22, -90, -103, -119, 52, 105, 53, 24, -59, -87, 25, -38, -31, -15, 39, 104, -4, 0, 62, -117, 103, -79, 112, 65, -43, -49, -26, -126, -108, 120, -4, -44, 73, -33, 87, 39, 84, 7, 107, -81, 91, 61, -86, 100, 103, -112, -123, -118, 98, 85, -14, -88, -92, -45, -79, 3, -28, -18, 64, 2, -125, 53, -70, 100, -10, 86, -52, 17, -22, 110, -126, -100, -115, 45, -18, 99, -79, -92, -8, -120, -104, -63, 43, 70, -41, 98, 121, -68, -8, 60, -93, -95, -83, 83, -86, 75, -128, 120, -6, -11, 24, -124, 70, -128]

Aomxrosi07i3l7nnnk + zd2iahiskdjt3fqazitrpnrjfqrna4fenapwapotn sxbb1c / mgpr4 / nrj31cnvadrrrr1s9qmrnkiwkylxyqkttsqpk7kacgzw6zpzq zbhqbokcjs3uy7gk + iiywstg12j5vpg8o6gtu6plghj69rieroa =

Поскольку я получаю тот же результат для одних и тех же входных данных, ошибка заключается не в процедуре подписания, а в данных, которые я ввожу. И тут я не могу найти решение. Кстати, подпись действительна, если я проверяю ее как строку с соответствующим открытым ключом или сертификатом... ошибка возникает, когда я присоединяю узел подписи к исходному документу и проверяю его как XMLDSIG. Подпись принимает в основном три входа. Байты для подписи. Алгоритм закрытого ключа. Ключ и алгоритм в порядке (я думаю), тогда проблема должна быть в подписываемых байтах, однако я не могу найти, что я делаю неправильно. Если кто-то может помочь мне, я буду очень благодарен.

Я включаю некоторый код ниже, чтобы показать методы, которые я использую для получения одной и той же подписи на разных языках и устройствах.

Это общедоступный статический байт JAVA. Строка stringAFirmar = новая строка (bSignedInfo, "ISO-8859-1");

            //ONE WAY OF SIGNING
            byte[] signedInfoSha1Digest = sha1Digest(bSignedInfo);
            //byte[] bytesCS = new byte[]{55,-59,-1,71,-62,26,57,126,76,7,120,53,-38,-51,8,38,127,-29,5,25};
            String vSignedInfoSha1DigestString64 = Base64.encodeToString(signedInfoSha1Digest, Base64.DEFAULT);
            byte[] signedInfoDerSha1Digest = mergeArrays(DER_SHA1_DIGEST_IDENTIFIER, signedInfoSha1Digest);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] signatureBytes = cipher.doFinal(signedInfoDerSha1Digest);
            String base64RsaSignature1 = base64encode(signatureBytes, true);
            //String vFirma = bytesToHex(signatureBytes);

            //ANOTHER WAY OF SIGNING
            Signature instance2 = Signature.getInstance("SHA1withRSA");
            instance2.initSign(privateKey);
            instance2.update(bSignedInfo);
            byte[] bFirma3 = instance2.sign();
            String base64RsaSignature2 = base64encode(bFirma3, true);
            Log.i("Log","nada");

            //VALIDATE THE RESULT
            Signature instanceValida = Signature.getInstance("SHA1withRSA");
            instanceValida.initVerify(Certificado);
            instanceValida.update(bSignedInfo);
            if(instanceValida.verify(bFirma3)==true)
                Log.i("Validacion","La firma es valida");
            else
                Log.i("Validacion","La firma NO ES valida");

            return bFirma3;
        } 
        catch (Throwable e) {
            Log.e(LOG_TAG, "Error generating signature for XML", e);
            throw e;
        }
    }

Это С#

public static void SignXmlFile3(string Cadena, X509Certificate2 Key)
        {
            /*opcion 1
            RSACryptoServiceProvider RSA = Key.PrivateKey as RSACryptoServiceProvider;
            RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
             * */

            //Opcion 2
            RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter();
            RSAFormatter.SetKey(Key.PrivateKey as RSACryptoServiceProvider);
            RSAFormatter.SetHashAlgorithm("SHA1");
            SHA1Managed SHhash = new SHA1Managed();
            var bytes3 = Encoding.UTF8.GetBytes(Cadena);
            var bytes = Convert.FromBase64String(Cadena);
            byte[] SignedHashValue = RSAFormatter.CreateSignature(SHhash.ComputeHash(bytes));
            string signature = System.Convert.ToBase64String(SignedHashValue);


        }

Это С# с использованием bouncyCastle на WindowsPhone

public String Sign(String data, String privateModulusHexString, String privateExponentHexString, X509Certificate2 vX509Certificate2)
        {
            //var test = DotNetUtilities.FromX509Certificate(vX509Certificate2);

            /* Make the key */
            RsaKeyParameters key = MakeKey(privateModulusHexString, privateExponentHexString, true);

            /* Init alg */
            ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");

            /* Populate key */
            sig.Init(true, key);

            /* Get the bytes to be signed from the string */
            var bytes = Encoding.UTF8.GetBytes(data);

            /* Calc the signature */
            sig.BlockUpdate(bytes, 0, bytes.Length);
            byte[] signature = sig.GenerateSignature();

            /* Base 64 encode the sig so its 8-bit clean */
            var signedString = Convert.ToBase64String(signature);

            return signedString;
        }

person SonMauri    schedule 13.06.2014    source источник
comment
Вам нужно выполнить вход в WinCE с помощью .NET CF?   -  person Eugene Mayevski 'Callback    schedule 13.06.2014
comment
В первом случае да. Потому что эта машина (CN50) не включает JVM, а все доступные платные программы. Эта стоимость не была включена в первоначальное предложение клиенту, поэтому мы не можем добавить ее сейчас. Я считаю, что машина также может запускать код C/C++, но я думаю, что это будет даже сложнее, чем, скажем, в .NET CF.   -  person SonMauri    schedule 13.06.2014
comment
Чтобы ответить на ваш короткий вопрос: документ, на который вы ссылаетесь, показывает, что хешируется/подписывается, а не сам SignedInfo. Это внешние данные как в обертке (оболочке), так и в обернутой подписи. Канонизация также является важной частью. Чтобы избавить себя от хлопот, вы можете использовать нашу библиотеку SecureBlackbox, которая обеспечивает безопасность XML и поддерживает .NET CF.   -  person Eugene Mayevski 'Callback    schedule 13.06.2014
comment
Спасибо, Евгений, но я не понял. Что вы имеете в виду под документом, на который вы ссылаетесь? Кроме того, я знаю, что канонизация очень актуальна, поэтому я начинаю с канонизированного документа, чтобы получить правильное DigestValue (которым я и являюсь). Отсюда я понимаю, что байты, которые действительно зашифрованы RSA, находятся в узле SignedInfo (включая открывающие и закрывающие теги). Я посмотрю на вашу библиотеку, но я хочу действительно понять эту проблему...   -  person SonMauri    schedule 13.06.2014
comment
Документ - тот, что на di-mgt.com.au. Хэш вычисляется по самому документу ИЛИ ссылочному узлу (узлам) или внешнему ресурсу. Подпись RSA делается над хешем. Узел SignedInfo представляет собой контейнер для различной информации.   -  person Eugene Mayevski 'Callback    schedule 13.06.2014
comment
Понятно. Вы говорите, что подпись вычисляется по этой строке? N8X/R8IaOX5MB3g12s0IJn/jBRk= Или это в байтах (они одинаковые) [78, 56, 88, 47, 82, 56, 73, 97, 79, 88, 53, 77, 66, 51, 103, 49, 50 , 115, 48, 73, 74, 110, 47, 106, 66, 82, 107, 61] Потому что это не то, что я нашел в документации. Я попробую еще раз (думаю, я уже пробовал это)   -  person SonMauri    schedule 13.06.2014
comment
Я только что попробовал это. Я подписал строку: N8X/R8IaOX5MB3g12s0IJn/jBRk=; В байтах: [0] 78 [1] 56 [2] 88 [3] 47 [4] 82 [5] 56 [6] 73 [7] 97 [8] 79 [9] 88 [10] 53 [11] 77 [12] 66 [13] 51 [14] 103 [15] 49 [16] 50 [17] 115 [18] 48 [19] 73 [20] 74 [21] 110 [22] 47 [23] 106 [ 24] 66 [25] 82 [26] 107 [27] 61 Затем Sha1ed получает [0] 0 [1] 246 [2] 184 [3] 232 [4] 236 [5] 171 [6] 70 [7] 151 [8] 41 [9] 254 [10] 134 [11] 77 [12] 101 [13] 157 [14] 206 [15] 19 [16] 17 [17] 72 [18] 193 [19] 160 Затем RSAed и, наконец, B64ed получил в конце неправильную подпись   -  person SonMauri    schedule 14.06.2014
comment
Прошу прощения, ввожу вас в заблуждение по поводу второй части (детали со временем забыл). w3.org/TR/xmldsig-core/#sec-CoreGeneration описывает процедуру и вы правы, SignedInfo целиком хешируется.   -  person Eugene Mayevski 'Callback    schedule 14.06.2014
comment
Спасибо. Мы собираемся использовать вашу библиотеку (вероятно), потому что я протестировал ее, и она работала нормально. Однако мне очень хотелось бы знать, как, черт возьми, рассчитывается подпись! Я просто не могу понять, что я делаю не так.   -  person SonMauri    schedule 16.06.2014


Ответы (1)


Ответ не может быть проще. Проблема была просто в SignedInfo, которую я передал для хеширования. Правильная signedInfo в моем случае была такой:

<ds:SignedInfo xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"

xmlns:cac="урна:оазис:имена:спецификация:ubl:схема:xsd:CommonAggregateComponents-2" xmlns:cbc="урна:оазис:имена:спецификация:ubl:схема:xsd:CommonBasicComponents-2" xmlns:ccts= "urn:un:unece:uncefact:documentation:2" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ext="urn:oasis:names:specification:ubl: схема:xsd:CommonExtensionComponents-2" xmlns:qdt="урна:оазис:имена:спецификация:ubl:схема:xsd:QualifiedDatatypes-2" xmlns:sac="урна:sunat:имена:спецификация:ubl:peru:schema: xsd:SunatAggregateComponents-1" xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">jRNx5uoRrC2UcrSNyJjdZRTB8 / о =

Все включенные пространства имен распространяются от самого родительского узла.

Так что все...

ПД. Я пытался канонизировать в JAVA с помощью библиотеки XOM, однако помимо прочего эта библиотека просто удалила большую часть пространств имен из начального узла.

person SonMauri    schedule 25.06.2014