如何根据 XML 文件生成签名?

How to generate a signature based on an XML file?

请帮我解决以下问题: 我在 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 代码没有说明任何有关 XML 的内容,所以我不知道您是否学到了任何有关移植代码的知识,但是如果您确实在 C# 中使用了 XML那么它就会失败。

总之,你需要直接使用RSA函数。从 RSA.Create() might make a lot of sense in most languages. However, .NET is rather certificate / key based (you perform operations on the key rather than using the key, for better or worse, and private keys are considered part of the certificate that they belong to). So using a constructor to read PKCS#12 开始可能是一个更好的起点。

这就是这个小教程的全部内容。我一秒钟都不相信你认为你的代码会是一个正确的端口,所以重新开始。编程愉快。


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

在 .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);