本章介绍如何加密XML文档。
提示:发现在此命名空间中启用SOAP
日志记录非常有用,这样就可以收到有关任何错误的更多信息。
加密的XML文档包括以下元素:
<EncryptedData>
元素,其中包含由随机生成的对称密钥加密的加密数据。(使用对称密钥加密比使用公钥加密更有效。)<EncryptedKey>
元素。每个<EncryptedKey>
元素携带用于加密数据的对称密钥的加密副本;它还包含一个带有公钥的X.509
证书。拥有匹配私钥的接收方可以解密对称密钥,然后解密<EncryptedData>
元素。<?xml version="1.0" encoding="utf-8"?>
<Container xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptedKey>
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
<DigestMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
</EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIICnDCCAYQCAWUwDQYJKo... content omitted</X509Certificate>
</X509Data>
</KeyInfo>
<CipherData>
<CipherValue>J2DjVgcB8vQx3UCy5uejMB ... content omitted</CipherValue>
</CipherData>
<ReferenceList>
<DataReference URI="#Enc-E0624AEA-9598-4436-A154-F746B07A2C55"/>
</ReferenceList>
</EncryptedKey>
<EncryptedData Id="Enc-E0624AEA-9598-4436-A154-F746B07A2C55" Type="http://www.w3.org/2001/04/xmlenc#Content">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"></EncryptionMethod>
<CipherData>
<CipherValue>LmoBK7+nDelTOsC3 ... content omitted</CipherValue>
</CipherData>
</EncryptedData>
</Container>
要创建加密文档,请使用类%XML.Security.EncryptedData
和%XML.Security.EncryptedKey
。这些启用XML的类投影到适当名称空间中的有效<EncryptedData>
和<EncryptedKey>
元素。
创建加密的XML文档的最简单方法如下:
在加密文档之前,必须创建包含要将加密文档发送到的实体的证书的 IRIS凭据集。在这种情况下,不需要(也不应该拥有)关联的私钥。
一个通用容器类必须包括以下内容:
%XML.Security
的属性。<EncryptedData>
元素的EncryptedData
。这个属性将携带加密的数据。
%XML.Security
的属性。被投影为<EncryptedKey>
元素的EncryptedKey
。这些属性将携带相应的密钥信息。
示例如下:
Class XMLEncryption.Container Extends (%RegisteredObject, %XML.Adaptor)
{
Property Data As %XML.Security.EncryptedData(XMLNAME = "EncryptedData");
Property Key As %XML.Security.EncryptedKey(XMLNAME = "EncryptedKey");
Parameter NAMESPACE = "http://www.w3.org/2001/04/xmlenc#";
}
要生成并编写加密文档,请执行以下操作:
为此,通常使用%XML.Writer
将启用XML的对象的输出写入流。
%SYS.X509Credentials
的至少一个实例,将访问要向其提供加密文档的实体的InterSystems IRIS凭据集。为此,请调用此类的GetByAlias()
类方法。例如: set credset=##class(%SYS.X509Credentials).GetByAlias("recipient")
若要运行此方法,必须以该凭据集的OwnerList
中包含的用户身份登录,否则OwnerList
必须为空。
%XML.Security.EncryptedKey
实例。若要创建此类的实例,请使用此类的CreateX509()
类方法。例如: set enckey=##class(%XML.Security.EncryptedKey).Createx509(credset,encryptionOptions,referenceOption)
credset
是%SYS
的实例。x509credentials
在刚刚创建的新窗口中打开。encryptionOptions
是$$$SOAPWSIncludeNone
(还有其他选项,但它们不适用于此场景)。此宏在%soap.inc
包含文件中定义。
referenceOption
指定了对加密元素的引用的性质。这里使用的宏在%soap.inc
包含文件中定义。
%Library.ListOfObjects
实例,并使用其Insert()
方法在刚创建插入%XML.Security.EncryptedKey
实例。%New()
方法创建%XML.Security.EncryptedData
实例。例如: set encdata=##class(%XML.Security.EncryptedData).%New()
%XML.Security.EncryptedData的EncryptStream()
实例方法加密在步骤2中创建的流。例如: set status=encdata.EncryptStream(stream,encryptedKeys)
%XML.Security.EncryptedData
的实例写入此类的相应属性。%XML.Writer
为容器类生成输出。例如,前面显示的CONTAINER
类还包括以下方法:
/// w ##class(XMLEncryption.Container).Demo("E:\temp\SecurityXml.txt")
ClassMethod Demo(filename = "", obj = "")
{
#Include %soap
if (obj = "") {
s obj = ##class(MyApp.Person).%OpenId(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 }
set status = writer.RootObject(obj)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
do stream.Rewind()
set container = ..%New() ; 这就是我们要写出的对象
set cred = ##class(%SYS.X509Credentials).GetByAlias("servercred")
set parts =$$$SOAPWSIncludeNone
set ref = $$$KeyInfoX509Certificate
set key = ##class(%XML.Security.EncryptedKey).CreateX509(cred, parts, ref)
set container.Key = key ; 这个细节取决于类
//需要创建一个键列表(本例中仅为一个)
set keys = ##class(%Collection.ListOfObj).%New()
do keys.Insert(key)
set encdata = ##class(%XML.Security.EncryptedData).%New()
set status = encdata.EncryptStream(stream, keys)
set container.Data = encdata ; 这个细节取决于类
// 为容器写输出
set writer = ##class(%XML.Writer).%New()
set writer.Indent = 1
if (filename'="") {
set status = writer.OutputToFile(filename)
if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
}
set status = writer.RootObject(container)
if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
}
此方法可以接受任何启用XML的类的OREF
;如果没有提供,则使用默认值。
在解密加密的XML
文档之前,必须同时提供以下两项:
要解密加密的XML文档,请执行以下操作:
%XML.Reader
实例打开并使用它打开文档。Document
属性,%XML.Reader
实例。correlation()
方法将<EncryptedKey>
元素或元素与类%XML.Security.EncryptedKey
关联起来。 do reader.Correlate("EncryptedKey","%XML.Security.EncryptedKey")
<EncryptedKey>
元素或多个元素。Next()
方法,该方法通过引用返回一个导入的对象(如果有的话)。if 'reader.Next(.ikey,.status) {
write !,"Unable to import key",!
do $system.OBJ.DisplayError(status)
quit
}
导入的对象是%XML.Security.EncryptedKey
的实例。
创建%Library.ListOfObjects
的实例。
并使用它的Insert()
方法插入%XML.Security.EncryptedKey
的实例。
刚从文档中获得的。
调用类%XML.Security.EncryptedData
的ValidateDocument()
方法
set status=##class(%XML.Security.EncryptedData).ValidateDocument(.doc,keys)
第一个参数(通过引用返回)是在第2步中检索到的DOM的修改版本。
第二个参数是上一步中的键列表。
%XML.Writer
为修改后的DOM生成输出。例如,前面显示的CONTAINER
类包含以下类方法:
ClassMethod DecryptDoc(filename As %String)
{
#Include %soap
set reader = ##class(%XML.Reader).%New()
set status = reader.OpenFile(filename)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
set doc = reader.Document
//获取<Signature>元素
do reader.Correlate("EncryptedKey","%XML.Security.EncryptedKey")
if 'reader.Next(.ikey,.status) {
write !,"无法导入密钥",!
do $system.OBJ.DisplayError(status)
quit
}
set keys = ##class(%Collection.ListOfObj).%New()
do keys.Insert(ikey)
// 以下步骤返回解密的文档
set status = ##class(%XML.Security.EncryptedData).ValidateDocument(.doc,keys)
set writer = ##class(%XML.Writer).%New()
set writer.Indent = 1
do writer.Document(doc)
quit $$$OK
}