在签名 XML 上验证签名

Validate signature on signed XML

我已成功修改 this code to sign and validate the signature on an XML file. But I am having trouble then validating the signature on a machine that doesn't have the cert installed. I found this Microsoft 参考以在 C# 中执行此操作,但这似乎还需要安装证书。或者 "Key Container" 只是一个用相同证书签名的可执行文件?有人可以使用 PowerShell 而不是 C# 为我指点资源吗,因为我的 C# 技能很弱! 似乎可以在 XML 上验证签名而无需安装证书。手指交叉。

I am having trouble then validating the signature on a machine that doesn't have the cert installed.

有道理!

public 密钥签名验证的基础是......好吧,public 密钥 部分用于签署某些东西 - 和证书是一种非常常见的验证和分发 public 密钥的方式!

因此,您要么需要 public 使证书(没有私钥)可用,要么将 public 密钥 material 与签名文档一起导出并发送。

我们可以更新 Sign-XML 例程,将证书本身复制到 xml 文档中的签名 blob,如下所示:

function Sign-XML {
    param(
      [xml]$xml, 
      [System.Security.Cryptography.X509Certificates.X509Certificate2]$certificate
    )

    [System.Security.Cryptography.xml.SignedXml]$signedXml = $NULL
    $signedXml = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $xml

    # Grab the PrivateKey from the cert - fail if not present
    if(-not $certificate.HasPrivateKey){
        throw 'Private Key not passed to Sign-XML, cannot sign document'
        return
    }
    $signedXml.SigningKey = $certificate.PrivateKey

    # Add a KeyInfo blob to the SignedXml element
    # KeyInfo blob will hold the certificate and therefore the public key
    $keyInfo = New-Object System.Security.Cryptography.Xml.KeyInfo
    $keyInfo.AddClause((New-Object System.Security.Cryptography.Xml.KeyInfoX509Data -ArgumentList $certificate))
    $signedXml.KeyInfo = $keyInfo

    $Reference = New-Object System.Security.Cryptography.Xml.Reference
    $Reference.Uri = ""

    $env = New-Object System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform

    $Reference.AddTransform($env);
    $signedXml.AddReference($Reference)
    $signedXml.ComputeSignature()

    [System.Xml.XmlElement]$xmlSignature = $signedXml.GetXml()
    #Add signature to end of xml file
    [void]$xml.DocumentElement.AppendChild($xml.ImportNode($xmlSignature, $true))

    if ($xml.FirstChild -is [system.xml.XmlDeclaration]) {
        [void]$xml.RemoveChild($xml.FirstChild);
    }
    $xml
}

这里有 2 点需要注意:

  1. 第二个参数现在采用 X509Certificate2 对象而不是原始密钥容器
  2. 我们在包含证书的文档中的签名信息中添加了一个KeyInfoX509Data子句

现在证书是签名文档的一部分,收件人可以轻松地以编程方式验证签名和 public 密钥:

function Verify-XmlSignature {
    Param (
        [xml]$checkxml,
        [switch]$Force
    )

    # Grab signing certificate from document
    $rawCertBase64 = $signed.DocumentElement.Signature.KeyInfo.X509Data.X509Certificate

    if(-not $rawCertBase64){
        throw 'Unable to locate signing certificate in signed document'
        return
    }

    $rawCert = [convert]::FromBase64String($rawCertBase64)
    $signingCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @(,$rawCert)

    [System.Security.Cryptography.Xml.SignedXml]$signedXml = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $checkxml
    $XmlNodeList = $checkxml.GetElementsByTagName("Signature")
    $signedXml.LoadXml([System.Xml.XmlElement] ($XmlNodeList[0]))
    return $signedXml.CheckSignature($signingCertificate, $Force)
}

$Force 传递给 CheckSignature() 的第二个参数意味着如果用户指定 -Force[,则对证书本身的信任验证不会影响验证例程的结果=19=]