Problem Description
I’m having some problems signing an ActivityPub Accept
activity. The error I’m getting is:
{"error":"Verification failed for russell@podcastperformance.com https://podcastperformance.com/users/russell using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)","signed_string":"(request-target): post /users/russell/inbox\nhost: podcastindex.social\ndate: Sat, 08 Jun 2024 16:29:26 GMT\ndigest: SHA-256=oLv+MpAd569crts9yw+vajks8CsMZ3R/KqhMZaACVUY=","signature":"d0485QzmCxFPsPIZunc3OKUydYbLaRgPlnjHN4hI1cR4eQ4Y4vrRXvdP2TUfiEaMnnqaXT+Kshv8ugAKyxCw90KSmbyYLxZw+eOHxMaX/xNAhGsgivV6ZKMAwgrO1iyqrOxohCflq/c0hIhw2WMuEAoya0ag7L3dFyPn3hwNYr+eAt/Y1RohEj96cYyizHzX//op1SQ/zW1fWmbksmefiDF0v/Nt0fqU9TGXdTR5VFEZQQLtqBUTb8SFe7nD9u4UTIyhrqAqRzfOe9fLpyUkMZ0lgrjuMvixhqamyBMeS2Sks9TIiEnSdVfoShfUoxeQZ3nCx5Sw6jb5je4H1/0rsw=="}%
Code Details
Function to Handle Follow Activity
async function handleFollowActivity(db, user, activity) {
await db.followUser(user.username, activity.actor);
const acceptActivity = {
"@context": "https://www.w3.org/ns/activitystreams",
"summary": "User accepted a follow",
"id": `https://podcastperformance.com/activities/${generateUniqueId()}`,
"type": "Accept",
"actor": `https://podcastperformance.com/users/${user.username}`,
"object": activity.id
};
const keyId = `https://podcastperformance.com/users/${user.username}#main-key`;
const path = `/users/${user.username}/inbox`;
const host = 'podcastperformance.com';
const signatureResult = await db.generateSignature(acceptActivity, path, 'POST', host);
const { signature, date, digest } = signatureResult;
// Send the Accept activity to the actor's inbox
await sendActivityToInbox(activity.actor, acceptActivity, signatureResult);
}
Function to Generate Signature
const crypto = require('crypto');
const generateSignature = async (data, path, method, host) => {
if (!db) throw new Error('Database not initialized');
const collection = db.collection('podcast_performance_users');
const user = await collection.findOne({ 'username': 'russell' }, { projection: { secretKey: 1 } });
if (!user) {
throw new Error('No User');
}
const privateKey = user.secretKey;
// Generate the digest of the body
const digest = crypto.createHash('sha256').update(JSON.stringify(data)).digest('base64');
// Construct the signing string
const date = new Date().toUTCString();
const signingString = `(request-target): ${method.toLowerCase()} ${path}\n` +
`host: ${host}\n` +
`date: ${date}\n` +
`digest: SHA-256=${digest}`;
// Create a sign object and sign the string
const sign = crypto.createSign('RSA-SHA256');
sign.update(signingString);
sign.end();
// Generate the signature using the private key
const signature = sign.sign(privateKey, 'base64');
return {
signature,
date,
digest
};
}
Function to Send Activity to Inbox
const axios = require('axios');
async function sendActivityToInbox(actor, activity, signatureResult) {
const inboxUrl = actor.endsWith('/') ? `${actor}inbox` : `${actor}/inbox`;
const body = JSON.stringify(activity);
const { signature, date, digest } = signatureResult;
const host = new URL(inboxUrl).host;
const headers = {
'Content-Type': 'application/activity+json',
'Date': date,
'Digest': `SHA-256=${digest}`,
'Signature': `keyId="https://podcastperformance.com/users/russell#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="${signature}"`
};
try {
const response = await axios.post(inboxUrl, body, { headers });
console.log('Activity sent successfully:', response.data);
} catch (error) {
console.error('Error sending activity:', error.response.data);
}
}
Importing Crypto Module
import crypto from 'crypto';
Issue
The signature verification fails, and the error message suggests that the generated signature does not match the expected value. The signed_string
and signature
seem to be correct, but the receiving server rejects them.
Could anyone provide guidance on what might be causing this issue and how to resolve it? Specifically, are there any known issues with the signing process or the headers used in the request? Any help would be greatly appreciated!