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();
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.
.
#Rachanee is your solution working? I have the same code but validation is failing.
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.
I'm currently having a bit of an issue when I try to upload a photo taking using an iOS camera to a MySQL database using PHP and unfortunately have been unable to find the right help online.
Basically The User takes a photo on their iOS device and I take that raw MediaPromise and put it into a ByteArray. I then call a PHP function using AMFPHP to add the binary to a Blob in my database. But when I test the whole thing, it never seems to work. Could somebody maybe help me with this problem or at least point me in the right direction? It would be highly appreciated. Here's the code:
AS3:
var dataSource:IDataInput;
function imageSelected1(event:MediaEvent) {
var imagePromise:MediaPromise = event.data;
dataSource = imagePromise.open();
if( imagePromise.isAsync ) {
var eventSource:IEventDispatcher = dataSource as IEventDispatcher;
eventSource.addEventListener( Event.COMPLETE, onDataComplete );
}
else {
readMediaData();
}
}
// PHP Connection //
// Database //
var gw2:NetConnection = new NetConnection();
gw2.connect(connectiongoeshere);
// Responder //
var pictureresponder:Responder = new Responder(onpicture);
function onpicture(pictureobj:Object) {
gotoAndStop(1);
}
function onDataComplete( event:Event ):void {
readMediaData();
}
function readMediaData() {
var imageBytes:ByteArray = new ByteArray();
//dataSource.readBytes( imageBytes );
dataSource.readBytes(imageBytes);
gw2.call("class.setData", pictureresponder, imageBytes);
}
PHP:
function setData($ba) {
// Connecting to the database //
mysql_pconnect("hi", "hi", "hi");
mysql_select_db("hi");
$result = mysql_query("UPDATE users set profilepicture2 ='$ba->data' WHERE email= 'email#gmail.com'");
return $result;
}
I would like to parse a string such as p1=6&p2=7&p3=8 into a NameValueCollection.
What is the most elegant way of doing this when you don't have access to the Page.Request object?
There's a built-in .NET utility for this: HttpUtility.ParseQueryString
// C#
NameValueCollection qscoll = HttpUtility.ParseQueryString(querystring);
' VB.NET
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)
You may need to replace querystring with new Uri(fullUrl).Query.
HttpUtility.ParseQueryString will work as long as you are in a web app or don't mind including a dependency on System.Web. Another way to do this is:
NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
string[] parts = segment.Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
queryParameters.Add(key, val);
}
}
A lot of the answers are providing custom examples because of the accepted answer's dependency on System.Web. From the Microsoft.AspNet.WebApi.Client NuGet package there is a UriExtensions.ParseQueryString, method that can also be used:
var uri = new Uri("https://stackoverflow.com/a/22167748?p1=6&p2=7&p3=8");
NameValueCollection query = uri.ParseQueryString();
So if you want to avoid the System.Web dependency and don't want to roll your own, this is a good option.
I wanted to remove the dependency on System.Web so that I could parse the query string of a ClickOnce deployment, while having the prerequisites limited to the "Client-only Framework Subset".
I liked rp's answer. I added some additional logic.
public static NameValueCollection ParseQueryString(string s)
{
NameValueCollection nvc = new NameValueCollection();
// remove anything other than query string from url
if(s.Contains("?"))
{
s = s.Substring(s.IndexOf('?') + 1);
}
foreach (string vp in Regex.Split(s, "&"))
{
string[] singlePair = Regex.Split(vp, "=");
if (singlePair.Length == 2)
{
nvc.Add(singlePair[0], singlePair[1]);
}
else
{
// only one key with no value specified in query string
nvc.Add(singlePair[0], string.Empty);
}
}
return nvc;
}
To do this without System.Web, without writing it yourself, and without additional NuGet packages:
Add a reference to System.Net.Http.Formatting
Add using System.Net.Http;
Use this code:
new Uri(uri).ParseQueryString()
https://msdn.microsoft.com/en-us/library/system.net.http.uriextensions(v=vs.118).aspx
I needed a function that is a little more versatile than what was provided already when working with OLSC queries.
Values may contain multiple equal signs
Decode encoded characters in both name and value
Capable of running on Client Framework
Capable of running on Mobile Framework.
Here is my solution:
Public Shared Function ParseQueryString(ByVal uri As Uri) As System.Collections.Specialized.NameValueCollection
Dim result = New System.Collections.Specialized.NameValueCollection(4)
Dim query = uri.Query
If Not String.IsNullOrEmpty(query) Then
Dim pairs = query.Substring(1).Split("&"c)
For Each pair In pairs
Dim parts = pair.Split({"="c}, 2)
Dim name = System.Uri.UnescapeDataString(parts(0))
Dim value = If(parts.Length = 1, String.Empty,
System.Uri.UnescapeDataString(parts(1)))
result.Add(name, value)
Next
End If
Return result
End Function
It may not be a bad idea to tack <Extension()> on that too to add the capability to Uri itself.
If you don't want the System.Web dependency, just paste this source code from HttpUtility class.
I just whipped this together from the source code of Mono. It contains the HttpUtility and all it's dependencies (like IHtmlString, Helpers, HttpEncoder, HttpQSCollection).
Then use HttpUtility.ParseQueryString.
https://gist.github.com/bjorn-ali-goransson/b04a7c44808bb2de8cca3fc9a3762f9c
If you want to avoid the dependency on System.Web that is required to use HttpUtility.ParseQueryString, you could use the Uri extension method ParseQueryString found in System.Net.Http.
Make sure to add a reference (if you haven't already) to System.Net.Http in your project.
Note that you have to convert the response body to a valid Uri so that ParseQueryString (in System.Net.Http)works.
string body = "value1=randomvalue1&value2=randomValue2";
// "http://localhost/query?" is added to the string "body" in order to create a valid Uri.
string urlBody = "http://localhost/query?" + body;
NameValueCollection coll = new Uri(urlBody).ParseQueryString();
I just realized that Web API Client has a ParseQueryString extension method that works on a Uri and returns a HttpValueCollection:
var parameters = uri.ParseQueryString();
string foo = parameters["foo"];
private void button1_Click( object sender, EventArgs e )
{
string s = #"p1=6&p2=7&p3=8";
NameValueCollection nvc = new NameValueCollection();
foreach ( string vp in Regex.Split( s, "&" ) )
{
string[] singlePair = Regex.Split( vp, "=" );
if ( singlePair.Length == 2 )
{
nvc.Add( singlePair[ 0 ], singlePair[ 1 ] );
}
}
}
Just access Request.QueryString. AllKeys mentioned as another answer just gets you an array of keys.
HttpUtility.ParseQueryString(Request.Url.Query) return is HttpValueCollection (internal class). It inherits from NameValueCollection.
var qs = HttpUtility.ParseQueryString(Request.Url.Query);
qs.Remove("foo");
string url = "~/Default.aspx";
if (qs.Count > 0)
url = url + "?" + qs.ToString();
Response.Redirect(url);
Since everyone seems to be pasting his solution.. here's mine :-)
I needed this from within a class library without System.Web to fetch id parameters from stored hyperlinks.
Thought I'd share because I find this solution faster and better looking.
public static class Statics
public static Dictionary<string, string> QueryParse(string url)
{
Dictionary<string, string> qDict = new Dictionary<string, string>();
foreach (string qPair in url.Substring(url.IndexOf('?') + 1).Split('&'))
{
string[] qVal = qPair.Split('=');
qDict.Add(qVal[0], Uri.UnescapeDataString(qVal[1]));
}
return qDict;
}
public static string QueryGet(string url, string param)
{
var qDict = QueryParse(url);
return qDict[param];
}
}
Usage:
Statics.QueryGet(url, "id")
Hit up Request.QueryString.Keys for a NameValueCollection of all query string parameters.
To get all Querystring values try this:
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)
Dim sb As New StringBuilder("<br />")
For Each s As String In qscoll.AllKeys
Response.Write(s & " - " & qscoll(s) & "<br />")
Next s
var q = Request.QueryString;
NameValueCollection qscoll = HttpUtility.ParseQueryString(q.ToString());
I translate to C# version of josh-brown in VB
private System.Collections.Specialized.NameValueCollection ParseQueryString(Uri uri)
{
var result = new System.Collections.Specialized.NameValueCollection(4);
var query = uri.Query;
if (!String.IsNullOrEmpty(query))
{
var pairs = query.Substring(1).Split("&".ToCharArray());
foreach (var pair in pairs)
{
var parts = pair.Split("=".ToCharArray(), 2);
var name = System.Uri.UnescapeDataString(parts[0]);
var value = (parts.Length == 1) ? String.Empty : System.Uri.UnescapeDataString(parts[1]);
result.Add(name, value);
}
}
return result;
}
let search = window.location.search;
console.log(search);
let qString = search.substring(1);
while(qString.indexOf("+") !== -1)
qString = qString.replace("+", "");
let qArray = qString.split("&");
let values = [];
for(let i = 0; i < qArray.length; i++){
let pos = qArray[i].search("=");
let keyVal = qArray[i].substring(0, pos);
let dataVal = qArray[i].substring(pos + 1);
dataVal = decodeURIComponent(dataVal);
values[keyVal] = dataVal;
}
This is my code, I think it's very useful:
public String GetQueryString(string ItemToRemoveOrInsert = null, string InsertValue = null )
{
System.Collections.Specialized.NameValueCollection filtered = new System.Collections.Specialized.NameValueCollection(Request.QueryString);
if (ItemToRemoveOrInsert != null)
{
filtered.Remove(ItemToRemoveOrInsert);
if (!string.IsNullOrWhiteSpace(InsertValue))
{
filtered.Add(ItemToRemoveOrInsert, InsertValue);
}
}
string StrQr = string.Join("&", filtered.AllKeys.Select(key => key + "=" + filtered[key]).ToArray());
if (!string.IsNullOrWhiteSpace(StrQr)){
StrQr="?" + StrQr;
}
return StrQr;
}
I create a serive api using asp.net and a methoad POST upload a any file, and I want using code php call that api, but i don't known how.
Code asp.net api upload file as follows:
public Task<IEnumerable<string>> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
MyMultipartFormDataStreamProvider streamProvider = new MyMultipartFormDataStreamProvider(fullPath);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var fileInfo = streamProvider.FileData.Select(i =>
{
var info = new FileInfo(i.LocalFileName);
return "File uploaded as " + info.FullName + " (" + info.Length + ")";
});
return fileInfo;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
}
}
And class:
public class MyMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public MyMultipartFormDataStreamProvider(string path)
: base(path)
{
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
string fileName;
if (!string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName))
{
fileName = headers.ContentDisposition.FileName;
}
else
{
fileName = Guid.NewGuid().ToString() + ".data";
}
return fileName.Replace("\"", string.Empty);
}
}
So, how to I can call this api using code php ?
I referenced code to: http://www.codeguru.com/csharp/.net/uploading-files-asynchronously-using-asp.net-web-api.htm
To me it looks like you have a php website and looked on the web for an upload example and found a asp.net one.
If you got php running why not use php for the upload?
How to get blob image from mysql to as3 with php and addChild it in a way of getting byteArray and converting it to image?
this is php:
if($RequestType == 'Select'){
while($row=mysql_fetch_assoc($results)){
$arrdata[]=$row;
}
foreach($arrdata as $key=>$value){
$output[$key] = $arrdata[$key];
$output[$key]['png'] = base64_encode($arrdata[$key]['png']);
}
header('Content-type: application/json');
print(json_encode($output));
i've already got something like this, but longer:
VBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAC4jAAAuIwF4pT92AAAYxUlEQVR42u1bB3Sc1ZWe8k/vvfem6aPpGk2RZjQzGrVRl6xiyZZkWS4gFxyDGyEsxjE1gVBTSGAdQg4keyCxF4NsY1pCDARsBwiE4pBsIJjN7nKWgM3eJ7/hTDhUIyJyNj7nHh3P/DP/f79373e/e98b0oUXXkj6/2ykfwLwTwAW58br1q0j9fX1UQcGBpgjw0Pcy
(In this I'm assuming you have already handled the HTTP request result)
To transform a base64 encoded image to a DisplayObject, you first need to decode the Base64 to a ByteArray then use the Loader class to load the image content, listening for the Loader's Event.COMPLETE event to dispatch. Once you've got that event Loader.content will contain a DisplayObject that you can add to the screen using addChild().
Example: (EDIT: Added an Array to keep track of loaded objects in order they were requested and altered function to calls to cope)
private var countLoadedImages:int = 0;//track how many images have loaded
private var arrImageLoaders:Array = new Array();//store loaders for call back
public function loadImageFromBase64(yourBase64:String):void
{
var base64:Base64Decoder = new Base64Decoder();
base64.decode(yourBase64);//decode your image
var data:ByteArray = base64.toByteArray();//convert to byte array
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
arrImageLoaders.push(loader.contentLoaderInfo);//add to array in order func was called
loader.loadBytes(data);//hand over to Loader
}
protected function onLoadComplete(event:Event):void
{
countLoadedImages++;
event.target.removeEventListener(Event.COMPLETE,onLoadComplete);
if (countLoadedImages == arrImageLoaders.length)
{
allImagesLoaded();
}
}
protected function allImagesLoaded():void
{
var contentLoaderInfo:LoaderInfo
for (var x:int = 0; x<arrImageLoaders.length; x++)
{
contentLoaderInfo = arrImageLoaders[x] as LoaderInfo;
addChild(contentLoaderInfo.content);
}
}