How do I use crypto.subtle.importKey to verify the signature sent by Admob SSV ?
The key to verify the signature is available at https://gstatic.com/admob/reward/verifier-keys.json. I can't get importKey to work with it.
3 Replies
I wasn't able to find the answer to your question in the official Deno documentation. However, based on your message, I can provide some general advice on using
crypto.subtle.importKey
in the context of web cryptography.
When using crypto.subtle.importKey
to import a key for signature verification, you typically need to specify the format of the key, the key data, the algorithm the key will be used with, whether the key is extractable, and the key usages.
Here's a generic example of how you might use crypto.subtle.importKey
:
Please note that the above code is a general example and may not directly apply to your situation. You will need to adjust the key format, algorithm, and other parameters according to the actual key and signature algorithm used by Admob SSV.
Since this advice is not based on the Deno documentation, please double-check the accuracy of the advice given, and ensure that it aligns with the specifics of the Admob SSV signature verification process and the format of the keys provided by the URL you mentioned.It looks like that JSON file is using a P-256 key. It matches what OpenSSL generates for prime256v1. So to load the key, this is what you would do.
I don't have any signatures to test it with so can't be sure. Google should really include the algorithm in that verifier-keys.json file.
Thanks ! "spki" was what was wrong in my setup. I can create the key, but the verification fails.
´´´
async function verify(queryUrl: string) {
const {signature, key_id} = queryString.parse(queryUrl);
let queryParamsString = queryUrl;
if (queryParamsString.indexOf('?') > -1) {
queryParamsString = queryUrl.split('?')[1];
}
const contentToVerify = queryParamsString.substring(0, queryParamsString.indexOf('signature') -1);
const googleKeyRes = await axios.get('https://gstatic.com/admob/reward/verifier-keys.json');
const {keys} = googleKeyRes.data;
const base64Decoded = base64.decodeBase64(keys.find((e) => e['keyId'] == key_id).base64);
const key = await crypto.subtle.importKey('spki', base64Decoded, {name: "ECDSA", namedCurve: 'P-256'}, true, ["verify"]);
const encoder = new TextEncoder()
const s = encoder.encode(signature); // "MEQCIG8sJ33C2t7D-0PeLIbO2rxH7c78MS3G3P8-b_KBuQCUAiBUhq06AFVfpDTr8XPaBtSsAGUNZAQysKuJiVuly90g8A"
const d = encoder.encode(contentToVerify); // "ad_network=5450213213286189855&ad_unit=1234567890&reward_amount=1&reward_item=Reward×tamp=1718045362304&transaction_id=123456789&user_id=1"
const result = await crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, key, s, d);
if (result) {
return true;
} else {
throw new Error('Invalid Signature Supplied');
}
};
´´´
This is according to https://developers.google.com/admob/android/ssv, which says to have the query content ("ad_network=" etc) as the content that's signed.
I'm probably doing the encoding wrong I guess but I can't figure out what's wrong about it. It defaults to UTF8, from what I read and the admob doc uses UTF8 as well.