Index: app/code/Magento/Authorizenet/Model/Directpost.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/Model/Directpost.php	(revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ app/code/Magento/Authorizenet/Model/Directpost.php	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -543,15 +543,16 @@
     public function validateResponse()
     {
         $response = $this->getResponse();
-        //md5 check
-        if (!$this->getConfigData('trans_md5')
-            || !$this->getConfigData('login')
-            || !$response->isValidHash($this->getConfigData('trans_md5'), $this->getConfigData('login'))
+        $hashConfigKey = !empty($response->getData('x_SHA2_Hash')) ? 'signature_key' : 'trans_md5';
+
+        //hash check
+        if (!$response->isValidHash($this->getConfigData($hashConfigKey), $this->getConfigData('login'))
         ) {
             throw new \Magento\Framework\Exception\LocalizedException(
                 __('The transaction was declined because the response hash validation failed.')
             );
         }
+
         return true;
     }
 
Index: app/code/Magento/Authorizenet/Model/Directpost/Request.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/Model/Directpost/Request.php	(revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ app/code/Magento/Authorizenet/Model/Directpost/Request.php	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -7,6 +7,7 @@
 namespace Magento\Authorizenet\Model\Directpost;
 
 use Magento\Authorizenet\Model\Request as AuthorizenetRequest;
+use Magento\Framework\Intl\DateTimeFactory;
 
 /**
  * Authorize.net request model for DirectPost model
@@ -18,9 +19,33 @@
      */
     protected $_transKey = null;
 
+    /**
+     * Hexadecimal signature key.
+     *
+     * @var string
+     */
+    private $signatureKey = '';
+
+    /**
+     * @var DateTimeFactory
+     */
+    private $dateTimeFactory;
+
+    /**
+     * @param DateTimeFactory $dateTimeFactory
+     * @param array $data
+     */
+    public function __construct(
+        DateTimeFactory $dateTimeFactory,
+        array $data = []
+    ) {
+        $this->dateTimeFactory = $dateTimeFactory;
+        parent::__construct($data);
+    }
+
     /**
      * Return merchant transaction key.
-     * Needed to generate sign.
+     * Needed to generate MD5 sign.
      *
      * @return string
      */
@@ -31,7 +56,7 @@
 
     /**
      * Set merchant transaction key.
-     * Needed to generate sign.
+     * Needed to generate MD5 sign.
      *
      * @param string $transKey
      * @return $this
@@ -43,7 +68,7 @@
     }
 
     /**
-     * Generates the fingerprint for request.
+     * Generates the MD5 fingerprint for request.
      *
      * @param string $merchantApiLoginId
      * @param string $merchantTransactionKey
@@ -63,7 +88,7 @@
     ) {
         return hash_hmac(
             "md5",
-            $merchantApiLoginId . "^" . $fpSequence . "^" . $fpTimestamp . "^" . $amount . "^" . $currencyCode,
+            $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode,
             $merchantTransactionKey
         );
     }
@@ -85,6 +110,7 @@
             ->setXRelayUrl($paymentMethod->getRelayUrl());
 
         $this->_setTransactionKey($paymentMethod->getConfigData('trans_key'));
+        $this->setSignatureKey($paymentMethod->getConfigData('signature_key'));
         return $this;
     }
 
@@ -168,17 +194,81 @@
      */
     public function signRequestData()
     {
-        $fpTimestamp = time();
-        $hash = $this->generateRequestSign(
-            $this->getXLogin(),
-            $this->_getTransactionKey(),
-            $this->getXAmount(),
-            $this->getXCurrencyCode(),
-            $this->getXFpSequence(),
-            $fpTimestamp
-        );
+        $fpDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC'));
+        $fpTimestamp = $fpDate->getTimestamp();
+
+        if (!empty($this->getSignatureKey())) {
+            $hash = $this->generateSha2RequestSign(
+                $this->getXLogin(),
+                $this->getSignatureKey(),
+                $this->getXAmount(),
+                $this->getXCurrencyCode(),
+                $this->getXFpSequence(),
+                $fpTimestamp
+            );
+        } else {
+            $hash = $this->generateRequestSign(
+                $this->getXLogin(),
+                $this->_getTransactionKey(),
+                $this->getXAmount(),
+                $this->getXCurrencyCode(),
+                $this->getXFpSequence(),
+                $fpTimestamp
+            );
+        }
+
         $this->setXFpTimestamp($fpTimestamp);
         $this->setXFpHash($hash);
+
         return $this;
     }
+
+    /**
+     * Generates the SHA2 fingerprint for request.
+     *
+     * @param string $merchantApiLoginId
+     * @param string $merchantSignatureKey
+     * @param string $amount
+     * @param string $currencyCode
+     * @param string $fpSequence An invoice number or random number.
+     * @param string $fpTimestamp
+     * @return string The fingerprint.
+     */
+    private function generateSha2RequestSign(
+        $merchantApiLoginId,
+        $merchantSignatureKey,
+        $amount,
+        $currencyCode,
+        $fpSequence,
+        $fpTimestamp
+    ): string {
+        $message = $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode;
+
+        return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantSignatureKey)));
+    }
+
+    /**
+     * Return merchant hexadecimal signature key.
+     *
+     * Needed to generate SHA2 sign.
+     *
+     * @return string
+     */
+    private function getSignatureKey(): string
+    {
+        return $this->signatureKey;
+    }
+
+    /**
+     * Set merchant hexadecimal signature key.
+     *
+     * Needed to generate SHA2 sign.
+     *
+     * @param string $signatureKey
+     * @return void
+     */
+    private function setSignatureKey(string $signatureKey)
+    {
+        $this->signatureKey = $signatureKey;
+    }
 }
Index: app/code/Magento/Authorizenet/Model/Directpost/Response.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/Model/Directpost/Response.php	(revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ app/code/Magento/Authorizenet/Model/Directpost/Response.php	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -24,27 +24,33 @@
      */
     public function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId)
     {
-        if (!$amount) {
-            $amount = '0.00';
-        }
-
         return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount));
     }
 
     /**
      * Return if is valid order id.
      *
-     * @param string $merchantMd5
+     * @param string $storedHash
      * @param string $merchantApiLogin
      * @return bool
      */
-    public function isValidHash($merchantMd5, $merchantApiLogin)
+    public function isValidHash($storedHash, $merchantApiLogin)
     {
-        $hash = $this->generateHash($merchantMd5, $merchantApiLogin, $this->getXAmount(), $this->getXTransId());
+        if (empty($this->getData('x_amount'))) {
+            $this->setData('x_amount', '0.00');
+        }
 
-        return Security::compareStrings($hash, $this->getData('x_MD5_Hash'));
-    }
+        if (!empty($this->getData('x_SHA2_Hash'))) {
+            $hash = $this->generateSha2Hash($storedHash);
+            return Security::compareStrings($hash, $this->getData('x_SHA2_Hash'));
+        } elseif (!empty($this->getData('x_MD5_Hash'))) {
+            $hash = $this->generateHash($storedHash, $merchantApiLogin, $this->getXAmount(), $this->getXTransId());
+            return Security::compareStrings($hash, $this->getData('x_MD5_Hash'));
+        }
 
+        return false;
+    }
+
     /**
      * Return if this is approved response from Authorize.net auth request.
      *
@@ -54,4 +60,54 @@
     {
         return $this->getXResponseCode() == \Magento\Authorizenet\Model\Directpost::RESPONSE_CODE_APPROVED;
     }
+
+    /**
+     * Generates an SHA2 hash to compare against AuthNet's.
+     *
+     * @param string $signatureKey
+     * @return string
+     * @see https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement
+     */
+    private function generateSha2Hash(string $signatureKey): string
+    {
+        $hashFields = [
+            'x_trans_id',
+            'x_test_request',
+            'x_response_code',
+            'x_auth_code',
+            'x_cvv2_resp_code',
+            'x_cavv_response',
+            'x_avs_code',
+            'x_method',
+            'x_account_number',
+            'x_amount',
+            'x_company',
+            'x_first_name',
+            'x_last_name',
+            'x_address',
+            'x_city',
+            'x_state',
+            'x_zip',
+            'x_country',
+            'x_phone',
+            'x_fax',
+            'x_email',
+            'x_ship_to_company',
+            'x_ship_to_first_name',
+            'x_ship_to_last_name',
+            'x_ship_to_address',
+            'x_ship_to_city',
+            'x_ship_to_state',
+            'x_ship_to_zip',
+            'x_ship_to_country',
+            'x_invoice_num',
+        ];
+
+        $message = '^';
+        foreach ($hashFields as $field) {
+            $message .= ($this->getData($field) ?? '') . '^';
+        }
+
+        return strtoupper(hash_hmac('sha512', $message, pack('H*', $signatureKey)));
+    }
 }
Index: app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
+++ app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Authorizenet\Test\Unit\Model\Directpost;
+
+use Magento\Authorizenet\Model\Directpost\Request;
+use Magento\Framework\Intl\DateTimeFactory;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+class RequestTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * @var DateTimeFactory|MockObject
+     */
+    private $dateTimeFactory;
+
+    /**
+     * @var Request
+     */
+    private $requestModel;
+
+    protected function setUp()
+    {
+        $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC'));
+        $this->dateTimeFactory->method('create')
+            ->willReturn($dateTime);
+
+        $this->requestModel = new Request($this->dateTimeFactory);
+    }
+
+    /**
+     * @param string $signatureKey
+     * @param string $expectedHash
+     * @dataProvider signRequestDataProvider
+     */
+    public function testSignRequestData(string $signatureKey, string $expectedHash)
+    {
+        /** @var \Magento\Authorizenet\Model\Directpost $paymentMethod */
+        $paymentMethod = $this->createMock(\Magento\Authorizenet\Model\Directpost::class);
+        $paymentMethod->method('getConfigData')
+            ->willReturnMap(
+                [
+                    ['test', null, true],
+                    ['login', null, 'login'],
+                    ['trans_key', null, 'trans_key'],
+                    ['signature_key', null, $signatureKey],
+                ]
+            );
+
+        $this->requestModel->setConstantData($paymentMethod);
+        $this->requestModel->signRequestData();
+        $signHash = $this->requestModel->getXFpHash();
+
+        $this->assertEquals($expectedHash, $signHash);
+    }
+
+    /**
+     * @return array
+     */
+    public function signRequestDataProvider()
+    {
+        return [
+            [
+                'signatureKey' => '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF65' .
+                    '70C8C29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F',
+                'expectedHash' => '719ED94DF5CF3510CB5531E8115462C8F12CBCC8E917BD809E8D40B4FF06' .
+                    '1E14953554403DD9813CCCE0F31B184EB4DEF558E9C0747505A0C25420372DB00BE1'
+            ],
+            [
+                'signatureKey' => '',
+                'expectedHash' => '3656211f2c41d1e4c083606f326c0460'
+            ],
+        ];
+    }
+}
Index: app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php	(revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -13,52 +13,15 @@
     /**
      * @var \Magento\Authorizenet\Model\Directpost\Response
      */
-    protected $responseModel;
+    private $responseModel;
 
     protected function setUp()
     {
         $objectManager = new ObjectManager($this);
-        $this->responseModel = $objectManager->getObject(\Magento\Authorizenet\Model\Directpost\Response::class);
-    }
-
-    /**
-     * @param string $merchantMd5
-     * @param string $merchantApiLogin
-     * @param float|null $amount
-     * @param float|string $amountTestFunc
-     * @param string $transactionId
-     * @dataProvider generateHashDataProvider
-     */
-    public function testGenerateHash($merchantMd5, $merchantApiLogin, $amount, $amountTestFunc, $transactionId)
-    {
-        $this->assertEquals(
-            $this->generateHash($merchantMd5, $merchantApiLogin, $amountTestFunc, $transactionId),
-            $this->responseModel->generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId)
+        $this->responseModel = $objectManager->getObject(
+            \Magento\Authorizenet\Model\Directpost\Response::class
         );
     }
-
-    /**
-     * @return array
-     */
-    public function generateHashDataProvider()
-    {
-        return [
-            [
-                'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306',
-                'merchantApiLogin' => 'Magento',
-                'amount' => null,
-                'amountTestFunc' => '0.00',
-                'transactionId' => '1'
-            ],
-            [
-                'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A',
-                'merchantApiLogin' => 'Magento2',
-                'amount' => 100.50,
-                'amountTestFunc' => 100.50,
-                'transactionId' => '2'
-            ]
-        ];
-    }
 
     /**
      * @param $merchantMd5
@@ -74,7 +37,8 @@
     }
 
     /**
-     * @param string $merchantMd5
+     * @param string $storedHash
+     * @param string $hashKey
      * @param string $merchantApiLogin
      * @param float|null $amount
      * @param string $transactionId
@@ -82,12 +46,21 @@
      * @param bool $expectedValue
      * @dataProvider isValidHashDataProvider
      */
-    public function testIsValidHash($merchantMd5, $merchantApiLogin, $amount, $transactionId, $hash, $expectedValue)
-    {
+    public function testIsValidHash(
+        string $storedHash,
+        string $hashKey,
+        string $merchantApiLogin,
+        $amount,
+        string $transactionId,
+        string $hash,
+        bool $expectedValue
+    ) {
         $this->responseModel->setXAmount($amount);
         $this->responseModel->setXTransId($transactionId);
-        $this->responseModel->setData('x_MD5_Hash', $hash);
-        $this->assertEquals($expectedValue, $this->responseModel->isValidHash($merchantMd5, $merchantApiLogin));
+        $this->responseModel->setData($hashKey, $hash);
+        $result = $this->responseModel->isValidHash($storedHash, $merchantApiLogin);
+
+        $this->assertEquals($expectedValue, $result);
     }
 
     /**
@@ -95,9 +68,14 @@
      */
     public function isValidHashDataProvider()
     {
+        $signatureKey = '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF6570C8C' .
+            '29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F';
+        $expectedSha2Hash = '368D48E0CD1274BF41C059138DA69985594021A4AD5B4C5526AE88C8F' .
+            '7C5769B13C5E1E4358900F3E51076FB69D14B0A797904C22E8A11A52AA49CDE5FBB703C';
         return [
             [
                 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306',
+                'hashKey' => 'x_MD5_Hash',
                 'merchantApiLogin' => 'Magento',
                 'amount' => null,
                 'transactionId' => '1',
@@ -106,11 +84,21 @@
             ],
             [
                 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A',
+                'hashKey' => 'x_MD5_Hash',
                 'merchantApiLogin' => 'Magento2',
                 'amount' => 100.50,
                 'transactionId' => '2',
                 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC',
                 'expectedValue' => false
+            ],
+            [
+                'signatureKey' => $signatureKey,
+                'hashKey' => 'x_SHA2_Hash',
+                'merchantApiLogin' => 'Magento2',
+                'amount' => 100.50,
+                'transactionId' => '2',
+                'hash' => $expectedSha2Hash,
+                'expectedValue' => true
             ]
         ];
     }
Index: app/code/Magento/Authorizenet/etc/adminhtml/system.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/etc/adminhtml/system.xml	(revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ app/code/Magento/Authorizenet/etc/adminhtml/system.xml	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -29,6 +29,10 @@
                     <label>Transaction Key</label>
                     <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                 </field>
+                <field id="signature_key" translate="label" type="obscure" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0">
+                    <label>Signature Key</label>
+                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+                </field>
                 <field id="trans_md5" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0">
                     <label>Merchant MD5</label>
                     <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
Index: app/code/Magento/Authorizenet/etc/config.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/code/Magento/Authorizenet/etc/config.xml	(revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ app/code/Magento/Authorizenet/etc/config.xml	(revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -22,6 +22,7 @@
                 <title>Credit Card Direct Post (Authorize.net)</title>
                 <trans_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
                 <trans_md5 backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
+                <signature_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
                 <allowspecific>0</allowspecific>
                 <currency>USD</currency>
                 <create_order_before>1</create_order_before>
