louisameline / php-mail-signature

Sign your e-mails with DKIM and Domain Keys in PHP
GNU Lesser General Public License v2.1
59 stars 19 forks source link

Support for SHA-256 signing algorithm #15

Closed rhymeswithmogul closed 4 years ago

rhymeswithmogul commented 7 years ago

This is a great library! I've started using it in some of my little projects. Unfortunately, it only supports signing with SHA-1, which is now deprecated in favor of SHA-256 (see RFC 6376). I've updated the code to default to using SHA-256 instead. Only a few lines need to be changed in the class file.

Firstly, I created a new options and default_options value called dkim_hash, which defaults to sha256:

$default_options = array(
    [...]
    /*
     * Specify whether to sign with SHA-256 (recommended) or the older, weaker SHA-1.
     * This variable takes either the value "sha1" or "sha256", as those are the only
     * two algorithms supported by the current version of DKIM.
     */
    'dkim_hash' => 'sha256'
    [...]
)

Then, in _get_dkim_header(), I've made three changes:

  1. I've replaced the call to sha1() with a call to hash(). This supports either algorithm natively.
  2. The algorithm value in the DKIM-Signature header now reflects which hash we're using (rsa-sha1 or rsa-sha256).
  3. I use the fourth parameter to openssl_sign() to specify the appropriate algorithm by passing either the constant OPENSSL_ALGO_SHA1 or OPENSSL_ALGO_SHA256.
    private function _get_dkim_header($body){
        $body =
            ($this -> options['dkim_body_canonicalization'] == 'simple') ?
            $this -> _dkim_canonicalize_body_simple($body) :
            $this -> _dkim_canonicalize_body_relaxed($body);

        // Base64 of packed binary hash of body
        $bh = rtrim(chunk_split(base64_encode(pack("H*", hash($this->options['dkim_hash'],$body))), 64, "\r\n\t"));
        $i_part =
            ($this -> options['identity'] == null) ?
            '' :
            ' i='.$this -> options['identity'].';'."\r\n\t";

        $dkim_header =
            'DKIM-Signature: '.
                'v=1;'."\r\n\t".
                'a=rsa-' . $this -> options['dkim_hash'] . ';'."\r\n\t".
                'q=dns/txt;'."\r\n\t".
                's='.$this -> selector.';'."\r\n\t".
                't='.time().';'."\r\n\t".
                'c=relaxed/'.$this -> options['dkim_body_canonicalization'].';'."\r\n\t".
                'h='.implode(':', array_keys($this -> canonicalized_headers_relaxed)).';'."\r\n\t".
                'd='.$this -> domain.';'."\r\n\t".
                $i_part.
                'bh='.$bh.';'."\r\n\t".
                'b=';

        // now for the signature we need the canonicalized version of the $dkim_header
        // we've just made
        $canonicalized_dkim_header = $this -> _dkim_canonicalize_headers_relaxed($dkim_header);

        // we sign the canonicalized signature headers
        $to_be_signed = implode("\r\n", $this -> canonicalized_headers_relaxed)."\r\n".$canonicalized_dkim_header['dkim-signature'];

        // $signature is sent by reference in this function
        $signature = '';
        $signing_algorithm = null;

        if ($this->options['dkim_hash'] === 'sha256') {
            $signing_algorithm = OPENSSL_ALGO_SHA256;
        } else if ($this->options['dkim_hash'] === 'sha1') {
            $signing_algorithm = OPENSSL_ALGO_SHA1;
        } else {
            die('Unsupported dkim_hash value "' . $this->options['dkim_hash'] . '" -- DKIM only supports sha256 and sha1.');
        }
        if(openssl_sign($to_be_signed, $signature, $this -> private_key, $signing_algorithm)){
            $dkim_header .= rtrim(chunk_split(base64_encode($signature), 64, "\r\n\t"))."\r\n";
        }
        else {
            trigger_error(sprintf('Could not sign e-mail with DKIM : %s', $to_be_signed), E_USER_WARNING);
            $dkim_header = '';
        }

        return $dkim_header;
    }

I've also done the same to the _get_dk_header() function.

Hope this helps!

louisameline commented 7 years ago

Hi, it sounds good, thanks! Would you care to make a pull request?