HMAC 政策

本页面适用于 ApigeeApigee Hybrid

查看 Apigee Edge 文档。

政策图标

此政策计算并选择性地验证基于哈希的消息身份验证代码 (HMAC)。HMAC 有时称为“密钥消息身份验证代码”或“密钥哈希”,它使用诸如 SHA-1、SHA-224、SHA-256、SHA-384、SHA-512 或 MD-5之类的加密哈希函数,与密钥一起应用于“消息”,以在该消息上生成签名或消息身份验证代码。此处的“消息”一词指任何字节流。在典型的 HMAC 用法中,消息的发送者向接收者发送消息及其 HMAC,而接收者可以使用 HMAC 以及共享密钥对消息进行身份验证。

此政策为标准政策,可部署到任何环境类型。如需了解政策类型以及在每种环境类型中的可用性,请参阅政策类型

如需详细了解 HMAC,请参阅 HMAC:用于消息身份验证的键控哈希技术 (rfc2104)

示例

生成 HMAC

<HMAC name='HMAC-1'>

  <Algorithm>SHA256</Algorithm>

  <!-- the default encoding of the SecretKey is UTF-8 -->
  <SecretKey encoding='base64' ref='private.secretkey'/>

  <IgnoreUnresolvedVariables>true|false</IgnoreUnresolvedVariables> <!-- optional -->

  <!--
    The Message element accepts a template, which means the "message" the policy operates
    on can include fixed and multiple variable parts, including newlines and static functions.
    Whitespace, such as newlines and space characters, is significant.
   -->
  <Message>Fixed Part
{a_variable}
{timeFormatUTCMs(timeFormatString1,system.timestamp)}
{nonce}</Message>

  <!-- default encoding is base64 -->
  <Output encoding='base16'>name_of_variable</Output>

</HMAC>

验证 HMAC

<HMAC name='HMAC-1'>

  <Algorithm>SHA256</Algorithm>

  <!-- the default encoding of the SecretKey is UTF-8 -->
  <SecretKey encoding='base16' ref='private.secretkey'/>

  <IgnoreUnresolvedVariables>true|false</IgnoreUnresolvedVariables> <!-- optional -->

  <!--
     The Message element accepts a template. This policy verifies an HMAC on the request content.
   -->
  <Message>{request.content}</Message>

  <!--
    VerificationValue is optional.
    Include it to perform an HMAC check.
  -->
  <VerificationValue encoding='base16' ref='expected_hmac_value'/>

  <!-- default encoding of the output is base64 -->
  <Output encoding='base16'>name_of_variable</Output>

</HMAC>

签名的计算和验证过程完全相同。HMAC 政策会计算 HMAC,并且可以选择根据预期值验证计算出的签名。可选的 VerificationValue 元素(如果存在)会指示政策根据已知值或给定值检查计算出的值。


HMAC 的元素参考

政策参考介绍了 HMAC 政策的元素和特性。

适用于顶级元素的属性

<HMAC name="HMAC" continueOnError="false" enabled="true" async="false">

以下属性是所有政策的父级元素都具有的属性。

属性 说明 默认 Presence
name 政策的内部名称。您可以在名称中使用的字符仅限于 A-Z0-9._\-$ %。但是,Apigee 界面会实施其他限制,例如自动移除非字母数字字符。

(可选)使用 <DisplayName> 元素在 Apigee 界面代理编辑器中给政策添加不同的自然语言名称标签。

不适用 必填
continueOnError 设置为 false 可在政策失败时返回错误。对于大多数政策来说,这一行为符合预期。

设置为 true,即使在政策失败后,仍可以继续执行流。另请参阅:

false 可选
已启用 设置为 true 可实施政策。

设置为 false 可“关闭”政策。即使政策仍附加到某个流,也不会实施该政策。

true 可选
async 此属性已弃用。 false 已弃用

<Algorithm>

<Algorithm>algorithm-name</Algorithm>

指定在计算 HMAC 时使用的哈希算法。

默认 不适用
Presence 必填
类型 字符串
有效值 SHA-1SHA-224SHA-256SHA-384SHA-512MD-5

政策配置接受不区分大小写的算法名称,字母与数字之间可以包含短划线,也可以不包含短划线。例如,SHA256SHA-256sha256 是等效的。

<DisplayName>

<DisplayName>Policy Display Name</DisplayName>

除了用于名称属性之外,还可以用于在 Apigee 界面代理编辑器中给政策添加不同的自然语言名称标签。

默认 如果省略此元素,则会使用政策的名称属性的值。
Presence 可选
类型 字符串

<Message>

<Message>message_template_here</Message>
or
<Message ref='variable_here'/>

此元素指定要签名的消息载荷。此元素的输入支持消息模板(变量替换),允许在运行时添加其他项,例如时间戳、Nonce、标头列表或其他信息。例如:

<Message>Fixed Part
    {a_variable}
    {timeFormatUTCMs(timeFormatString1,system.timestamp)}
    {nonce}
</Message>

消息模板可以包含固定和可变部分,包括换行符和静态函数。空白(例如换行符和空格字符)很重要。

默认 不适用
Presence 必填
类型 字符串
有效值 任何字符串对文本值都有效。如果您提供 ref 特性,它将优先于文本值。该政策将文本值或引用的变量作为消息模板进行评估。

<Output>

<Output encoding='encoding_name'>variable_name</Output>

此元素指定政策应根据计算出的 HMAC 值设置的变量的名称。还指定要用于输出的编码。

默认

默认输出变量为 hmac.POLICYNAME.output

encoding 特性的默认值为 base64

Presence 可选。如果该元素不存在,则政策会设置流变量 hmac.POLICYNAME.output,并采用 base64 编码值。
类型 字符串
有效值

对于编码,有效值包括 hexbase16base64base64url

值不区分大小写;hexbase16 是同义词。

Output 元素的文本值可以是任意有效的流变量名称。

<SecretKey>

<SecretKey encoding='encoding_name' ref='private.secretkey'/>

指定用于计算 HMAC 的密钥。该键从引用的变量中获取,并根据特定编码进行解码。

默认

引用的变量没有默认值;ref 属性是必需的。

如果缺少 encoding 属性,则默认情况下,该政策会使用 UTF-8 对密钥字符串进行解码,以便获取密钥字节。

Presence 必填
类型 字符串
有效值

对于 encoding,有效值为 hexbase16base64utf8。默认为 UTF8。这些值不区分大小写,并且短划线无关紧要。Base16base-16bAse16 相同。Base16Hex 是同义词。

使用编码特性,您可以指定一个包含 UTF-8 可打印字符范围之外的字节的密钥。例如,假设政策配置如下所示:

 <SecretKey encoding='hex' ref='private.encodedsecretkey'/>

假设 private.encodedsecretkey 包含字符串 536563726574313233

在此示例中,密钥字节将解码为 [53 65 63 72 65 74 31 32 33](每个字节以十六进制表示)。再举一例,如果使用 encoding='base64',并且 private.encodedsecretkey 包含字符串 U2VjcmV0MTIz,则会为密钥生成同一组字节。如果没有编码属性,或者编码属性为 UTF8,则字符串值 Secret123 将生成同一组字节。这展示了三种表示相同密钥的不同方式。

<VerificationValue>

<VerificationValue encoding='encoding_name' ref='variable_name'/>
or
<VerificationValue encoding='encoding_name'>string_value</VerificationValue>

(可选)指定验证值以及用于对验证值进行编码的编码方法。该政策使用此编码来解码该值。

默认 不存在默认验证值。如果存在该元素,但缺少 encoding 特性,则政策使用默认编码 base64
Presence 可选
类型 字符串
有效值

编码特性的有效值包括 hexbase16base64base64url。值不区分大小写;hexbase16 是同义词。

VerificationValue 的编码无需与 Output 元素的编码相同。

<IgnoreUnresolvedVariables>

<IgnoreUnresolvedVariables>true|false</IgnoreUnresolvedVariables>

如果您希望在政策中指定的任何引用的变量无法解析时政策抛出错误,则设置为 false。设置为 true 可将任何无法解析的变量视为空字符串 (null)。

IgnoreUnresolvedVariables 布尔值仅会影响消息模板引用的变量。虽然 SecretKeyVerificationValue 可以引用变量,但这两者都需要可解析,因此 ignore 设置不适用于这些元素。

默认 False
Presence 可选
类型 布尔值
有效值 true 或 false

流变量

该政策可以在执行期间设置这些变量。

变量 说明 示例
hmac.policy_name.message 该政策根据有效的消息以及 Message 元素中指定的消息模板的评估结果设置此变量。 hmac.HMAC-Policy.message = "Hello, World"
hmac.policy_name.output Output 元素未指定变量名称时,获取 HMAC 计算的结果。 hmac.HMAC-Policy.output = /yyRjydfP+fBHTwXFgc5AZhLAg2kwCri+e35girrGw4=
hmac.policy_name.outputencoding 获取输出编码的名称。 hmac.HMAC-Policy.outputencoding = base64

常见问题

在某种程度上,HMAC 政策似乎很简单:提供密钥和消息,并在响应中获得计算出的 HMAC。如果获得已知消息和密钥组合的意外 HMAC 值,则使用该政策可能会令人沮丧。本节将说明一些使用说明来解决此类问题。

有两个常见问题会导致 HMAC 在验证期间不匹配:消息中存在空格差异,以及编码和解码存在差异。 后者适用于 SecretKey 元素以及 Output 元素

空白差异

似乎对人无关紧要的差异会影响输出 HMAC 值。例如,假设有一个可以表示为“Secret123”的密钥。使用 UTF-8 解码时,密钥字节为 [53 65 63 72 65 74 31 32 33]。使用该密钥计算消息 abc 上的 HMAC-SHA256 会导致 a7938720fe5749d31076e6961360364c0cd271443f1b580779932c244293bc94。向消息中添加一个空格,使其为 abc<SPACE>(其中 <SPACE> 表示 ASCII 32),这样 HMAC-SHA256 的结果为 274669b2a85d2532da48e2ce3d8e52ee17346d1bcd1a606d87db1934b5ab294b

同样,如果消息为 abc<NEWLINE>(其中 <NEWLINE> 表示 ASCII 10),则 HMAC 为 0780370844ca07f896066837e8230d3b6a775f678a4ae03e6b5e864c674831f5。消息中的细微更改会导致 HMAC 值截然不同。这是设计所致。 这是 HMAC 的预期行为。

要点:请务必确保用于计算原始 HMAC 的消息正文和用于验证 HMAC 的消息正文完全相同。如果 HMAC 的验证器以任何方式(添加任何空白或重新设置文本格式)更改消息载荷,则计算得出的 HMAC 将发生变化。

在政策配置中使用消息模板时请特别小心。例如,此政策配置片段显示了潜在问题:

<HMAC name='HMAC-1'>
    ...
    <!-- the result of this message template will include surrounding whitespace -->
    <Message>
        {request.content}
    </Message>
    ...
       
</HMAC>

评估 <Message> 元素中消息模板的结果将包含消息内容周围的换行符和空格。这可能不符合预期。更好的配置如下所示:

<HMAC name='HMAC-1'>
    ...
    <Message>{request.content}</Message>
    ...
         
</HMAC>

编码差异

对相同密钥材料进行不同解码会产生不同的密钥。假设有一个可表示为“U2VjcmV0S2V5MTIz”的密钥。使用 UTF-8 解码时,以 base16 表示的密钥字节为 [55 32 56 6a 63 6d 56 30 53 32 56 35 4d 54 49 7a]。使用 base64 解码时,密钥字节为 [53 65 63 72 65 74 4b 65 79 31 32 33]。 以不同方式解码源材料会导致不同的密钥,进而导致不同的 HMAC 值。

要点:请务必确保用于计算原始 HMAC 的密钥材料和用于验证 HMAC 的密钥材料完全相同。这可能意味着确保两端使用相同的密钥编码。在 HMAC 政策中,您可以使用 SecretKey 元素上的 encoding 属性来指定密钥编码。

此外,请考虑输出的编码。以 base16 或十六进制编码表示的 27f17e11c8ece93844c5eb5e55161d993368628a214f9a51c25d0185e8ea06e2 的 HMAC-SHA256 与以 base64 编码形式表示的 J/F+Ecjs6ThExeteVRYdmTNoYoohT5pRwl0BhejqBuI= 的 HMAC-SHA256 相同。它们看起来有所不同,但这两个字符串代表相同的值。确保使用相同的编码对最初计算的 HMAC 和验证 HMAC 进行编码。在 HMAC 政策中,您可以使用 Output 元素上的 encoding 属性指定所需的输出编码,并使用 VerificationValue 元素上的同一属性指定验证器的解码方式。

错误参考信息

本部分介绍当此政策触发错误时返回的故障代码和错误消息,以及由 Apigee 设置的故障变量。在开发故障规则以处理故障时,请务必了解此信息。如需了解详情,请参阅您需要了解的有关政策错误的信息处理故障

运行时错误

政策执行时可能会发生这些错误。

故障代码 HTTP 状态 发生的条件
steps.hmac.UnresolvedVariable 401

如果 HMAC 政策中指定的变量为以下任一项,就会出现此错误:

  • 超出范围(在执行政策的特定流中不可用)

  • 无法解析(未定义)
steps.hmac.HmacVerificationFailed 401 HMAC 验证失败;提供的验证值与计算值不匹配。
steps.hmac.HmacCalculationFailed 401 该政策无法计算 HMAC。
steps.hmac.EmptySecretKey 401 密钥变量的值为空。
steps.hmac.EmptyVerificationValue 401 包含验证值的变量为空。

部署错误

在您部署包含此政策的代理时,可能会发生这些错误。

错误名称 HTTP 状态 发生的条件
steps.hmac.MissingConfigurationElement 401 当缺少必需的元素或特性时,就会出现此错误。
steps.hmac.InvalidValueForElement 401 如果算法元素中指定的值不是以下值之一,则会出现此错误:SHA-1SHA-224SHA-256SHA-512MD-5
steps.hmac.InvalidSecretInConfig 401 如果存在明确为 SecretKey 提供的文本值,则会出现此错误。
steps.hmac.InvalidVariableName 401 如果 SecretKey 变量不包含 private 前缀 (private.),则会出现此错误。

故障变量

发生运行时错误时,系统会设置这些变量。如需了解详情,请参阅您需要了解的有关政策错误的信息

变量 地点 示例
fault.name="fault_name" fault_name 是故障名称,如上面的运行时错误表中所列。故障名称是故障代码的最后一部分。 fault.name Matches "UnresolvedVariable"
hmac.policy_name.failed 此政策会在发生故障时设置此变量。 hmac.HMAC-Policy.failed = true

错误响应示例

处理错误时,最佳做法是捕获错误响应的 errorcode 部分。不要依赖 faultstring 中的文本,因为它可能会发生变化。

故障规则示例

<FaultRules>
    <FaultRule name="HMAC Policy Errors">
        <Step>
            <Name>AM-Unauthorized</Name>
            <Condition>(fault.name Matches "HmacVerificationFailed")</Condition>
        </Step>
        <Condition>hmac.HMAC-1.failed = true</Condition>
    </FaultRule>
</FaultRules>