javascript - Node .js/SAML : How to decrypt contents of RequestedSecurityToken

标签 javascript node.js encryption saml passport.js

我正在使用 passport-wsfed-saml2 , WS-fed 和 SAML2 协议(protocol)的通行证策略。

逻辑的 WS-fed 部分似乎无法处理 RequestedSecurityToken内容为 <xenc:EncryptedData> 的元素元素。

这使得该策略与指定了加密证书的 ADFS 2.0 依赖方不兼容。

我想猴子修补策略的 WsFederation.extractToken带有一些解密逻辑的方法。

下面是 RequestSecurityTokenResponse 的示例我想预处理的 xml。我应该如何解密 token ?具体来说,我如何使用 <KeyInfo> 中提供的信息?元素结合 <xenc:CipherData>元素访问明文 token 数据。

<?xml version="1.0" encoding="UTF-8"?>
<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
  <t:Lifetime>
    <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-01-05T21:02:07.193Z</wsu:Created>
    <wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-01-05T22:02:07.193Z</wsu:Expires>
  </t:Lifetime>
  <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <wsa:Address>https://localhost</wsa:Address>
    </wsa:EndpointReference>
  </wsp:AppliesTo>
  <t:RequestedSecurityToken>
    <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
      <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
          <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
          </e:EncryptionMethod>
          <KeyInfo>
            <o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
              <X509Data>
                <X509IssuerSerial>
                  <X509IssuerName>CN=token-signing, OU=SomeOrg, O=EvilCorp, L=Williston, S=VT, C=US</X509IssuerName>
                  <X509SerialNumber>13135613350938963680</X509SerialNumber>
                </X509IssuerSerial>
              </X509Data>
            </o:SecurityTokenReference>
          </KeyInfo>
          <e:CipherData>
            <e:CipherValue>v6ueRi+G+s31b9RZxE1X8gfNWk6qC9EWimhmDQzLOl/9HQrToqcLRNVqpdocfAgAGp3RkyR9IcwED7PZkreNNzEYMN3pntqS1372Nk6EEYwJSVmWkXmsv4m+xeJvGPQrDIZOwlq22OBt0EAwXoq7LvkmF0s/uhB4TItD47iAsDOFThMpuPoYo0EDLgPWzHtrZqTsC33c10zKKgyynSJPAKaC/+a9mSc4uxq55njU4GLVP/p4FvubPF2U1j4I7ozRGGWsAD5iTGwIOIF7H/ftKoRGIoFen29Ud87mm00BrF0GSUzcxTX+isMfI+HWp8u9zaO1ZLge5+x12BJcVWOYwblTQ7IPWyCMmaUscGgQPZ82ROrMCbX2f6HcGHtl8rwzXbz/VfAZkkxXZAfq9NRjSIRcmVtwC4cjwPAAcwE6V8+lvFn/2dUgzSz9y5K4HpzWZc2jg91oyzhFV+5luC+NV2HPAtTshjWOWhAcVuZYdINfcU1rSHKirBtDPQjxEWcyxkGyrl6UfWq1sEDuaXBPVNWT9/jyjuf7Rzyxnype8SleTK197FnD+rq6NzG9H4MpTFwhgokiPx4/RONjog7I1qnNM5wFybJ5WvkSh+x1w1w7/CNGipJSXCy3swGuSgSF3LI1bUZSzL+JqhUmYq8EVxW31TPe7JbBwMdvnGl7e6Q=</e:CipherValue>
          </e:CipherData>
        </e:EncryptedKey>
      </KeyInfo>
      <xenc:CipherData>
        <xenc:CipherValue>.... whole bunch of base64 encoded data ....</xenc:CipherValue>
      </xenc:CipherData>
    </xenc:EncryptedData>
  </t:RequestedSecurityToken>
  <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
  <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
  <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
</t:RequestSecurityTokenResponse>

最佳答案

<RequestedSecurityToken>元素包含 <EncryptedData>元素。 EncryptedData 元素由三部分组成:

1. EncryptionMethod,在我的例子中是aes256-cbc :
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
2. KeyInfo,在我的例子中这是 AES 加密 key ,但该 key 已使用 ADFS 中配置的加密证书进行了加密。我们需要使用加密证书的 RSA 私钥解密 key ,为我们提供可用于解密 SAML 安全 token 的 AES 加密 key /密码:
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
    <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
    </e:EncryptionMethod>
    <KeyInfo>
      <o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <X509Data>
          <X509IssuerSerial>
            <X509IssuerName>CN=token-signing, OU=SomeOrg, O=EvilCorp, L=Williston, S=VT, C=US</X509IssuerName>
            <X509SerialNumber>13135613350938963680</X509SerialNumber>
          </X509IssuerSerial>
        </X509Data>
      </o:SecurityTokenReference>
    </KeyInfo>
    <e:CipherData>
      <e:CipherValue>v6ueRi+G+s31b9RZxE1X8gfNWk6qC9EWimhmDQzLOl/9HQrToqcLRNVqpdocfAgAGp3RkyR9IcwED7PZkreNNzEYMN3pntqS1372Nk6EEYwJSVmWkXmsv4m+xeJvGPQrDIZOwlq22OBt0EAwXoq7LvkmF0s/uhB4TItD47iAsDOFThMpuPoYo0EDLgPWzHtrZqTsC33c10zKKgyynSJPAKaC/+a9mSc4uxq55njU4GLVP/p4FvubPF2U1j4I7ozRGGWsAD5iTGwIOIF7H/ftKoRGIoFen29Ud87mm00BrF0GSUzcxTX+isMfI+HWp8u9zaO1ZLge5+x12BJcVWOYwblTQ7IPWyCMmaUscGgQPZ82ROrMCbX2f6HcGHtl8rwzXbz/VfAZkkxXZAfq9NRjSIRcmVtwC4cjwPAAcwE6V8+lvFn/2dUgzSz9y5K4HpzWZc2jg91oyzhFV+5luC+NV2HPAtTshjWOWhAcVuZYdINfcU1rSHKirBtDPQjxEWcyxkGyrl6UfWq1sEDuaXBPVNWT9/jyjuf7Rzyxnype8SleTK197FnD+rq6NzG9H4MpTFwhgokiPx4/RONjog7I1qnNM5wFybJ5WvkSh+x1w1w7/CNGipJSXCy3swGuSgSF3LI1bUZSzL+JqhUmYq8EVxW31TPe7JbBwMdvnGl7e6Q=</e:CipherValue>
    </e:CipherData>
  </e:EncryptedKey>
</KeyInfo>
3. CipherData:SAML 安全 token ,使用 AES 加密。
<xenc:CipherData>
  <xenc:CipherValue>.... whole bunch of base64 encoded data ....</xenc:CipherValue>
</xenc:CipherData>

解决方案

这是猴子修补 passport-wsfed-saml2 库的 WsFederation.extractToken 的代码方法:

import WsFederation = require('passport-wsfed-saml2/lib/passport-wsfed-saml2/wsfederation');
import { createPrivateKey } from 'ursa-purejs';
import { createDecipheriv, randomBytes } from 'crypto';
import { DOMParser } from 'xmldom';
import { readFileSync } from 'fs';

const tokenSigningKey = createPrivateKey(readFileSync('./certs/token-signing.pem'));
const parser = new DOMParser();

WsFederation.prototype.standardExtractToken = WsFederation.prototype.extractToken;
WsFederation.prototype.extractToken = function (this: any, req: string) {
  const token: Element | null = this.standardExtractToken(req);
  // Is the SAML token encrypted?
  if (!token || token.nodeName !== 'xenc:EncryptedData') {
    // no. return it.
    return token;
  }

  // We need to decrypt the SAML token...

  // Grab the CipherValue elements. There will be two:
  //   0. The encryption key for the SAML token, encrypted by ADFS using the rsa-oaep-mgf1p 
  //      algo and the public key of the encryption certificate configured in the relying party.
  //   1. The SAML token, encrypted using the aes-256-cbc algo with the key from #0 ^^^
  const ciphers = token.getElementsByTagNameNS('http://www.w3.org/2001/04/xmlenc#', 'CipherValue');
  const aesPasswordCipher = <string>ciphers[0].textContent;
  const samlTokenCipher = <string>ciphers[1].textContent;

  // Decrypt the password for the SAML token.
  const aesPassword = tokenSigningKey.decrypt(aesPasswordCipher, 'base64');

  // Decrypt the SAML token.
  const decipher = createDecipheriv('aes-256-cbc', aesPassword, randomBytes(16));
  let saml = decipher.update(new Buffer(samlTokenCipher, 'base64'), 'binary', 'utf8');
  saml += decipher.final('utf8');

  // Parse the XML and return the token.
  return parser.parseFromString(saml);
};

有用的链接:https://coolaj86.com/articles/asymmetric-public--private-key-encryption-in-node-js/

关于javascript - Node .js/SAML : How to decrypt contents of RequestedSecurityToken,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41495684/

相关文章:

javascript - jQuery:如何检查数组中是否存在某个值?

javascript - 如何更改 Discord.js 机器人的状态?

android - mp3文件的加密

security - 如何存储加密的设置?

javascript - 为什么 mousedown 不能在 <html> 标签上工作?

javascript - 如何使用 Parse.promise javascript 定义 Promise 数组并获取结果数组 [parse.com]

Javascript 对象混淆

node.js - 如何通过本地网络与 Electron/ express 应用程序共享数据?

node.js - FUNCTION_ERROR_INIT_FAILURE AWS lambda

php - 加密题