0

I'm trying to Verifying that requests originate from Office for the web by using proof keys

I have read this resource many times but still do not know how to implement it in PHP.

I tried the code below it didn't seem to solve the problem, I do not know what to do next.

Can someone give me a suggestion?

$rsa = new Crypt_RSA();
$modulus = new Math_BigInteger(base64_decode($modulus), 256);
$exponent = new Math_BigInteger(base64_decode($exponent), 256);

$rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
$rsa->setPublicKey();
$publicKey = $rsa->getPublicKey();
Lam
  • 1
  • 1

3 Answers3

3

I understand that verifying WOPI proof key is quite complicated to implement while extracting programming logic from their explanation in the document is challenging. Unfortunately, I'm not a PHP developer. However, I'd like to share my production C# code in case it could help.

private async Task<bool> ValidateWopiProof(HttpContext context)
{
    // Make sure the request has the correct headers
    if (!context.Request.Headers.ContainsKey(WopiRequestHeader.PROOF) ||
        !context.Request.Headers.ContainsKey(WopiRequestHeader.TIME_STAMP))
        return false;

    // TimestampOlderThan20Min
    var timeStamp = long.Parse(context.Request.Headers[WopiRequestHeader.TIME_STAMP].ToString());
    var timeStampDateTime = new DateTime(timeStamp, DateTimeKind.Utc);
    if ((DateTime.UtcNow - timeStampDateTime).TotalMinutes > 20)
        return false;

    // Set the requested proof values
    var requestProof = context.Request.Headers[WopiRequestHeader.PROOF];
    var requestProofOld = String.Empty;
    if (context.Request.Headers.ContainsKey(WopiRequestHeader.PROOF_OLD))
        requestProofOld = context.Request.Headers[WopiRequestHeader.PROOF_OLD];

    // Get the WOPI proof info from Wopi discovery
    var wopiProofPublicKey = await _wopiDiscovery.GetWopiProof();

    // Encode the values into bytes
    var accessTokenBytes = Encoding.UTF8.GetBytes(context.Request.Query["access_token"].ToString());

    var hostUrl = GetAbsolouteUrl(context);
    var hostUrlBytes = Encoding.UTF8.GetBytes(hostUrl.ToUpperInvariant());

    var timeStampBytes = BitConverter.GetBytes(Convert.ToInt64(context.Request.Headers[WopiRequestHeader.TIME_STAMP])).Reverse().ToArray();

    // Build expected proof
    List<byte> expected = new List<byte>(
        4 + accessTokenBytes.Length +
        4 + hostUrlBytes.Length +
        4 + timeStampBytes.Length);

    // Add the values to the expected variable
    expected.AddRange(BitConverter.GetBytes(accessTokenBytes.Length).Reverse().ToArray());
    expected.AddRange(accessTokenBytes);
    expected.AddRange(BitConverter.GetBytes(hostUrlBytes.Length).Reverse().ToArray());
    expected.AddRange(hostUrlBytes);
    expected.AddRange(BitConverter.GetBytes(timeStampBytes.Length).Reverse().ToArray());
    expected.AddRange(timeStampBytes);
    byte[] expectedBytes = expected.ToArray();

    return (VerifyProofKeys(expectedBytes, requestProof, wopiProofPublicKey.Value) ||
        VerifyProofKeys(expectedBytes, requestProofOld, wopiProofPublicKey.Value) ||
        VerifyProofKeys(expectedBytes, requestProof, wopiProofPublicKey.OldValue));
}

private string GetAbsolouteUrl(HttpContext context)
{
    var url = $"{_wopiUrlService.ApiAddress.TrimEnd('/')}{context.Request.Path}{context.Request.QueryString}";
    return url.Replace(":44300", "").Replace(":443", "");
}

private bool VerifyProofKeys(byte[] expectedProof, string proofFromRequest, string discoPublicKey)
{
    using (RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider())
    {
        try
        {
            rsaProvider.ImportCspBlob(Convert.FromBase64String(discoPublicKey));
            return rsaProvider.VerifyData(expectedProof, "SHA256", Convert.FromBase64String(proofFromRequest));
        }
        catch (FormatException)
        {
            return false;
        }
        catch (CryptographicException)
        {
            return false;
        }
    }
}

This implementation follows the document spec. For the method _wopiDiscovery.GetWopiProof(), I just get proof-key section from Wopi Discovery.

getting wopi proof key in wopi discovery.

  • 1
    you are a life saver... this code worked like a charm many thanks to you it has helped me a lot. – muhammad hasnain Nov 01 '20 at 23:26
  • In the end, I finally made a PHP service that does it properly, you can find it here: https://github.com/Champs-Libres/wopi-lib/blob/master/src/Service/WopiProofValidator.php – Pol Dellaiera Sep 06 '21 at 06:24
0

@Rachanee is your solution working? I have the same code but validation is failing.

muhammad hasnain
  • 501
  • 4
  • 16
0

You can find a WOPI Proof Validator in PHP, in this package: https://github.com/Champs-Libres/wopi-lib/

This package is a helper for integrating WOPI protocol in PHP applications.

It contains a WOPI Proof Validator service that you can use out of the box.

Pol Dellaiera
  • 127
  • 1
  • 8