ActivityPubにおそるおそる話しかけてみた

2023/7/26 [12:37:55] (水) 天気

ActivityPubを通じてフォローしたり投稿したりするには避けて通れないRSAの署名、というかサインというか。

これ絡みが、もう本当に五里霧中で迷走するばかり。

いろいろ検索しまくってこれでイケるかも?ぐらいのところまで来たので、いったんメモ。


RSAモジュールで公開鍵と秘密鍵

↑鍵を作ってサインする方法は前回のこれで正解。


一番の問題は、サインを作る対象となる文字列をどうするの、というところ。これはなんでもいいんだけど、ActivityPubに通すので、そっちに合わせる必要がある。


結論からいうと必要な材料は以下。

・リクエストのメソッド(POSTとかGET)とリクエストするURI

・相手側のホスト名

・日付、

・本文のDigest

mastodonの仕様だと必須なのは、日付とdigestの2つなんだけど、実際にmastodonから飛んでくるリクエストのログを見ると、この4つが必ず入っている。

オマケでcontent-typeやacceptを入れることもある、らしい。


具体例をあげると

(request-target) POST /actor/inbox
host: example.com
date: Wed, 26 Jul 2023 02:00:32 GMT
digest: SHA-256=unkTWZOjiNdJT4SDuAU6tCy6A0QM0PZ2bs0353xOt+k=
content-type: application/activity+json

「キー 区切り 値」の並び

host「:」「半角空白」example.com

という形式で、キーはすべてアルファベットの小文字。

(request-target)だけはコロンは不要


これら、ひとつひとつを「改行」で繋げて並べた文字列を対象としてサインすることになる。


digestというのは

元データから、ハッシュ関数と呼ばれるあらかじめ定められた計算手順により求められた値です。 ダイジェスト値とも呼ばれます。 ハッシュ値を求めるハッシュ関数(※)には、元データが1ビットでも異なれば大きく異なるハッシュ値が生成される(同じハッシュ値になることが実用上ない)方式が選ばれます。

perlだと「MIME::Base64」のencode_base64という関数をそのまま利用。

encode_base64(本文,"")と、2番めの引数に空を渡さないとDigestに改行がついてくるので注意。

本文というのはfollowやcreateといったActivityPubで使うjson文字列。


対象文字列が決まったら、RSAで署名して、リクエストのヘッダに入れる。

’host’ => ’example.com’,
’date’ => ’Wed, 26 Jul 2023 03:52:07 GMT’,
’digest’ => ’SHA-256=unkTWZOjiNdJT4SDuAU6tCy6A0QM0PZ2bs0353xOt+k=’,
’content-type’ => ’application/activity+json’,
’signature’ => ’keyId="https://tokoroten.doncha.net/t2aki#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="qCEXfTo~略~I9zfRqcUaTwjew=="’,

↑これはperlのlwpを使ってヘッダに設定している部分。

Signatureの

keyIdは「actor.json」に入れた公開鍵のkeyId

algorithmは暗号化の方法「rsa-sha256」


↓適宜改行は入れたけど、こんなリクエストを飛ばす

HTTP_DATE :: Tue, 25 Jul 2023 23:55:38 GMT
HTTP_DIGEST :: SHA-256=giwfKVIT61lc9LpR7EsgEJBmHilheDpPwFnqdjMBjrA=
HTTP_HOST :: example.com
HTTP_SIGNATURE :: keyId="https://tokoroten.doncha.net/t2aki#main-key",
algorithm="rsa-sha256",
headers="(request-target) host date digest content-type",
signature="SE〜略〜JEVPj9

サインの検証は問題ないし、リクエストもこれで問題ない、と思ったんだけど、mastodonだと401で弾かれる。firefishだと202でどうやら受け付けてもらえるっぽい。


同じリクエストを送ってるのになんでこっちはダメでこっちがOKなのか理解できていないのが現状。



その他ハマったところ。


公開鍵って改行されてるけど、actor.jsonのpublicKeyPemに入れるのにどーすんの問題。

jsonは改行入りデータは扱えない。ただただ改行を削除して一行にすればいいんだろ、ぐらいに思ってたんだけど、検索して眺めてると「バックスラッシュ」「n」という2文字の文字列になってるっぽい。これ、改行なのか2文字の文字列のことなのかわけわからず、ほぼ1日弱経過。

perlのJSONでencode_jsonしてみりゃいいことに気づいてやってみたら、2文字の文字列に置換されていた…改行を「バックスラッシュ」「n」の2文字の文字列て、なんか不細工じゃね。


(request-target)ってなんやねん問題。

どのリクエストのヘッダもこれが先頭にあったんで、てっきり、これに続く要素を要求しますよ、という宣言みたいなもんだと思ってたら大きな間違い。こいつもキーのひとつだった。

HTTPメッセージに署名をするSignatureヘッダの標準化


Digestの生成を疑った問題。

リクエストが401で弾かれっぱなしだったんで、どこが問題だろうと。HOSTやDateは目で見てわかる文字列で、そこは大丈夫だったので、Digestが違ってるわけ?と。

perlのDigest::SHA qw(sha256) MIME::Base64 encode_base64(sha256($content),"")で作ったDigestの検証。

コマンドラインで

cat accept.json | head -c -1 | sha256sum | xxd -r -p | base64

などとやって差分のないことを確認。Digestも問題なかった、やっぱり1日弱経過。



実際、まだマストドンからは401で弾かれるので、何か間違えてるはず。

とはいえ、himagine.club(firefish)から202が返ってきたを見てめっちゃ嬉しかったので、ここらでひと段落とするかなあ。これ以上やったらただでさえマダラハゲなのに禿頭一直線になりそうで怖い。


mastodonのSignatureのソースコード

"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"

ActivityPubに対するSignatureのPHPのソースコード

’(request-target) %s %s%s’


あれ?(request-target)の後ろにコロンがいるのかいらないのかよくわからないぞ。コロン一つでverifyの結果が違う…て、どっちでやっても401だったけどな。

[07/27 19:52:17] うまく開通したスクリプトではコロンをつけた文字列。


[07/26 16:29:44] 追記

をっと。202が返ってきても繋がったわけじゃなかった。まだどことも繋がらないってこと。どこが違うんだろ。401じゃないってことはヘッダやサインは問題ない、という理解でいいのかな。これは根が深い。お手上げ、かなあ。


[07/27 11:21:59] 追記

開通!というかActivityPubを通じてFediverse連合空間に入ってfollowもpostもできた!


401の原因。

actor.jsonに記載のpublicKeyPemの始まりのハイフンがコピペミスでひとつ足りなかった。5つ必要なのに4つしかなかった…だけだった。

我ながら雑で阿呆なミスに愕然し、驚愕するばかり。


とはいえ、この何日か、けっこう必死こいて調べまくったので、SignatureやHTTPヘッダについて改めて知ったこともあったので、結果オーライ。


副産物というかverifyするスクリプトやログを片っ端から取るスクリプトを作ったのも今後役に立つだろうと思うし。


image
<<2026/1>>
    123
45678910
11121314151617
18192021222324
25262728293031
検索:

【最近の20件】