我正在尝试使用证明密钥验证请求是否来自 Office Online
我正在使用 nodejs 实现 wopi 主机,上面的链接中有代码示例使用 .NET 实现了证明密钥过程,因此我使用edge包能够使用 nodejs 运行 .NET 代码。
问题是代码运行良好,但它总是返回请求中的证明密钥无效,即使请求来自 wopi 客户端也是如此。
解释:有 3 个函数:
validateProofKey - 主函数 - 获取所有需要的参数来验证证明密钥,如果它们有效,则使用以下函数返回 bool:constructProofKey和TryProofkeyVerification
构造ProofKey - 声明 .NET 函数(来自示例代码的 constructProofKey),运行它并返回构造的证明
密钥 TryProofkeyVerification的结果- 声明 .NET 函数(来自 .NET 示例代码的 TryProofkeyVerification),运行它并返回布尔结果
编码 :
static async validateProofKey(access_token: string, request_url: string, X_WOPI_TimeStamp: string, X_WOPI_Proof: string, X_WOPI_Proof_old: string): Promise<boolean> {
//get public key provided in WOPI discovery:
if (!wopiDiscovery) { wopiDiscovery = await this.getWopiDiscovery() }
const public_Keys: iWopiDiscoveryProofKey = wopiDiscovery["wopi-discovery"]["proof-key"];
const public__Key: string = public_Keys.value;
const old_public_Key: string = public_Keys.oldvalue;
this.printProofkeyArgs(access_token, request_url, X_WOPI_TimeStamp, X_WOPI_Proof, X_WOPI_Proof_old, public__Key, old_public_Key, modulus_b64, exp_b64);
//1. Create the expected value of the proof headers.
let expectedProofKey: Buffer = await this.constructProofKey(access_token, request_url, X_WOPI_TimeStamp);
//2.Use the public key provided in WOPI discovery to decrypt the proof provided in the X-WOPI-Proof header.
const verified: boolean = await this.TryProofkeyVerification(expectedProofKey, X_WOPI_Proof, public__Key) ||
await this.TryProofkeyVerification(expectedProofKey, X_WOPI_Proof_old, public__Key) ||
await this.TryProofkeyVerification(expectedProofKey, X_WOPI_Proof, old_public_Key)
return verified;
}
构造证明密钥
private static constructProofKey(access_token: string, request_url: string, X_WOPI_TimeStamp: string): Promise<Buffer> {
return new Promise((resolve, reject) => {
const proofParams = {
access_token: access_token,
request_url: request_url,
X_WOPI_TimeStamp: X_WOPI_TimeStamp
}
const constructProofKey = edge.func(`
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class Startup
{
public async Task<object> Invoke(dynamic proofParams)
{
return Helper.constructProofKey(proofParams);
}
}
static class Helper
{
public static byte[] constructProofKey(dynamic proofParams)
{
// Encode values from headers into byte[]
long timestamp = Convert.ToInt64(proofParams.X_WOPI_TimeStamp);
var accessTokenBytes = Encoding.UTF8.GetBytes(proofParams.access_token);
var hostUrlBytes = Encoding.UTF8.GetBytes(proofParams.request_url.ToUpperInvariant());
var timeStampBytes = EncodeNumber(timestamp);
// prepare a list that will be used to combine all those arrays together
List < byte > expectedProof = new List<byte>(
4 + accessTokenBytes.Length +
4 + hostUrlBytes.Length +
4 + timeStampBytes.Length);
expectedProof.AddRange(EncodeNumber(accessTokenBytes.Length));
expectedProof.AddRange(accessTokenBytes);
expectedProof.AddRange(EncodeNumber(hostUrlBytes.Length));
expectedProof.AddRange(hostUrlBytes);
expectedProof.AddRange(EncodeNumber(timeStampBytes.Length));
expectedProof.AddRange(timeStampBytes);
// create another byte[] from that list
byte[] expectedProofArray = expectedProof.ToArray();
return expectedProofArray;
}
private static byte[] EncodeNumber(long value)
{
return BitConverter.GetBytes(value).Reverse().ToArray();
}
}
`)
constructProofKey(proofParams, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
});
})
}
TryProofkeyVerification
private static TryProofkeyVerification(expectedProofKey: Buffer, private_proofkey: string, public_proofkey: string): Promise<boolean> {
return new Promise((resolve, reject) => {
const proofParams = {
expectedProofKey: expectedProofKey,
private_proofkey: private_proofkey,
public_proofkey: public_proofkey
}
//declare .NET code
const TryVerification = edge.func(`
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class Startup
{
public async Task<object> Invoke(dynamic proofParams)
{
return Helper.TryVerification(proofParams);
}
}
static class Helper
{
public static bool TryVerification(dynamic proofParams)
{
byte[] expectedProofKey = (byte[])proofParams.expectedProofKey;
using(RSACryptoServiceProvider rsaAlg = new RSACryptoServiceProvider())
{
byte[] publicKey = Convert.FromBase64String(proofParams.public_proofkey);
byte[] signedProofBytes = Convert.FromBase64String(proofParams.private_proofkey);
try
{
rsaAlg.ImportCspBlob(publicKey);
return rsaAlg.VerifyData(expectedProofKey, "SHA256", signedProofBytes);
}
catch(FormatException)
{
Console.WriteLine("Format Exception");
return false;
}
catch(CryptographicException e)
{
Console.WriteLine("CryptographicException Exception");
return false;
}
}
}
}
`);
//Invoke .NET code
TryVerification(proofParams, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
});
})
}
注意 - 我已经将参数记录到控制台以确保参数在所有过程中正确传输