WSSE - XML SOAP 安全和密钥加密和存储 (EncryptedData/EncryptedKey)

Posted

技术标签:

【中文标题】WSSE - XML SOAP 安全和密钥加密和存储 (EncryptedData/EncryptedKey)【英文标题】:WSSE - XML SOAP security and key encryption and storing (EncryptedData/EncryptedKey) 【发布时间】:2019-01-29 09:35:54 【问题描述】:

最近几天我一直在寻找有关此的文档..

我需要通过 SOAP 发送带有 WSSE 安全标头的 XML,但不知道如何加密和存储加密的密钥

这是一个例子

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">
            <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-1B758D26C51BFCD86614340101135741">
                <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
                <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                    <wsse:SecurityTokenReference>
                        <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIIDODCCAiCgAwIBAgIGAU0FlCVCMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxCYW5rIENvbm5lY3QxFTATBgNVBAsTDEJhbmsgQ29ubmVjdDEdMBsGA1UEAxMUQmFuayBDb25uZWN0IElBLXRlc3QwHhcNMTUwNDI5MTQyODI0WhcNMTgwNDI5MTQyODI0WjAcMRowGAYDVQQDExFiYW5rIGNvbm5lY3QtdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI23KdtaRKPTFTe/A1PnsF9dpSlTiXurKmio0OCgTP9wClHwync3JsInRwGTooA20P9zWobUnEFbEiAgRVYCxuYoldRE6NLhSC854/YTjMBeevH1TNa38lpavGiI4UwFhg70U9/JuYs21hoFyzVfaWlVfOkAMm1U/n4wHq6FZW461S5PY4A/UI1Mr8WgeIHU9GqMBtFvjynzq3SLenOPgdmKtyJ3V8EOU+DlgwKmDbxMVMtYNDZtoQvOWnuvlJ6ICDcqcW7OUkmwCKodjxxPvrdaPxyZDhT7h4FgRtrAOS8qR6L7x9D4ZIoxOMPudGvr99OSb4KVtaAEt/R7hKxG3OsCAwEAAaNCMEAwHwYDVR0jBBgwFoAU680YSkZnx1IaJAmI49LlTGiia0wwHQYDVR0OBBYEFMaWOY7Vf/iB3WVA96j5kRtbF8prMA0GCSqGSIb3DQEBCwUAA4IBAQAJ+bssSFWE6KsYT7HSDKag4Eot7yNGMY4Don/MilDnOREdu20QUS131DKrSkpBQiCXbyRUQjUoun4yue0EG+rlG3QUIlNNdJ4KZJB+dTYdLUV7XTYJNPimKAmoZ+PFNvT1eGgWcMT+MbTfpk0mw0V8IprYGa8UPchd6vtSVwpbTcPc/F4bgUTlm/V+FG4bQS61gF0koj0DEZjzat7CBHpozRgfRlXgwu26vnhWGc99uKH4GAKN4JpqPi/6Yz+7iQNJUC3yeezgBxFrIXuLpkBZSP4zunf9VxsICnxkFUXOTuYBdcbhPNzqMknD5ijFcFRZITwdv7x3uJGLkM7iUfBp</wsse:KeyIdentifier>
                    </wsse:SecurityTokenReference>
                </ds:KeyInfo>
                <xenc:CipherData>
                    <xenc:CipherValue>af9+FhA91ytLwjeRvTYJsRCkhjHmAQGwqYwMBoNZBn7BZhF/a6EUpM9ByarVhx1SRCpjW5fb8tBVuJO1ZkjfTUZ5EAh/oDLbkmwPdSAAVzmAURHwCq3XQgMZV3lAczlLnPamxjjZBCGqxvAmBo1CvFFPC4AcBedqY92mP8XGyVHpS7JYKOxqXK2vUA1by7371x+Mu0aoS2zJPyPLa1IPwOYgR9qicmWz1RNPiEVA8ZBCN0NRyg7FLJxdUcE81z+1SjButBo2j3qcwkNcecHzZAnweY+LSWp3H5JA3WNzUHUuvFHEaPzT5jd7fUI16xo8NLK8/Rd8Eq/zDD+T3baeVQ==</xenc:CipherValue>
                </xenc:CipherData>
                <xenc:ReferenceList>
                    <xenc:DataReference URI="#ED-1B758D26C51BFCD86614340101135852"/>
                </xenc:ReferenceList>
            </xenc:EncryptedKey>
        </wsse:Security>
        <technicalAddress xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"/>
        <activationHeader xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
            <organisationIdentification>
                <mainRegistrationNumber>8079</mainRegistrationNumber>
                <isoCountryCode>DK</isoCountryCode>
            </organisationIdentification>
            <functionIdentification>112233445566778899</functionIdentification>
            <erpInformation/>
            <endToEndMessageId>d28b6a7dad414014a59029ef1a7e84d4</endToEndMessageId>
            <createDateTime>2015-06-11T10:08:33.258+02:00</createDateTime>
        </activationHeader>
    </soap:Header>
    <soap:Body>
        <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-1B758D26C51BFCD86614340101135852" Type="http://www.w3.org/2001/04/xmlenc#Content">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
                    <wsse:Reference URI="#EK-1B758D26C51BFCD86614340101135741"/>
                </wsse:SecurityTokenReference>
            </ds:KeyInfo>
            <xenc:CipherData>
                <xenc:CipherValue>dTSVuEJ90OYguQOsOz2ZtcE2mybwuvVl19pp7/e5yuvNygx3w5v+prpEvbjYLauiIAB3lrVDK2astJeYJGnDbaVJVeU0YqH5ItYVn7Wz36jJM52KB+UNbYo8EdTKYjsZuADzH+tAoA+pwYxGBXMEQctNI+C711HgP2hbpHNYOG7nAMOIrP/0B3FCy+st+9CbYlwAEENreTYunEEA41hciFnWCsIx0el7OeuiA6V51fAmvrF19RPNKwaptvbvmVdKj//RQ/0U1kRny16mDnFfX92bI3HBQm4XJA0nEfSvio7EUAAdhe77GMfu7+JELqXNowPGPLlvrbCFYnQhxGRITHtTIEbtJA6MKtBzHgjtw5pt7oWxKgGUnaJTfOPOSv43RLFGggkT/+gTjnZOagu8hhXp0x5HXJuZzw90aIS3jAfSPDc2ivct4WhWk0wcuQyC2rAh4I7gtiR+LqJJGqvucw4S+NR95FunKHKEW4yasKW1oU31/rRbp4Bmwo6BPsQlxnaSHPtk68IVkYDBslz1A5gOP+M/Iam2WI02y6sE/7aAH1ruN3pZlVuYFc3JDNHOPOvevP110d60lroknGdc9vxcFfj48OCKw/8Ed6tiXtAvk0Qu9Qt4ZyLUoPKIWEqjdLjwVadTDJQFAxRptNgiCos7s0czadUu7FNCRxfndjDxhA7trvys44ufEyK++YzZIgNu3r4dywNI22Nm+JZtLj+rX8ARE6FTPlxGBD0SSdXsfCfY2N1ytBBHQRnPsVaHK1p7KOhwQVbqEupcGyvaRolnymOzDLGFdS06OGYFrYXdgIbuqYtZP8QerXtUl0sWNAvvqHSPCQcpKecpMEecar+FUVwLEA+H1wzOprCMbRR+EgIboeDqQ7GxXqugkuFyvnlLDgxnaWhEhQb/5kAcQmnyUZ57MhDcUJqqQ4Cdmwrcxho1P+YqWY9yn0E86F+hl5976a/gH5KBobB84OWmgcX42eAmqpJf+8c8SuBv+7NctbQOk21aYlFEpkwSme/kG1/edtyoHQH/hF0RB1cT8g+u9S9AK2rs3s2G+Ap0U5oyY8pqJalGdZSBudE0sU4mhOV8trtx0FrN9A7pNkTcGPH25nCtyIz6rzR+DP8Mtgw5385s5ivVlDb+z74Wbh6iu7ZkVAogNTpUYU/1BxDXWJqFMkFmfziNxQ5AQqm1vGlBzXifoQkUFX1riutNphmu0Hs+7KMmMLvtW2cXmQDpkHFKVheeN4w7pBCEZ8KhZ0VTOwRZcdvrNcpYfXM13/QdTHQmCqqwgS/VvlUFz7PDn0/OKo6moUic8W6b1iEvd3kfc7QkunxoOUoJr4RwJ+PqCzN6PxQivAFA2tmDPc8qEa1PAdxTeNFoR/6dNQRojouuJq3C1LrbmGf6lQPvKi3KeKHXyjmDr7Tve+al2tcWJVr+1qEM3/XuthoiZbuTDxYUjZ2nf2fhHrmNcfvrfNxSNHVdQPp2R9Rf3eGxlRJsmRpef66VbYhOpmiH4xmq45EWiyBZmYm+tZtjsP51EDMIvdFbVRSGO/hMqURrDSsJXJeot27Iup2s0P2n/6a9k0c4SVvf/WXNN5x9JNvjU97bQNDQRfonJmo9pRYYHl1tSqNIYBK7KsMH+qr1vmiJuhrXUuL/RtOKvE9KXQ8kGoC9oF5rFn21z40ElxG5XRTASg==</xenc:CipherValue>
            </xenc:CipherData>
        </xenc:EncryptedData>
    </soap:Body>
</soap:Envelope>

首先,我以前从未使用过 SOAP,所以我做错事的可能性很大 :)

在这里找到了一些东西,但我需要更多详细信息https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#aes256-cbc

ivkey 是如何存储在标头中的 CipherValue 中的?

向 web 服务发送 XML 请求时出现此错误

23-08-2018 12:50:02   General exception:Padding is invalid and cannot be removed.
23-08-2018 12:50:02   Stack trace:    at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)
   at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptData(EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm)
   at SomeClassCore.XmlSecurity.Decryptor.DecryptData(Byte[] symmetricKey)
   at SomeClassCore.SecurityServiceImpl.UnwrapRequest(ServiceRequest serviceRequest)
   at BD.BCA.MessageHandler.MessageHandler.ProcessRequest(HttpContext context)

已经搜索了更多.. 也许iv 必须是存储数据的一部分。但它仍然不起作用?和上面一样的错误

class Encryption 
    const AES256_CBC = 'AES-256-CBC';

    public function data_encrypt(string $data, string $cipher): Array
        switch($cipher)
            case self::AES256_CBC:
                $key_length     = 32;
                $block_length   = 16;
                break;
        

        $iv     = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
        $key    = openssl_random_pseudo_bytes($key_length);

        $encrypted_data = $iv.openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);

        return [
            'data'  => base64_encode($this->pkcs7_padding($encrypted_data, $block_length)),
            'key'   => $key
        ];
    

    public function key_encrypt(string $key): string
        $public_cert = openssl_pkey_get_public('contents of public cert');
        openssl_public_encrypt($key, $data, $public_cert, OPENSSL_PKCS1_OAEP_PADDING);
        openssl_free_key($public_cert);

        return base64_encode($data);
    

    private function pkcs7_padding(string $data, int $block_length): string
        $pad = $block_length - (strlen($data) % $block_length);

        return $data.str_repeat(chr($pad), $pad);
    


$Enc = new Encryption;
$data_encrypted = $Enc->data_encrypt('The message I want to encrypt', Encryption::AES256_CBC);

//  This base64 encoded string goes to <EncryptedData>
$data_encrypted['data'];

//  This base64 encoded string goes to <EncryptedKey> in the header
$Enc->key_encrypt($data_encrypted['key']);

更新

已与网络服务的维护者联系,OAEP padding 用于 RSA 加密,PKCS7 padding 用于 AES 芯片..

正如我所见,这也是我所做的?

【问题讨论】:

简单建议;你确定你需要使用 OPENSSL_PKCS1_OAEP_PADDING 吗?也许 API 使用的是 PKCS5 或 PKCS7?此外,该 API 似乎存在 Padding Oracle 漏洞。 @jurgen,你的意思是:另外,这个 API 好像有 Padding Oracle 漏洞? 我认为 jurgen 是对的。此错误消息:“Padding is invalid and cannot be removed”表示 API 易受攻击。详情请见owasp.org/index.php/Testing_for_Padding_Oracle_(OTG-CRYPST-002)。 @t.m.adam,我会通知提供商 :) 但是您有解决方案吗?或者您现在有没有解决方案?我不是Java开发人员,所以我很难说..第一次非对称解密或第二次对称解密会失败吗? :) 对不起,我不知道。让我研究一下这个问题,如果我发现有用的东西,我一定会发布答案或评论。 【参考方案1】:

您的错误表明 API 未能解密 openssl AES-256-CBC 数据。

我认为原因是因为在您的班级中,您通过pkcs7_padding() 函数路由加密。我相信默认情况下,只要您不在openssl_encrypt() 函数中指定OPENSSL_ZERO_PADDING,填充是pkcs7。所有 AES 加密的块大小为 128 位或 16 字节。

所以本质上你是在填充你已经填充的加密。所以基本上我只是从你的班级中删除了你的pkcs7_padding()

我确实测试了您的公钥加密。我能够使用来自2048b certificate 的 2048b rsa 密钥并使用 PEM 格式的证书生成加密的公钥。我不知道它是否正确填充。但是OPENSSL_PKCS1_OAEP_PADDING 可能是正确的。

我的猜测是,如果 API 进入 AES 部分,RSA 加密就会起作用。

至于如何将数据组装到 XML 中,我不知道。 但似乎合理的是,密码值中的&lt;xenc:EncryptedKey&gt; 标记将是 RSA 加密密钥,而&lt;xenc:EncryptedData&gt; 标记的密码值将是 AES 加密数据。您只需要弄清楚 API 是如何获取 IV 的。

阅读您的 API 文档,了解他们期望如何交付 IV。如果这对您不起作用,我也会继续寻找。

我稍后会对此进行研究。但是要知道尝试不手动填充加密。希望能帮助到你。

要考虑的另一件事是,在您的案例示例中,您不需要使用 IV。在您的 AES 加密中,您将为每次加密生成一个新密钥,然后通过您的证书获得的 RSA 公钥加密 AES 密钥。

如果您使用相同的 AES 密钥,我会看到需要实现 IV,但在这种情况下我不需要。无论如何...我们需要知道 API 是否需要 IV,如果需要,预计如何发送?

class Encryption 

    const AES256_CBC = 'AES-256-CBC';

    public function __construct()



    

    public function data_encrypt($data, $cipher)

        switch($cipher)
            case self::AES256_CBC:
                $key_length     = 32;
                $block_length   = 16;
                break;
        

        $iv     = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
        $key    = openssl_random_pseudo_bytes($key_length);

        $encrypted_data = $iv . openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);

        return [
            'data'  => base64_encode($encrypted_data),
            'key'   => $key //Does this need to be encoded? 
        ];
    

    //***Important***
    //Make sure you certificate is 1.) a x.509 certificate resource, 2.)A file path that leads to a PEM encoded certificate, or 3.) a PEM formatted key.
    public function key_encrypt($text)

        $keyResource = openssl_pkey_get_public(file_get_contents('path/to/myCert.pem')); //This returns a resource or FALSE.

        if(!$keyResource)
          echo 'Something wrong with certificate.';
        

        openssl_public_encrypt($text, $cipherText, $keyResource, OPENSSL_PKCS1_OAEP_PADDING);
        openssl_free_key($keyResource); 
        return base64_encode($cipherText);

    




$Enc = new Encryption;

$cipherText = $Enc->data_encrypt('The message I want to encrypt', Encryption::AES256_CBC);

//  This base64 encoded string goes to <EncryptedData>
echo 'AES Data: ' . $cipherText['data'] . '<br><br>';
echo 'AES Key: ' . $cipherText['key'] . '<br><br>';

//  This base64 encoded string goes to <EncryptedKey> in the header
$key = $Enc->key_encrypt($cipherText['key']);

echo 'RSA OAEP Padded Key: ' . $key;

【讨论】:

【参考方案2】:

*经过测试和工作的代码 * 我建议您将所涉及的不同部分分开。问题的最可能原因是执行顺序(即您应该在加密之前进行填充)。我也很惊讶没有签名,但在你的情况下可能不需要。但是,我准备了建议的代码供您测试,并添加了解密/解码功能以使测试更容易。祝你好运。

<?php

class Encryption 

    const AES256_CBC = 'AES-256-CBC';
    const IV_BYTES = 16;

    protected $binary_security_token = null;
    protected $private_key = null;
    protected $public_key = null;

    public function data_encrypt(string $data, string $password): Array 

        $key            = hash('sha256', $password, true);
        $iv             = openssl_random_pseudo_bytes(self::IV_BYTES);

        $padding        = 16 - (strlen($data) % 16);
        $data          .= str_repeat(chr($padding), $padding);

        $encrypted_data = openssl_encrypt($data, self::AES256_CBC, $key, OPENSSL_RAW_DATA, $iv);
        $encoded_data   = base64_encode($iv . $encrypted_data);

        return [
            'data'  => $encoded_data,
            'key'   => $key
        ];
    

    public function data_decrypt(string $data, string $password): Array 

        $decoded_data   = base64_decode($data);
        $key            = hash('sha256', $password, true);
        $iv             = substr($decoded_data, 0, self::IV_BYTES);
        $encrypted_data = substr($decoded_data, self::IV_BYTES);

        $decrypted_data = openssl_decrypt($encrypted_data, self::AES256_CBC, $key, OPENSSL_RAW_DATA, $iv);
        $padding        = ord($decrypted_data[strlen($decrypted_data) - 1]); 

        return [
            'data' => substr($decrypted_data, 0, -$padding)
        ];
    

    public function key_encrypt(string $key): ?string 

        $encoded_data = null;

        if ($this->public_key && openssl_public_encrypt($key, $data, $this->public_key, OPENSSL_PKCS1_OAEP_PADDING)) 
            $encoded_data = base64_encode($data);
        
        // openssl_free_key($this->public_key);

        return $encoded_data;
    

    public function key_decrypt(string $data): ?string 

        $decrypted_data = null;

        $decoded_data = base64_decode($data, true);
        if ($this->private_key && openssl_private_decrypt($decoded_data, $decrypted, $this->private_key, OPENSSL_PKCS1_OAEP_PADDING)) 
            $decrypted_data = $decrypted;
        
        // openssl_free_key($decrypted);

        return $decrypted_data;
    

    public function generate_keys(): void 

        $config = [ "private_key_bits" => 2048, "private_key_type" => OPENSSL_KEYTYPE_RSA ];
        $resource = openssl_pkey_new($config);

        if (openssl_pkey_export($resource, $this->private_key)) 
            echo "private_key:\n" . $this->private_key . "\n";
            $private_key_file = "private_key.pem";
            file_put_contents("private_key.pem" , $this->private_key);
        

        $this->public_key = openssl_pkey_get_details($resource);
        $this->public_key = $this->public_key["key"];
        $this->binary_security_token = preg_replace("#-.+-|[\r\n]| #", "", $this->public_key);
        echo "public_key:\n" . $this->public_key . "\n";
        file_put_contents("public_key.pem", $this->public_key);
    

    public function load_keys(): void 

        $private_key_path = realpath(dirname(__FILE__) . "/private_key.pem");
        if (!$private_key_path) 
            $this->generate_keys();
            return;
        
        $private_key_contents = file_get_contents($private_key_path);
        if (!$private_key_contents) 
            $this->generate_keys();
            return;
        
        $public_key_path = realpath(dirname(__FILE__) . "/public_key.pem");
        if (!$public_key_path) 
            $this->generate_keys();
            return;
        
        $public_key_contents = file_get_contents($public_key_path);
        if (!$public_key_contents) 
            $this->generate_keys();
            return;
        

        // Signature to see that data is not manipulated, could be performed on an encrypted body. The spec says you only make a signature for what you can see.
        // Is it important to "hide data", "detect manipulated data" or both ...
        $this->binary_security_token = preg_replace("#-.+-|[\r\n]| #", "", $public_key_contents); // BinarySecurityToken for securityToken in Security header
        // ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
        // EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"

        if (openssl_pkey_export($private_key_contents, $this->private_key)) 
            echo "private_key:\n" . $this->private_key . "\n";
        

        $public_resource = openssl_pkey_get_public($public_key_contents);
        if ($public_resource) 
            $this->public_key = openssl_pkey_get_details($public_resource);
            $this->public_key = $this->public_key["key"];
            echo "public_key:\n" . $this->public_key . "\n";
        
    


$enc = new Encryption();

$encrypted = $enc->data_encrypt("The message I want to encrypt", "password");

// This base64 encoded string goes to <EncryptedData>
// $encrypted['data']

// Test that data_encrypt / data_decrypt works (from a terminal)
echo "encrypted data:\n" . $encrypted["data"] . "\n";
$decrypted = $enc->data_decrypt($encrypted["data"], "password");
echo "decrypted data:\n" . $decrypted["data"] . "\n";

// This base64 encoded string goes to <EncryptedKey> in the header
// $enc->key_encrypt($encrypted['key']);
if (version_compare(phpversion(), "7.1.0", ">=")) 
    $enc->load_keys();
    $pwd_hash_pre = bin2hex($encrypted["key"]);
    echo "hex key:" . $pwd_hash_pre . "\n";
    $encrypted_key = $enc->key_encrypt($encrypted["key"]);
    echo "\nencrypted and base64encoded key:" . $encrypted_key . "\n";
    $decrypted_key = $enc->key_decrypt($encrypted_key);
    $pwd_hash_post = bin2hex($decrypted_key);
    echo "\ndecrypted and decoded key:" . $pwd_hash_post . "\n";
    $equal_hashes = $pwd_hash_pre === $pwd_hash_post ? 'true' : 'false';
    echo "password hashes equal:" . $equal_hashes . "\n";

【讨论】:

以上是关于WSSE - XML SOAP 安全和密钥加密和存储 (EncryptedData/EncryptedKey)的主要内容,如果未能解决你的问题,请参考以下文章

在序列化期间将名称空间和前缀添加到soap消息头

PHP SOAP HEADER构造(wsse)

SoapFault:元素 http://schemas.xmlsoap.org/soap/envelope/:Body 上没有 id 属性(代码 wsse:InvalidSecurity)

缺少相互证书的 Wcf 请求 wsse:Security

从resfful API设计到加密算法

C#调用JAVA接口WSSE方式用WebClient方式