第十八章 签署XML文档

第十八章 签署XML文档

本章介绍如何向XML文档添加数字签名。

关于数字签名文档

数字签名的XML文档包括一个或多个<Signature>元素,每个元素都是数字签名。
每个<Signature>元素对文档中的特定元素进行如下签名:

  • 每个签名元素都有一个ID属性,该属性等于某个唯一值。例如:
<Person xmlns="http://mynamespace" Id="123456789">
  • 一个<Signature>元素包含一个<Reference>元素,它指向该Id,如下所示:
<Reference URI="#123456789">

<Signature>元素是由私钥签名的。此元素包括由签名机构签署的X.509证书。如果已签名文档的接收方信任此签名机构,则接收方可以验证证书,并使用包含的公钥验证签名。

注意: IRIS还支持一种变体,其中有签名的元素有一个名为ID的属性,而不是ID。

下面是一个示例,为了便于阅读,添加了空格:

<?xml version="1.0" encoding="UTF-8"?>
<Person xmlns="http://mynamespace" Id="123456789">
  <Name>Persephone MacMillan</Name>
  <DOB>1976-02-20</DOB>
  <s01:Signature xmlns="http://www.w3.org/2000/09/xmldsig#" 
                 xmlns:s01="http://mynamespace" 
                 s02:Id="Id-BC0B1674-758D-40B9-84BF-F7BAA3AA19F4" 
xmlns:s02="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
      </CanonicalizationMethod>
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1">
      </SignatureMethod>
      <Reference URI="#123456789">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature">
          </Transform>
          <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml1317c14n-20010315">
          </Transform>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
        <DigestValue>FHwW2U58bztLI4cIE/mp+nsBNZg=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>MTha3zLoj8Tg content omitted</SignatureValue>
    <KeyInfo>
      <X509Data>
        <X509Certificate>MIICnDCCAYQCAWUwDQYJ content omitted</X509Certificate>
      </X509Data>
    </KeyInfo>
  </s01:Signature>
</Person>

要创建数字签名,可以使用类%XML.Security.Signature
这是一个支持xml的类,它的投影是适当名称空间中的有效<Signature>元素。

创建数字签名XML文档

要创建数字签名的XML文档,请使用%XML.Writer为一个或多个适当定义的启用了XML的对象生成输出。

在为对象生成输出之前,必须创建所需的签名并将其写入对象,以便可以将信息写入目标。

签名的前提条件

在签署文档之前,必须至少创建一个IRIS凭据集。InterSystems IRIS凭据集是存储在系统管理器数据库中的以下信息集的别名:

  • 包含公钥的证书。证书应由文档接收者信任的签名机构签名。
  • 关联的私钥, IRIS在需要时使用,但从不发送。签名需要私钥。
  • (可选)私钥的密码, IRIS在需要时使用私钥,但从不发送。可以加载私钥,也可以在运行时提供私钥。

启用XML的类的要求

启用XML的类必须包括以下内容:

  • 投影为ID属性的特性。
  • 至少一个类型为%XML.Security的属性。投影为<Signature>元素的签名。(一个XML文档可以包含多个<Signature>元素。)

考虑以下类:

Class XMLEncryption.Simple Extends (%RegisteredObject, %XML.Adaptor)
{

Parameter NAMESPACE = "http://mynamespace";

Parameter XMLNAME = "Person";

Property Name As %String;

Property DOB As %String;

Property PersonId As %String(XMLNAME = "Id", XMLPROJECTION = "ATTRIBUTE");

Property MySig As %XML.Security.Signature(XMLNAME = "Signature");

}

生成和添加签名

要生成和添加数字签名,请执行以下步骤:

  1. 可以选择包含%soap.inc包含文件,该文件定义可能需要使用的宏。
  2. 创建%SYS.X509Credentials的实例在访问相应InterSystems IRIS凭据集。为此,调用%SYS.X509CredentialsGetByAlias()类方法。
classmethod GetByAlias(alias As %String, pwd As %String) as %SYS.X509Credentials
  • alias 别名是证书的别名。
  • pwd 是私钥密码。仅当关联的私钥已加密并且在加载私钥文件时未加载密码时,才需要私钥密码。

若要运行此方法,必须以该凭据集的OwnerList中包含的用户身份登录,否则OwnerList必须为空。

  1. 在使用给定凭据集创建 %XML.Security.Signature的实例。为此,请调用该类的Createx509()类方法:
classmethod CreateX509(credentials As %SYS.X509Credentials, signatureOption As %Integer, referenceOption As %Integer) as %XML.Security.Signature
  • credentials 凭据是刚刚创建%SYS.X509Credentials的实例。
  • signatureOption$$$SOAPWSIncludeNone(还有其他选项,但它们不适用于此方案)
  • referenceOption 指定对符号元素的引用的性质。

这里使用的宏在%soap.inc中定义包括文件。

  1. 获取ID属性的值,对于此签名将点的ID。此详细信息取决于启用XML对象的定义。
  2. 创建%XML.Security.Reference的实例,指向该ID。为此,请调用该类的Create()类方法:
ClassMethod Create(id As %String, algorithm As %String, prefixList As %String)
  • id是该参考应该指向的ID。
  • algorithm 算法应该是以下之一:
    • $$$SOAPWSEnvelopedSignature_","_$$$SOAPWSexcc14n — 使用此版本获取独占规范化。

    • $$$SOAPWSEnvelopedSignature — 这相当于前面的选项。

    • $$$SOAPWSEnvelopedSignature_","_$$$SOAPWSexcc14n — 使用此版本进行包容性规范化。

  1. 对于签名对象,调用AddReference()方法将此引用添加到签名:
Method AddReference(reference As %XML.Security.Reference)
  1. 更新启用XML的类的相应属性以包含签名。
set object.MySig=signature 
  1. 创建%XML.Document的实例,该实例包含序列化为XML的启用了XML的对象。

这是必要的,因为签名必须包括有关签名文档的信息。

注意:本文档不包含空格。

  1. 调用签名对象的SignDocument()方法:
Method SignDocument(document As %XML.Document) As %Status

此方法的参数是刚刚创建的中%XML.Document的实例。SignDocument()方法使用该实例中的信息更新签名对象。

  1. 使用%XML.Writer中为对象生成输出。

注意:生成的输出必须包含与签名中使用的文档相同的空格(或不包含空格)。签名包含文档的摘要,如果将编写器中的缩进属性设置为1,则摘要将与文档不匹配。

例如:

放入到对应的实体类中,有一些属性需要替换

Method WriteSigned(filename As %String = "")
{
#Include %soap
    //创建签名对象
    set cred=##class(%SYS.X509Credentials).GetByAlias("servercred")
    set parts=$$$SOAPWSIncludeNone
    set ref=$$$KeyInfoX509Certificate
    
    set signature=##class(%XML.Security.Signature).CreateX509(cred,parts,ref,.status)
    if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}

    // 获取我们要签名的元素的ID属性;
    set refid=$this.PersonId ; 此详细信息取决于类的结构
    
    // 然后在签名对象中创建对该ID的引用
    set algorithm=$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSc14n
    set reference=##class(%XML.Security.Reference).Create(refid,algorithm)
    do signature.AddReference(reference)

    //设置MySig属性,以便$this具有我们为其生成输出时所需的所有信息
    set $this.MySig=signature ; 此详细信息取决于类的结构
    
    //除了$this之外,我们还需要%XML.Document的一个实例,该实例包含序列化为XML的对象
    set document=..GetXMLDoc($this)

    //使用序列化的XML对象对文档进行签名,这将更新部分签名
    set status=signature.SignDocument(document)
    if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}

    // 写入对象的输出
    set writer=##class(%XML.Writer).%New()
    if (filename'="") {
        set status=writer.OutputToFile(filename)
        if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
    }
    do writer.RootObject($this)
}

前面的实例方法使用以下泛型类方法,该方法可以与任何启用了XML的对象一起使用:

ClassMethod GetXMLDoc2(object) As %XML.Document
{
    //步骤1-将对象作为XML写入流
    set writer=##class(%XML.Writer).%New()
    set stream=##class(%GlobalCharacterStream).%New()
    set status=writer.OutputToStream(stream)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}
    set status=writer.RootObject(object)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}

    //步骤2-从流中提取%XML.Document
    set status=##class(%XML.Document).GetDocumentFromStream(stream,.document)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}
    quit document
}

变体:引用中带有URI=""的数字签名

作为一种变体,签名的<Reference>元素可以具有URI="",这是对包含签名的XML文档根节点的引用。
要通过以下方式创建数字签名:

  1. 可以选择包含%soap.inc包含文件,该文件定义可能需要使用的宏。
  2. 创建%SYS.X509Credentials的实例在访问相应InterSystems IRIS凭据集。为此,请调用%SYS.X509CredentialsGetByAlias()类方法,如前面的步骤所述。
  3. 创建使用给定凭据集的%XML.Security.Signature的实例。为此,请调用该类的CreateX509()类方法,如前面的步骤所述。
  4. 按如下方式创建%XML.Security.X509Data的实例:
set valuetype=$$$KeyInfoX509SubjectName_","_$$$KeyInfoX509Certificate
set x509data=##class(%XML.Security.X509Data).Create(valuetype,cred)

其中,cred%SYS的实例。
x509credentials在之前创建的新窗口中打开。
这些步骤创建了一个<X509Data>元素,其中包含一个<X509SubjectName>元素和一个<X509Certificate>元素。

  1. <X509Data>元素添加到签名的<KeyInfo>元素中,方法如下:
do signature.KeyInfo.KeyInfoClauseList.Insert(x509data)

其中签名是%XML.Security的实例。
x509data%XML.Security.X509Data的实例。

  1. 创建%XML.Security的实例。
    参考如下:
set algorithm=$$$SOAPWSEnvelopedSignature
set reference=##class(%XML.Security.Reference).Create("",algorithm) 
  1. 在步骤6(调用AddReference())中继续上述步骤。

验证数字签名

对于收到的任何数字签名文档,都可以验证签名。不需要具有与文档内容匹配的启用XML的类。

验证签名的前提条件

若要验证数字签名,必须首先为签名者向InterSystems IRIS提供受信任的证书。如果InterSystems IRIS可以验证签名者的证书链(从签名者自己的证书到来自InterSystems IRIS信任的证书颁发机构(CA)的自签名证书),包括中间证书(如果有),则InterSystems IRIS可以验证签名。

验证签名

要验证数字签名的XML文档中的签名,请执行以下操作:

  1. 创建%XML.Reader的实例并使用它打开文档。
  2. 获取阅读器的Document属性。这是 %XML.Document的一个实例。包含作为`DOM的XML文档的文档
  3. 使用阅读器的correlation()方法将<Signature>元素或元素与类%XML.Security.Signature关联起来。
    例如:
 do reader.Correlate("Signature","%XML.Security.Signature")
  1. 遍历文档以读取<Signature>元素或多个元素。为此,可以使用阅读器的Next()方法,该方法通过引用返回一个导入的对象(如果有的话)。
    例如:
if 'reader.Next(.isig,.status) {
    write !,"Unable to import signature",!
    do $system.OBJ.DisplayError(status)
    quit
    }

导入的对象是%XML.Security.Signature的实例

  1. 调用导入签名的ValidateDocument()方法。该方法的参数必须是%XML的实例。先前检索到的文档。
 set status=isig.ValidateDocument(document)

例如:

ClassMethod ValidateDoc(filename As %String) 
{
    set reader=##class(%XML.Reader).%New()
    set status=reader.OpenFile(filename)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
    
    set document=reader.Document
    //获取 <Signature> 元素
    //假设只有一个签名
    do reader.Correlate("Signature","%XML.Security.Signature")
    if 'reader.Next(.isig,.status) {
        write !,"无法导入签名",!
        do $system.OBJ.DisplayError(status)
        quit
    }
    set status=isig.ValidateDocument(document)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
}

变体:引用ID的数字签名

在典型的情况下,<Signature>元素包含一个<Reference>元素,该元素指向文档中其他地方的唯一Id。
InterSystems IRIS还支持一种变体,其中<Reference>元素指向名为ID(而不是ID)的属性。
在这种变体中,需要额外的工作来签署文档和验证文档。

要对文档进行数字签名,请遵循“创建数字签名XML文档”中的步骤,并进行以下更改:

  • 对于支持xml的类,包含一个作为ID属性而不是ID属性投影的属性。
  • 在生成和添加签名时,调用%XMLAddIDs()方法。文档实例。
    在获得序列化的XML文档之后,在调用签名对象的SignDocument()方法之前,执行此操作。
    例如:
    //设置MySig属性,使$this在为其生成输出时拥有所需的所有信息
    set $this.MySig=signature    ; 这个细节取决于类的结构

    //除了$this之外,我们还需要%XML的实例 包含序列化为XML的对象的文档
    set document=..GetXMLDoc($this)

    //***** 当签名引用ID属性时添加步骤 *****
    do document.AddIDs()
    
    //使用序列化的XML对象对文档进行签名,这更新了部分签名
    set status=signature.SignDocument(document)
    if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
  • 当验证文档时,在调用Correlate()之前包含以下步骤:
  1. 调用 %XML.DocumentAddIDs()方法。
  2. 调用XML阅读器的Rewind()方法。

例如:

    set document=reader.Document
    
    //添加签名引用ID属性时的步骤
    do document.AddIDs()
    do reader.Rewind()

    //获取 <Signature> 元素
    do reader.Correlate("Signature","%XML.Security.Signature")
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容