title: Why does Windows CryptVerifySignature Fail on Signature created by PHP?
body: <p>I want to create a hash in PHP and sign it on a Linux server (using XAMPP on Windows for the testing) and then verify the hash on Windows using the public key.  I have the following PHP script to generate it (I then right click, view source, copy/paste the signature create to the C/C++ Windows app).  When I run the Windows app, the SHA256 hash matches the one created in PHP but the validation fails. I tried not byte swaping the signature in windows but same issue.   What am I doing wrong?</p>
  <p>Here is the PHP code:</p>
  <pre><code>&lt;?php
  
  $privatekey=&lt;&lt;&lt;EOD
  -----BEGIN PRIVATE KEY-----
  MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC27fzjIIr/V3ds
  1hLNyuyrpw5yAtTPG/deWyaU1kLHk028ohfkm9nbGFCloJNiIf51fSlpyPDiBjnC
  pTCr/ac6TPGcXiJcxNvpzK9uFS2iQDgWHlJzvPT5dJacvg8RWXklVg3wseHMnp1t
  mvCZrMDCiay4dAL0GWRyxUe12kX05XA4J0oSJbi11zwIq3SlhtzWaCn7njOLV0dh
  BLidBhEY1Y47rjikCFaJrfac324NNvNuIJPI0gfhtqZFjjSC9vhUw0zE9SWl8iLb
  3+qJ6HJ/Et+iPMGCmuvR4YB/UrdoN/pKQazfIfYoaRnoz0rT55uj42TL/mdhgWkO
  3ftuocJtAgMBAAECggEBAKqq7z2YpxYDrNBGCdUmdhjQC5IjQhjYprnJoP1LyZIZ
  xHUxZclL/r2CuftNDTSJMra6x2CCsPqvhEQtinNsfBDQqLqSuMyEfghrh2DVMXYn
  JCy+rX591LDILZyfVb+CEpza5Lajv18AvS/9VmyJ2G2ntRWCZx8GcckCdh0cQBhy
  QPKGmad2iBs0vcsP+2JTqxy1jMIPTydCVYwyXpTqqfUFvFWkeTQ8ZZT7mopOUqTL
  wJBUTNwEbTCaoVMP/Ya57KjT0/3iD9r3n2IIOUsE1fIDJqFaoE0cXtERjTnkevTa
  xJeuiqvVEd9+i2coTrpywLX6Ri9ATnYECg8XMW568gECgYEA8wqzxOM9TjV2yxyE
  O3d6Fo6nVBpdFoj1S9Ies7SLwHEwIyF3/FmQB75jJs8uMhFNHbKpOiscOeKmtkEd
  iSrN4qyQboNvBsHbL7nS0yF5zo9zRWH6RfAjV2Z+8cRNhN1rQqYrLZjDilo26B4c
  VMmCoOGOKaPnYVMqAc9cPTmVZd0CgYEAwK7PRznksy19k8qRPZdtnlHMhNucNr0+
  UvxK5MGV0aTvM5ZoJb75fT7YD5YN05FoMB4N4xO05S/l9ewM62zUOnTIRJM5wyDA
  MEmTl5yxFv7CKpglAByc+hYsRQ+wES0GJ5p+yjFNP3kNANUIjIVQVWSJB6s6XsHL
  qcFk3k4M7dECgYEA09AT9BeHKk32BfYIWDs8L8SLmASR9D3Qag/pqfxDf6glp7hX
  dZooqFcI2p3dSM1DRAqc6ZEAvIIHgPcm4pBndpGmHmp1rJHukJ2GW3LlUVw7vdAx
  6hnq8xTktZe7Z3wBKsjfAJIOeNk+2PTFBC/KpKNu3mN2F5//ECzD2qgAyBECgYEA
  qKIVnMOpSbuddRRLSvlVocL7WKePL0Uu35gYv95BDcro4mBXY/mhBqFSnUl5blmL
  MtmK09rybcce3r0pjX2gvExq3cFwQztNmLU8K+uh/XsXWgnwEAn6xNFVC1gwgsfB
  5DVNlA3UCqIZ5EmOAtXGOgYT+5c7LaU0ZqxwaDMtE5ECgYB5j2uYxS75QgziesFs
  ibIhmXYNhfZydpce5fhsIVZADOBTrj1Pkb9qoXAJgGgbuI17E+Mb5Adgs3W5YWHy
  MJG9Ih8xQRM6Xau4EGF+I73JWcQHDxeoEYFi8nvTtb//hUMsME/gADyXgthTBPXS
  fYx1VsZzfLe9U3YxOmrgM6fXIA==
  -----END PRIVATE KEY-----
  EOD;
  
  $publickey=&lt;&lt;&lt;EOD
  -----BEGIN PUBLIC KEY-----
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtu384yCK/1d3bNYSzcrs
  q6cOcgLUzxv3XlsmlNZCx5NNvKIX5JvZ2xhQpaCTYiH+dX0pacjw4gY5wqUwq/2n
  OkzxnF4iXMTb6cyvbhUtokA4Fh5Sc7z0+XSWnL4PEVl5JVYN8LHhzJ6dbZrwmazA
  womsuHQC9BlkcsVHtdpF9OVwOCdKEiW4tdc8CKt0pYbc1mgp+54zi1dHYQS4nQYR
  GNWOO644pAhWia32nN9uDTbzbiCTyNIH4bamRY40gvb4VMNMxPUlpfIi29/qiehy
  fxLfojzBgprr0eGAf1K3aDf6SkGs3yH2KGkZ6M9K0+ebo+Nky/5nYYFpDt37bqHC
  bQIDAQAB
  -----END PUBLIC KEY-----
  EOD;
  
      // $datatohash=$_POST[&quot;var1&quot;].$_POST[&quot;var2&quot;].$_POST[&quot;var3&quot;];
      $datatohash=&quot;TestData&quot;;
      
      $hashvalue=hash(&quot;sha256&quot;, $datatohash);
    
      if (openssl_sign($hashvalue, $signature, $privatekey, OPENSSL_ALGO_SHA256)) {
          echo &quot;Success: &quot;, $hashvalue, &quot;\n&quot;, base64_encode($signature), &quot;\n\n&quot;;
          echo &quot;BYTE Signature[]={ &quot;;
          for ($i = 0; $i &lt; strlen($signature); $i++) {
              echo &quot;0x&quot;, dechex(ord($signature[$i])), &quot;,&quot;;
          }/*
          for ($i = strlen($signature)-1; $i &gt;=0 ; $i--) {
              echo &quot;0x&quot;, dechex(ord($signature[$i])), &quot;,&quot;;
          }*/
          echo &quot;};\n&quot;;
          
          if (openssl_verify($hashvalue, $signature, $publickey, OPENSSL_ALGO_SHA256)) {
              echo &quot;Verified\n&quot;;
          }
          else {
              echo &quot;Verification Failed\n&quot;;
          }
      }
      else {
          echo &quot;Failure:&quot;;
      }
  
  ?&gt;
  </code></pre>
  <p>Here is the Windows C/C++ code:</p>
  <pre><code>// RSAVerify.cpp : This file contains the 'main' function. Program execution begins and ends there.
  //
  
  #include &lt;windows.h&gt;
  #include &lt;inttypes.h&gt;
  
  #include &lt;stdio.h&gt;
  
  enum ePemType
  {
    RSA_SIGNATURE, RSA_PRIVATE_KEY, RSA_PUBLIC_KEY
  };
  
  
  char PublicKey[]=&quot;-----BEGIN PUBLIC KEY-----\
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtu384yCK/1d3bNYSzcrs\
  q6cOcgLUzxv3XlsmlNZCx5NNvKIX5JvZ2xhQpaCTYiH+dX0pacjw4gY5wqUwq/2n\
  OkzxnF4iXMTb6cyvbhUtokA4Fh5Sc7z0+XSWnL4PEVl5JVYN8LHhzJ6dbZrwmazA\
  womsuHQC9BlkcsVHtdpF9OVwOCdKEiW4tdc8CKt0pYbc1mgp+54zi1dHYQS4nQYR\
  GNWOO644pAhWia32nN9uDTbzbiCTyNIH4bamRY40gvb4VMNMxPUlpfIi29/qiehy\
  fxLfojzBgprr0eGAf1K3aDf6SkGs3yH2KGkZ6M9K0+ebo+Nky/5nYYFpDt37bqHC\
  bQIDAQAB\
  -----END PUBLIC KEY-----&quot;;
  
  BYTE Signature[]={ 0x48,0x7d,0xeb,0xc,0x3c,0x6b,0x2e,0xd7,0x17,0x8d,0x9b,0x43,0xe2,0x29,0x97,0x8c,0x35,0x65,0x5a,0x41,0x89,0x4a,0x18,0x26,0x29,0x84,0x6f,0x1c,0xc3,0x9,0xcf,0x26,0x4,0x8d,0x2c,0xe0,0x3f,0xe6,0x73,0xc6,0x7e,0x94,0xee,0x5b,0x5e,0x20,0x4f,0x50,0xf,0x38,0x9c,0x63,0x78,0x89,0x7c,0x80,0x73,0xfb,0xf4,0x93,0x51,0x44,0xc6,0x2,0xd9,0x39,0xae,0xc2,0xb0,0xa4,0x19,0x95,0xe6,0x9,0x89,0x37,0x77,0x25,0x3a,0xd,0xe5,0xfe,0xc7,0x15,0x1d,0xab,0xd5,0xba,0x84,0xc8,0xa1,0xe2,0x97,0x5f,0x87,0x73,0xcd,0xbb,0x50,0x1c,0x4d,0x20,0x96,0x19,0x5f,0x1d,0xfe,0xc3,0xa7,0x58,0x1f,0x7,0xb9,0x2f,0xac,0x42,0x2,0x93,0x7a,0x68,0xb4,0x9f,0x5b,0xb2,0x9b,0x9c,0xcb,0x63,0xf3,0x4f,0xd0,0x20,0xc3,0x43,0x37,0x1c,0xb6,0x24,0x63,0x8b,0xe4,0xf7,0xf9,0x82,0x80,0x9,0xec,0x61,0xbf,0x5a,0xcc,0x3d,0xdf,0x7e,0x7b,0x88,0x18,0x7d,0x29,0x9c,0x7f,0x75,0xfb,0x11,0xdd,0x60,0xd3,0xfb,0x2a,0xc5,0xa4,0x5a,0xcd,0x26,0xe,0x13,0xa9,0x8,0x24,0x46,0xa9,0xdd,0xf4,0x0,0x34,0x76,0xac,0x71,0x9f,0xc1,0x12,0x88,0x64,0x43,0xb7,0xe3,0x39,0xa1,0xe1,0x50,0x7e,0x6c,0x6c,0xde,0x70,0xe1,0xd3,0x30,0xdf,0x3a,0x11,0x4,0x4a,0x24,0x32,0xd,0x67,0x56,0x8d,0x25,0x39,0x6c,0x40,0xf2,0x8a,0x85,0x67,0x41,0x29,0xa2,0xa1,0x2f,0x61,0xaa,0x59,0x18,0x60,0xbd,0x39,0x39,0xc2,0x9e,0xad,0x74,0xf7,0xee,0xcc,0xc0,0xd6,0x9c,0xc5,0x5c,0x80,0x47,0xe9,0xae,};
  
  //-------------------------------------------------------------------------
  // Purpose: Convert base64 key/certificate data to binary form
  //
  // Input:   base64data     - [i] the base64 data string
  //          base64datasize - [i] the base64 data size (0 = z-term data)
  //          flags          - [i] flags passed to CryptStringToBinary()
  //          binsize        - [o] update with the size of binary data
  //
  // Output:  buffer to binary data or NULL if problem
  //
  // Notes:   caller must delete[] returned buffer.
  //          flags will typically be CRYPT_STRING_ANY
  //
  BYTE* Base642Bin(const BYTE *base64data, DWORD base64datasize, DWORD flags, DWORD *binsize)
  {
    BYTE * bindata=NULL;
  
    // calculate size required
    if (CryptStringToBinaryA((const char*) base64data, base64datasize, flags, NULL, binsize, NULL, NULL)) {
      // create buffer
      if ((bindata=new BYTE[*binsize])!=NULL) {
        // decode base64    
        if (!CryptStringToBinaryA((const char*) base64data, base64datasize, flags, bindata, binsize, NULL, NULL)) {
          // clean up.
          delete[] bindata;
          bindata=NULL;
        }
      }
    }
    // set size to zero if unable to decode to binary
    if (bindata==NULL) {
      *binsize=0;
    }
    // return buffer
    return bindata;
  }
  
  
  //-------------------------------------------------------------------------
  // Purpose: Byte swap entire buffer (end == beginning)
  //
  // Input:   data      - [io] data buffer to swap
  //          datasize  - [i] data buffer size
  //
  // Output:  na
  //
  // Notes:   
  //
  void ByteSwapBufferDirect(BYTE *data, DWORD datasize)
  {
    BYTE *pa=data;
    BYTE *pb=data+datasize-1;
  
    for (DWORD i=0; i&lt;datasize/2; i++, pa++, pb--) {
      BYTE t=*pa; 
      *pa=*pb;
      *pb=t;
    }
  }
  
  //-------------------------------------------------------------------------
  // Purpose: Byte swap entire buffer (end == beginning)
  //
  // Input:   data      - [io] data buffer to swap
  //          datasize  - [i] data buffer size
  //
  // Output:  na
  //
  // Notes:   
  //
  void ByteSwapBuffer(BYTE *dataout, const BYTE *datain, DWORD datasize)
  {
    BYTE *pa=dataout;
    BYTE *pb=dataout+datasize-1;
  
    const BYTE *psa=datain;
    const BYTE *psb=datain+datasize-1;
  
    for (DWORD i=0; i&lt;datasize/2; i++, pa++, pb--, psa++, psb--) {
      *pa=*psb;
      *pb=*psa;
    }
  }
  
  //-------------------------------------------------------------------------
  // Purpose: Convert RSA PEM key/certificate/signature to DER format
  //
  // Input:   pemdata   - [i] pem data to convert
  //          pemsize   - [i] size of pem data (0 = z-term)
  //          pemtype   - [i] enumeration of PEM type 
  //          dersize   - [o] size of returned der data
  //
  // Output:  buffer of derdata 
  //
  // Notes:   caller must delete[] the returned buffer.
  //
  BYTE *RSAPEMToDER(const BYTE *pemdata, DWORD pemsize, ePemType pemtype, DWORD *dersize)
  {
    BYTE *derdata=Base642Bin(pemdata, pemsize, CRYPT_STRING_ANY, dersize);
    if (derdata) {
      // check if need to reverse bytes
      if (pemtype==RSA_SIGNATURE) {
        // yes, reverse from openssl MSB to Windows LSB
        ByteSwapBufferDirect(derdata, *dersize);
      }
    }
  
    return derdata;
  }
  
  //-------------------------------------------------------------------------
  // Purpose: Convert RSA PEM key/certificate/signature to handle used by 
  //          Windows crypt routines
  //
  // Input:   hcryptprovider - [i] handle to provider to assign key to
  //          pemdata   - [i] pem data to convert
  //          pemsize   - [i] size of pem data (0 = z-term)
  //          pemtype   - [i] enumeration of PEM type 
  //
  // Output:  handle to the imported key or 0 if failure
  //
  // Notes:   The hcryptprovider is updated to contain the key
  //
  HCRYPTKEY ImportPEM(HCRYPTPROV hcryptprovider, const BYTE *pemdata, DWORD pemsize, ePemType pemtype)
  {
    HCRYPTKEY hckey=0;
  
    DWORD dersize;
    BYTE *derdata=RSAPEMToDER(pemdata, pemsize, pemtype, &amp;dersize);
    if (derdata) {
      if (pemtype==RSA_PUBLIC_KEY) {
        // convert DER to RSA public key info
        CERT_PUBLIC_KEY_INFO *keydata;
        DWORD keylen;
        if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
                                derdata, dersize, CRYPT_DECODE_ALLOC_FLAG, NULL, &amp;keydata, &amp;keylen)) {
          // import public key blob
          if (!CryptImportPublicKeyInfo(hcryptprovider, X509_ASN_ENCODING, (PCERT_PUBLIC_KEY_INFO) keydata, &amp;hckey)) {
            // ensure zero
            hckey=0;
          }
          // clean up
          LocalFree(keydata);
        }
      }
      else {
        BYTE *keydata;
        DWORD keylen;
        PCRYPT_PRIVATE_KEY_INFO pki=0;
        DWORD pkisize;
        // convert PKCS8 data to private key info
        if (CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, derdata, dersize,
                                CRYPT_DECODE_ALLOC_FLAG, NULL, &amp;pki, &amp;pkisize)) {
            // convert private key info to RSA private key blob
          if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
                                  pki-&gt;PrivateKey.pbData, pki-&gt;PrivateKey.cbData, CRYPT_DECODE_ALLOC_FLAG,
                                  NULL, &amp;keydata, &amp;keylen)) {
             // import private key blob
            if (!CryptImportKey(hcryptprovider, keydata, keylen, 0, CRYPT_EXPORTABLE, &amp;hckey)) {
              hckey=NULL;
            }
            // clean up
            LocalFree(keydata);
          }
          // clean up
          LocalFree(pki);
        }
      }
    }
    // return key
    return hckey;
  }
  
  
  //-------------------------------------------------------------------------
  // Purpose: Create SHA256 hash
  //
  // Input:   hcryptprovider - [i] handle to provider for hash
  //          data      - [i] data to hash
  //          datasize  - [i] size of data to hash
  //
  // Output:  hash handle
  //
  // Notes:   
  //
  HCRYPTHASH SHA256_hash(HCRYPTPROV hcryptprovider, const BYTE *data, DWORD datasize)
  {
    HCRYPTHASH hash=0;
    // create hash object
    if (CryptCreateHash(hcryptprovider, CALG_SHA_256, 0, 0, &amp;hash)) {
      // hash data contents
      if (!CryptHashData(hash, data, datasize, 0)) {
        // remove hash
        CryptDestroyHash(hash);
        hash=0;
      }
    }
    // return hash handle
    return hash;
  }
  
  
  //-------------------------------------------------------------------------
  // Purpose: Verify hash 
  //
  // Input:   data      - [i] data to hash
  //          datasize  - [i] size of data to hash
  //          signature - [i] signature from linux
  //          signaturesize - [i] size of signature in bytes
  //          pempublickey - [i] public key
  //          pempublickeysize - [i] size of public key or 0 if z-term
  //
  // Output:  TRUE/FALSE
  //
  // Notes:   
  //
  BOOL VerifyHash(const BYTE* data, DWORD datasize, const BYTE *signature, DWORD signaturesize, const BYTE *pempublickey, DWORD pempublickeysize)
  {
    BOOL valid=FALSE;
  
    // Create provider
    HCRYPTPROV hcryptprov=0;
    if (CryptAcquireContext(&amp;hcryptprov, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
      // assign public key to provider
      HCRYPTKEY pubkey=ImportPEM(hcryptprov, pempublickey, pempublickeysize, RSA_PUBLIC_KEY);
      if (pubkey) {
        // convert signature from MSB to LSB for windows
        BYTE *lsbsig;
        if ((lsbsig=new BYTE[signaturesize])!=NULL) {
          // do swap
          ByteSwapBuffer(lsbsig, signature, signaturesize);
          // create the hash
          HCRYPTHASH hash=SHA256_hash(hcryptprov, data, datasize);
          if (hash) {
           #if defined(_DEBUG)
            BYTE hashdata[32];
            DWORD hashdatasize=sizeof(hashdata);
            if (CryptGetHashParam(hash, HP_HASHVAL, hashdata, &amp;hashdatasize, 0)) {
              printf(&quot;hash: &quot;);
              for (UINT i=0; i&lt;hashdatasize; i++) {
                printf(&quot;%02x&quot;, hashdata[i]);
              }
              printf(&quot;\n&quot;);
            }
           #endif
            // verify signature
            if (CryptVerifySignature(hash, lsbsig, signaturesize, pubkey, NULL, 0)) {
              valid=TRUE;
            }
            else {
              DWORD winec=GetLastError();
              printf(&quot;Verify Error (%xh)&quot;, winec);
              const char *str=&quot;&quot;;
              switch (winec) {
                case ERROR_INVALID_HANDLE:
                  str=&quot;Invalid Handle&quot;;
                  break;
                case ERROR_INVALID_PARAMETER:
                  str=&quot;Invalid Handle&quot;;
                  break;
                case NTE_BAD_FLAGS:
                  str=&quot;Bad Flags&quot;;
                  break;
                case NTE_BAD_HASH:
                  str=&quot;Bad Hash&quot;;
                  break;
                case NTE_BAD_KEY:
                  str=&quot;Bad Key&quot;;
                  break;
                case NTE_BAD_SIGNATURE:
                  str=&quot;Bad Signature&quot;;
                  break;
                case NTE_BAD_UID:
                  str=&quot;Bad UID&quot;;
                  break;
                case NTE_NO_MEMORY:
                  str=&quot;No Memory&quot;;
                  break;
              }
              printf(&quot;%s\n&quot;, str);
            }
            // clean up
            CryptDestroyHash(hash);
          }
  
          // clean up
          delete[] lsbsig;
        }
        // clean up
        CryptDestroyKey(pubkey);
      }
      // clean up
      CryptReleaseContext(hcryptprov, 0);
    }
  
    return valid;
  }
  
  
  int main()
  {
    BYTE *datatohash=(BYTE*) &quot;TestData&quot;; 
    DWORD datatohashsize=(DWORD) strlen((char*)datatohash);
  
    if (VerifyHash(datatohash, datatohashsize, Signature, sizeof(Signature), (BYTE*) PublicKey, sizeof(PublicKey))) {
      printf(&quot;Verified!\n&quot;);
    }
    else {
      printf(&quot;Failed Verification!\n&quot;);
    }
  }
  </code></pre>