【问题标题】:How to generate a signature based on an XML file?如何基于 XML 文件生成签名?
【发布时间】:2020-05-25 02:52:24
【问题描述】:

请帮我解决以下问题: 我有一个关于 Java 的代码 java代码如下:

StringBuilder fullText;
KeyStore p12 = KeyStore.getInstance("PKCS12");
p12.load(new FileInputStream("FileName.p12"), "1234".toCharArray());

Key key = (Key) p12.getKey("1", "1234".toCharArray());

//signing
Signature signer = Signature.getInstance("SHA1withRSA");

signer.initSign((PrivateKey) key);
signer.update(fullText.toString().getBytes());
b`yte[] digitalSignature = signer.sign();

String base64sign = new String(Base64.getEncoder().encode(digitalSignature));

我试图在 .Net 平台上重现它。 我在 .NET 3.5 平台上创建了一个代码。 X++上的代码如下:

public static boolean Encrypt(str sXmlDoc)
{
    boolean bSuccess = false;

    System.Security.Cryptography.X509Certificates.X509Certificate2 p12;
    System.Security.Cryptography.AsymmetricAlgorithm key;

    str sBase64Cert;
    str sBase64Xml;
    str sBase64Sign;

    str sTmp;

    System.Byte[] byteArray;
    System.Security.Cryptography.Xml.Signature signer;
    System.Exception  ex;
    str sKeyPublic;
    System.Byte[] keyPublic;
    System.Int32 myInt32;
    int myInt;
    System.Byte[] byteTmp, byteTmp2;
    System.Text.ASCIIEncoding txtEncoder;
    System.Security.Cryptography.Xml.KeyInfo keyInfo;
    System.Security.Cryptography.Xml.SignedXml signedXml;
    System.Xml.XmlDocument xmlDocument;
    System.Xml.XmlElement xmlElement;
    System.Security.Cryptography.Xml.SignedInfo signedInfo;
    System.Security.Cryptography.Xml.Reference reference;
    System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform env;

    System.Security.Cryptography.Xml.RSAKeyValue rsaKeyValue;
    System.Security.Cryptography.RSA rsaKey;

    try
    {


        p12 = new System.Security.Cryptography.X509Certificates.X509Certificate2("fileName.p12", "pass");
        if (p12)
        {
            //Signature

            //TEST
            if (p12.get_HasPrivateKey())
            {
                key = p12.get_PrivateKey();
                rsaKey = p12.get_PrivateKey();

                xmlDocument = new System.Xml.XmlDocument();
                xmlDocument.set_PreserveWhitespace(true); //Allow white spaces
                xmlDocument.LoadXml(sXmlDoc);
                signedXml = new System.Security.Cryptography.Xml.SignedXml(xmlDocument);
                signedXml.set_SigningKey(key);
                keyInfo = new System.Security.Cryptography.Xml.KeyInfo();
                rsaKeyValue = new System.Security.Cryptography.Xml.RSAKeyValue(rsaKey);
                keyInfo.AddClause(rsaKeyValue);
                signedXml.set_KeyInfo(keyInfo);
                // Create a reference to be signed.
                //System.Security.Cryptography.Xml.Reference reference;
                reference = new System.Security.Cryptography.Xml.Reference();
                reference.set_Uri("");
                // Add an enveloped transformation to the reference.
                env = new System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform();
                reference.AddTransform(env);
                // Add the reference to the SignedXml object.
                signedXml.AddReference(reference);
                signedXml.set_KeyInfo(keyInfo);
                signedXml.ComputeSignature();
                xmlElement = signedXml.GetXml();
                signer = new System.Security.Cryptography.Xml.Signature();
                signer = signedXml.get_Signature();
                signedInfo = new System.Security.Cryptography.Xml.SignedInfo();
                signedInfo = signer.get_SignedInfo();
                byteTmp = signer.get_SignatureValue();
                sTmp = System.Convert::ToBase64String(byteTmp);
                sBase64Sign = "<signature>"+sTmp+"</signature>";
                info(sBase64Sign);
            }
        }

    }
    catch (Exception::CLRError)
    {
        ex = ClrInterop::getLastException();
        if (ex != null)
        {
           ex = ex.get_InnerException();
           if (ex != null)
           {
               error(ex.ToString());
           }
        }
    }

    return bSuccess;
}

但结果与我在 java 上得到的不同。我打开了一个 p12 键。我签署了一个 XML 字符串并获得了该 XML 的签名,但得到了错误的字符串。我做错了什么?

【问题讨论】:

    标签: java cryptography .net-3.5 signature x++


    【解决方案1】:

    Java 代码中没有提到 XML,所以我不知道你是否学到了任何关于移植代码的知识,但如果你确实在 C# 中使用 XML,那么它就会失败。

    简而言之,您需要直接使用 RSA 函数。在大多数语言中,以RSA.Create() 开头可能很有意义。但是,.NET 是基于证书/密钥的(无论好坏,您密钥执行操作而不是使用密钥,并且私钥被认为是他们所属的证书)。所以使用a constructor to read PKCS#12 可能是一个更好的起点。

    这就是这个小教程的全部内容。我不相信您认为您的代码将是正确的端口,所以重新开始。快乐编程。


    编辑:哦,最后一个提示:SHA1withRSA 是 RSA,使用 PKCS#1 v1.5 填充来生成签名,使用 SHA-1 作为底层哈希函数(这当然意味着它是 SHATTERED 等等)。

    【讨论】:

    • .NET 上的私钥格式为 RSA-PKCS1-KeyEx
    • 在我的例子中,java的私钥格式是PKCS#8
    • 不,不是,至少不在您提供的问题中。因此,您自己问题的答案也是错误的。很高兴你解决了这个问题,但这对其他人没有帮助,因此我不得不投反对票。
    【解决方案2】:

    在 .NET 上签署 XML 的最佳方式是使用 Bouncy Castle 库。我希望有一天有人会在.NET 框架上解决它,而不使用外部库,但是通过使用BounsyCastle 找到了解决方案。 C# 上的这段代码使用了 BouncyCastle。

    string item = string.Empty;
    
    Pkcs12Store p12 = new Pkcs12Store();
    p12.Load(_p12, _p12_psw.ToCharArray());
    
    string alias = "";
    foreach (string al in p12.Aliases)
    {
        if (p12.IsKeyEntry(al) && p12.GetKey(al).Key.IsPrivate)
        {
            alias = al;
            break;
        }
    }
    
    //signature
    var data = Encoding.UTF8.GetBytes(xmlDoc);
    ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
    signer.Init(true,p12.GetKey(alias).Key);
    signer.BlockUpdate(data, 0, data.Length);
    
    var sign = signer.GenerateSignature();
    
    string base64Sign = Convert.ToBase64String(sign);
    item = "<signature>"+base64Sign+"</signature>", base64Sign);
    

    【讨论】:

    • 首先,您的Java 代码显然不使用基于XML 的签名,因此这不能解决问题中所述 的问题。仅仅说 BC 之所以有效,是因为它提供了 XML sig,无论您怎么看,都不是一个答案。
    猜你喜欢
    • 2018-11-30
    • 2020-08-23
    • 2014-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 2011-11-08
    • 1970-01-01
    相关资源
    最近更新 更多