Chromeの履歴はSQLiteらしいのでいじってみる
概要
- Chromeの履歴はSQLiteで管理されているらしく、容易に中身を見れそうなので試してみました。
- 自分のChromeの履歴を用いて、どのウェブサイトに頻繁にアクセス(回数、滞在時間)しているか見てみます。
おことわり
- SQLiteの.schemaコマンドを用いることで、どのようなテーブルが定義されているかは分かりますが、その使われ方(どのようなイベントが起きた時に、レコードが積まれるのか・レコードが更新されるのか)について明確に説明している公式情報を見つけられませんでした。
- そのため、今回実行したSQLが目的の情報を正しく取得するものとなっているかは微妙なところがあります。
テーブル定義を見てみる
Historyファイル*1をsqlite3コマンドがある環境にコピーして開きます。.schemaコマンドで、定義されているテーブルを確認すると
以下、17のテーブルが定義されていました。
urls、visits、keyword_search_termsあたりに面白そうなデータが入っていそうです。
CREATE TABLE meta(
key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,
value LONGVARCHAR);
CREATE TABLE downloads (
id INTEGER PRIMARY KEY,
guid VARCHAR NOT NULL,
current_path LONGVARCHAR NOT NULL,
target_path LONGVARCHAR NOT NULL,
start_time INTEGER NOT NULL,
received_bytes INTEGER NOT NULL,
total_bytes INTEGER NOT NULL,
state INTEGER NOT NULL,
danger_type INTEGER NOT NULL,
interrupt_reason INTEGER NOT NULL,
hash BLOB NOT NULL,
end_time INTEGER NOT NULL,
opened INTEGER NOT NULL,
last_access_time INTEGER NOT NULL,
transient INTEGER NOT NULL,
referrer VARCHAR NOT NULL,
site_url VARCHAR NOT NULL,
tab_url VARCHAR NOT NULL,
tab_referrer_url VARCHAR NOT NULL,
http_method VARCHAR NOT NULL,
by_ext_id VARCHAR NOT NULL,
by_ext_name VARCHAR NOT NULL,
etag VARCHAR NOT NULL,
last_modified VARCHAR NOT NULL,
mime_type VARCHAR(
255) NOT NULL,
original_mime_type VARCHAR(
255) NOT NULL,
embedder_download_data VARCHAR NOT NULL DEFAULT '');
CREATE TABLE downloads_url_chains (
id INTEGER NOT NULL,
chain_index INTEGER NOT NULL,
url LONGVARCHAR NOT NULL,
PRIMARY KEY (id, chain_index) );
CREATE TABLE downloads_slices (
download_id INTEGER NOT NULL,
offset INTEGER NOT NULL,
received_bytes INTEGER NOT NULL,
finished INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (download_id, offset) );
CREATE TABLE typed_url_sync_metadata (
storage_key INTEGER PRIMARY KEY NOT NULL,
value BLOB);
CREATE TABLE IF NOT EXISTS "urls"(
id INTEGER PRIMARY KEY AUTOINCREMENT,
url LONGVARCHAR,
title LONGVARCHAR,
visit_count INTEGER DEFAULT 0 NOT NULL,
typed_count INTEGER DEFAULT 0 NOT NULL,
last_visit_time INTEGER NOT NULL,
hidden INTEGER DEFAULT 0 NOT NULL);
CREATE TABLE sqlite_sequence(
name,
seq);
CREATE TABLE visit_source(
id INTEGER PRIMARY KEY,
source INTEGER NOT NULL);
CREATE TABLE keyword_search_terms (
keyword_id INTEGER NOT NULL,
url_id INTEGER NOT NULL,
term LONGVARCHAR NOT NULL,
normalized_term LONGVARCHAR NOT NULL);
CREATE TABLE segments (
id INTEGER PRIMARY KEY,
name VARCHAR,
url_id INTEGER NON NULL);
CREATE TABLE segment_usage (
id INTEGER PRIMARY KEY,
segment_id INTEGER NOT NULL,
time_slot INTEGER NOT NULL,
visit_count INTEGER DEFAULT 0 NOT NULL);
CREATE TABLE content_annotations (
visit_id INTEGER PRIMARY KEY,
floc_protected_score DECIMAL(
3,
2),
categories VARCHAR,
page_topics_model_version INTEGER,
annotation_flags INTEGER DEFAULT 0 NOT NULL,
entities VARCHAR,
related_searches VARCHAR,
visibility_score NUMERIC DEFAULT -1,
search_normalized_url,
search_terms LONGVARCHAR,
alternative_title);
CREATE TABLE context_annotations(
visit_id INTEGER PRIMARY KEY,
context_annotation_flags INTEGER DEFAULT 0 NOT NULL,
duration_since_last_visit INTEGER,
page_end_reason INTEGER,
total_foreground_duration NUMERIC DEFAULT -1000000);
CREATE TABLE clusters(
cluster_id INTEGER PRIMARY KEY,
score NUMERIC NOT NULL);
CREATE TABLE clusters_and_visits(
cluster_id INTEGER NOT NULL,
visit_id INTEGER NOT NULL,
score NUMERIC NOT NULL,
PRIMARY KEY(cluster_id, visit_id))WITHOUT ROWID;
CREATE TABLE downloads_reroute_info (
download_id INTEGER NOT NULL,
reroute_info_serialized VARCHAR NOT NULL,
PRIMARY KEY (download_id) );
CREATE TABLE IF NOT EXISTS "visits"(
id INTEGER PRIMARY KEY AUTOINCREMENT,
url INTEGER NOT NULL,
visit_time INTEGER NOT NULL,
from_visit INTEGER,
transition INTEGER DEFAULT 0 NOT NULL,
segment_id INTEGER,
visit_duration INTEGER DEFAULT 0 NOT NULL,
incremented_omnibox_typed_score BOOLEAN DEFAULT FALSE NOT NULL,
opener_visit INTEGER,
originator_cache_guid TEXT,
originator_visit_id INTEGER);
集計してみる
どれくらい頻繁にウェブサイトにアクセスしているか確認したいので、urls、visitsのテーブルを用いれば良さそうです。
ドメイン単位に、どれくらいの時間閲覧していたか、どれくらいの回数アクセスしたかを集計するSQLを書いてみます。
閲覧時間について
閲覧時間が長いドメイン順にソートして、上位5件を取得しました。
sqlite> WITH joined_http_url AS (
...> SELECT replace(replace(urls.url, 'https://', ''), 'http://', '') AS replaced,
...> visits.visit_duration AS duration
...> FROM visits
...> LEFT JOIN urls
...> ON visits.url = urls.id
...> WHERE urls.url LIKE 'https://%' or urls.url LIKE 'http://%'
...> ), per_domain AS (
...> SELECT CASE
...> WHEN instr(replaced, '/') > 0 THEN substr(replaced, 1, instr(replaced, '/') -1)
...> ELSE replaced
...> END AS domain,
...> duration
...> FROM joined_http_url
...> ), aggregation AS (
...> SELECT domain,
...> sum(duration) AS total
...> FROM per_domain
...> GROUP BY domain
...> )
...> SELECT (total / 3600 / 1000000) || ' hours ' || strftime('%M minutes %S seconds', total / 1000000 / 86400.0) as total_duration,
...> domain
...> FROM aggregation
...> ORDER BY total DESC
...> LIMIT 5
...> ;
total_duration domain
107 hours 44 minutes 31 seconds www.google.com
99 hours 44 minutes 21 seconds docs.python.org
82 hours 56 minutes 20 seconds github.com
64 hours 38 minutes 15 seconds www.rfc-editor.org
64 hours 36 minutes 36 seconds www.ietf.org
アクセス回数について
アクセス回数が多いドメイン順にソートして、上位5件を取得しました。
sqlite> WITH http_url AS ( ...> SELECT replace(replace(urls.url, 'https://', ''), 'http://', '') AS replaced, ...> urls.visit_count ...> FROM urls ...> WHERE urls.url LIKE 'https://%' or urls.url LIKE 'http://%' ...> ), per_domain AS ( ...> SELECT CASE ...> WHEN instr(replaced, '/') > 0 THEN substr(replaced, 1, instr(replaced, '/') -1) ...> ELSE replaced ...> END AS domain, ...> visit_count ...> FROM http_url ...> ), aggregation AS ( ...> SELECT domain, ...> sum(visit_count) AS total ...> FROM per_domain ...> GROUP BY domain ...> ) ...> SELECT total, ...> domain ...> FROM aggregation ...> ORDER BY total DESC ...> LIMIT 5 ...> ; total domain 3240 www.google.com 1340 github.com 1326 docs.google.com 872 twitter.com 395 www.cambly.com
それらしいデータを得ることはできましたが、どうも閲覧時間の方の集計について 主観よりも長い時間になっているように思えます。ページを開きっぱなしにして、別のタブに移動していたとしても、アクセス時間として計上される仕様なのかもしれません。(そのあたりは十分に調べられていません)
what3wordsをDNSで引けるようにした
概要
what3wordsというサービスを使うと、地球上のあらゆる場所を3つの単語で表せます。
例えば自由の女神の松明の場所はtoned.melt.shipで表せます。
https://w3w.co/toned.melt.ship
これを次のように、ドメイン名部分に単語を含ませる形でアクセスできるようにしてみました。
http://toned.melt.ship.point.place
仕組み
上記のpoint.placeドメインにアクセスすると、w3w.coが提供する本来のurlへリダイレクトするようにしています。
IDN対応
what3wordsでは、英単語のみならず日本語の単語でも場所を表すことができます。
例えば、次の単語も自由の女神の場所を表します。
あかり。ふえた。くねくね
ドメイン名は、英単語(ascii文字)のみならず日本語等も表現することができます。
そのため、以下を用いても同様にアクセスすることができます。
完備化の問題をいくつか解いてみたーその2
概要
拙作の完備化ツールをいろいろ使ってみたメモのその2です。 今回は、簡単な状態遷移図に対して用いてみます。 その1はこちらです。115個の完備化の問題を解いています。
やってみた
今回ツールに与える状態遷移図は以下のようにします。 これは、RFC 8555に出てくるとある状態遷移図をアレンジしたものです。

ツールに入力できる形式で表すと以下の通りです。
trnという関数が、状態遷移を示しています。第二引数の状態に於いて、第一引数で示すトリガーが生じると、次の状態に遷移するということを表しています。
$ cat data (VAR x y z) (RULES trn(INIT, INIT) -> PENDING trn(ERROR, PENDING) -> INVALID trn(RECEIVEREQUEST, PENDING) -> READY trn(ERROR, READY) -> INVALID trn(RECEIVEDATA, READY) -> PROCESSING trn(ERROR, PROCESSING) -> INVALID trn(FINISHCREATION, PROCESSING) -> VALID car(cons(x,y)) -> x cdr(cons(x,y)) -> y cdr(NIL) -> NIL apply(NIL, x) -> x apply(x, y) -> apply(cdr(x), trn(car(x),y)) trace(x) -> apply(x, INIT) )
状態遷移図を辿る際に便利な関数traceを定義する為に、trn以外の諸々の関数を定義しています。
traceは、引数に1つのリストをとります。リストは状態遷移の為のトリガーを並べたものです。
traceは、リスト内のトリガー(操作)を全て実行した際の状態を返す関数です。
上記を完備化した書き換え系の下で、次の2つの項が何になるか確認してみたいと思います。 \begin{align} trace([INIT, RECEIVEREQUEST, RECEIVEDATA])\\ trace([INIT, ERROR]) \end{align}
$ wget https://github.com/moratori/clover/releases/download/v2.4.2/clover-linux-x86_64_v2.4.2 && chmod +x clover-linux-x86_64_v2.4.2 $ ./clover-linux-x86_64_v2.4.2 rewrite data 'trace([INIT, RECEIVEREQUEST, RECEIVEDATA])' 'trace([INIT, ERROR])' YES (VAR z x y) (RULES trn(car(NIL),x) -> x apply(cons(y,x),z) -> apply(x,trn(y,z)) apply(cdr(x),trn(car(x),y)) -> apply(x,y) trace(x) -> apply(x,INIT) apply(NIL,x) -> x cdr(NIL) -> NIL cdr(cons(x,y)) -> y car(cons(y,x)) -> y trn(FINISHCREATION,PROCESSING) -> VALID trn(ERROR,PROCESSING) -> INVALID trn(RECEIVEDATA,READY) -> PROCESSING trn(ERROR,READY) -> INVALID trn(RECEIVEREQUEST,PENDING) -> READY trn(ERROR,PENDING) -> INVALID trn(INIT,INIT) -> PENDING ) (COMMENT ERROR < FINISHCREATION < INIT < INVALID < NIL < PENDING < PROCESSING < READY < RECEIVEDATA < RECEIVEREQUEST < VALID < TRN < APPLY < TRACE < CAR < CDR < CONS ) (COMMENT trace([INIT, RECEIVEREQUEST, RECEIVEDATA]) reduced PROCESSING trace([INIT, ERROR]) reduced INVALID )
最後に出力されている通り、 $trace([INIT, RECEIVEREQUEST, RECEIVEDATA])$は、$PROCESSING$となることが分かりました。 また、$trace([INIT, ERROR])$は、$INVALID$となることが分かりました。
完備化の問題をいくつか解いてみた
概要
先の記事に書きましたとおり、等式の完備化ツールをCommon Lispで作ってみています。
このツールを使って、いくつかの問題を実際に完備化してみたいと思いました。
試してみる
とは言っても、いい感じの入力となる問題を自分で作るのは難しいなと思っていたところ、以下を見つけました。
https://www.jaist.ac.jp/project/maxcomp/experiments/eq_systems.tar.gz
こちらは、JAISTの廣川研究室で開発されているツールMaxcomp(Maximal Completion)の 評価に用いられている問題のようです。
こちらの問題(合計115個)を利用させていただき、実行してみたところ以下の通りの結果となりました。
| 項目 | 値 |
|---|---|
| 実行環境(CPU) | Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz |
| 実行環境(メモリ) | 6GB |
| 実行環境(OS) | Ubuntu 20.04.1 LTS on WSL2 |
| 完備化できた問題の数 | 65 |
| 完備化に失敗した問題の数 | 6 |
| タイムアウトした問題の数 | 44 |
| 合計問題数 | 115 |
一つの問題ごとに、タイムアウトは30秒を設定しています。 だいたい、半分程度の問題を完備化するにとどまりました。
結果の詳細
補足
以下のように実行しています。
$ wget https://github.com/moratori/clover/releases/download/v2.2.1/clover-linux-x86_64_ver2.2.1 $ chmod +x clover-linux-x86_64_ver2.2.1 $ time timeout -k 3 30 ./clover-linux-x86_64_ver2.2.1 complete slothrop_ackermann.trs a(s(x),s(y))=>a(x,a(s(x),y)) a(s(x),Z)=>a(x,s(Z)) a(Z,x)=>s(x) real 0m0.170s user 0m0.227s sys 0m0.042s $
Common LispでKnuth-Bendixの完備化アルゴリズムを実装した
概要
Knuth-Bendixの完備化アルゴリズムを実装したツールを作ってみました。いくつかの簡単な問題に対して使ってみながら紹介します。
なお、Knuth-Bendixの完備化アルゴリズムの詳細自体については、こちらのページが分かりやすいです。
インストールの仕方
Linux/Windowsのそれぞれ用に、実行可能バイナリをビルドしました。 こちらからダウンロードできます。
Releases · moratori/clover · GitHub
Common Lisp(と、roswell)の実行環境がある場合は、リポジトリをクローンしてコードを取得しても大丈夫です。
実行してみる(その1)
実行権限を確認して、ダブルクリックなりで起動します。 コードを取得しroswell経由で実行する場合は以下の通り、起動スクリプトを叩いてください。
$ ./roswell/clover.ros
実行するとREPLに入るので、そこに完備化したい等式を入力していきます。 例えば、群の公理を示す以下の3つの等式を入力します。
\begin{align} plus(ZERO, x)& = x\\ plus(plus(x,y),z)& = plus(x, plus(y,z))\\ plus(i(x),x)& = ZERO \end{align}
(NIL)>>> :def-axiom group input . to finish definition axiom[1]>>> plus(ZERO,x) = x axiom[2]>>> plus(plus(x,y),z) = plus(x, plus(y,z)) axiom[3]>>> plus(i(x),x) = ZERO axiom[4]>>> . Detected that a set of equations has been inputted. Do you want to execute completion algorithm? (yes or no) yes The completion process was successful: i(plus(y,x))=>plus(i(x),i(y)) i(i(x))=>x i(ZERO)=>ZERO plus(x,ZERO)=>x plus(x,i(x))=>ZERO plus(x,plus(i(x),y))=>y plus(i(x),plus(x,y))=>y plus(ZERO,x)=>x plus(plus(x,y),z)=>plus(x,plus(y,z)) plus(i(x),x)=>ZERO (group)>>>
完備化が成功し、10個の項書き換え規則が出力されたことが分かります。 この状態で証明したい等式、例えば $plus(plus(x,y),plus(z,w)) = plus(x, plus(y, plus(z, w)))$ を入力してみます。
すると、想定通り証明することができます。
(group)>>> plus(plus(x,y),plus(z,w)) = plus(x, plus(y, plus(z, w))) The equation can be PROVED under the axiom group irreducible form under the group: plus(x,plus(y,plus(z,w))) = plus(x,plus(y,plus(z,w))) (group)>>>
先ほどの項書き換え規則の下で両辺の既約形が同じになった為、等式が成り立つことが分かります。
実行してみる(その2)
続いて別の問題として、以下リンクに記載のある「グラス置き換えパズル」を解いてみようと思います。
http://www.nue.ie.niigata-u.ac.jp/toyama/lab-intro/TRS-intro/index.html#glass
グラスの列の置き換えを、以下の等式で表してみます。列は $cons$ を用いたリストで表すこととしてます。
\begin{align} cons(S, cons(W, x))& = cons(W, x)\\ cons(x, cons(S, cons(W, y)))& = cons(x, cons(W, y))\\ cons(W, cons(B, x))& = cons(S, x)\\ cons(x, cons(W, cons(B, y)))& = cons(x, cons(S, y)) \end{align}
上2つは、列 $SW$ が列 $W$ と等しいことを示しています。
1つは、列の先頭に $SW$ が存在した場合、1つは、列の途中又は最後に存在した場合を示しています。 下2つも同様です。
この等式を完備化してみますと、3つの書き換え規則に変換されます。
(NIL)>>> :def-axiom glass input . to finish definition axiom[1]>>> cons(S, cons(W, x)) = cons(W, x) axiom[2]>>> cons(x, cons(S, cons(W, y))) = cons(x, cons(W, y)) axiom[3]>>> cons(W, cons(B, x)) = cons(S, x) axiom[4]>>> cons(x, cons(W, cons(B, y))) = cons(x, cons(S, y)) axiom[5]>>> . Detected that a set of equations has been inputted. Do you want to execute completion algorithm? (yes or no) yes The completion process was successful: cons(S,cons(S,x))=>cons(S,x) cons(S,cons(W,x))=>cons(W,x) cons(W,cons(B,x))=>cons(S,x) (glass)>>>
この状態で、次の2つの等式 $[S,S,W,B] = [W,B,W,B]$ と $[S,S,S,W] \neq [W,B,W,B]$ を証明してみます。
(glass)>>> [S,S,W,B] = [W,B,W,B] The equation can be PROVED under the axiom glass irreducible form under the glass: cons(S,NIL) = cons(S,NIL) (glass)>>> [S,S,S,W] != [W,B,W,B] The equation can be PROVED under the axiom glass irreducible form under the glass: cons(W,NIL) ≠ cons(S,NIL)
想定通り証明することができました。
実装の為に参考にした情報
資料
- Knuth-Bendixの完備化手続きとその応用
- ドレスデン工科大学の講義資料
実装関連
Maxcomp
JAISTの以下のページで公開されているHaskell実装を確認させていただきました。
egison-trs
EgisonおよびHaskellで実装されているコードを確認させていただきました。
ACMEでサーバー証明書を発行するプロセスを理解する
この記事の概要
- Automatic Certificate Management Environment(ACME)で証明書を発行するプロセスを理解する為のメモ書きです。
- ACMEサーバーを実際に立ち上げ手を動かすことで理解を深めたいと思います。
- この記事では、ACMEのアカウントを作成するところまで書きます。
ACMEについて
- ACMEは、RFC 8555で定められた証明書発行に関する仕組みです。
- 様々な認証局で、アドホックに行われていたサーバー証明書発行手順を標準化する目的で作られています。
- Let's EncryptなどのサービスがACMEを利用しています。
RFC 8555 - Automatic Certificate Management Environment (ACME)
進め方
同RFCの22ページ目(7.1 Resources)に、次の通りフローが記載されていましたので、これに沿って進めてみます。
The following table illustrates a typical sequence of requests required to establish a new account with the server, prove control of an identifier, issue a certificate, and fetch an updated certificate some time after issuance. The "->" is a mnemonic for a Location header field pointing to a created resource.
| Action | Request | Response |
|---|---|---|
| Get Directory | GET directory | 200 |
| Get nonce | HEAD newNonce | 200 |
| Create account | POST newAccount | 201 -> account |
| Submit order | POST newOrder | 201 -> order |
| Fetch challenges | POST-as-GET order's authorization urls | 200 |
| Respond to challenges | POST authorization challenge urls | 200 |
| Poll for status | POST-as-GET order | 200 |
| Finalize order | POST order's finalize url | 200 |
| Poll for status | POST-as-GET order | 200 |
| Download certificate | POST-as-GET order's certificate url | 200 |
準備
ACMEに対応した認証局システムを準備します。 Let's Encryptより、ACMEプロトコルのテストベッドとして、Pebbleというシステムが提供されているので、 それを動かしてみます。
$ go get -u github.com/letsencrypt/pebble/... $ cd $GOPATH/src/github.com/letsencrypt/pebble && go install ./...
pebbleを起動しておきます。
$ pebble -config ./test/config/pebble-config.json Pebble 2020/10/10 15:16:39 Starting Pebble ACME server 省略 Pebble 2020/10/10 15:16:39 Listening on: 0.0.0.0:14000 Pebble 2020/10/10 15:16:39 ACME directory available at: https://0.0.0.0:14000/dir
やってみる
Get Directory
まずは、Get Directoryを行うことで、ACMEで行う各種操作のためのURLを取得します。 DirectoryのURL自体は、事前にクライアントに設定しておく必要があります。 Pebbleであれば、起動時にURLが出力されていました。
$ curl --insecure https://localhost:14000/dir
{
"keyChange": "https://0.0.0.0:14000/rollover-account-key",
"newAccount": "https://0.0.0.0:14000/sign-me-up",
"newNonce": "https://0.0.0.0:14000/nonce-plz",
"newOrder": "https://0.0.0.0:14000/order-plz",
"revokeCert": "https://0.0.0.0:14000/revoke-cert",
"meta": {
"externalAccountRequired": false,
"termsOfService": "data:text/plain,Do%20what%20thou%20wilt"
},
}
応答として以下の6つとmetaというフィールドが定義されています。
- newNonce
- newAccount
- newOrder
- newAuthz
- revokeCert
- keyChange
newAuthzについては、ACMEサーバー側がpre-authorizationという仕組みに対応しない場合は、省略されます。
上記応答を見ると、pebbleでは対応していないようです。
更に、metaフィールドの中身については、次のフィールドがあります。
- termsOfService
- website
- caaIdentities
- externalAccountRequired
上記4つのmetaフィールドの使用例は以下の通りです。
"meta": {
"termsOfService": "https://example.com/acme/terms/2017-5-30",
"website": "https://www.example.com/",
"caaIdentities": ["example.com"],
"externalAccountRequired": false
}
Get nonce
ACMEクライアントが、サーバーにPOSTでアクセスする際にはnonceを送信する必要があります。 既存のnonceが有効でなくなってしまったり、初めてサーバーにアクセスする際に、nonceを取得する必要があります。
先ほどのDirectoryのnewNonceのURLに対してHEADメソッドで要求すると、nonceを取得できます。
nonceはReplay-Nonceフィールドに記載されています。
$ curl --insecure --head https://localhost:14000/nonce-plz HTTP/2 200 cache-control: public, max-age=0, no-cache link: <https://localhost:14000/dir>;rel="index" replay-nonce: wUoLKrGFi1i8rmbHn40Wyg date: Sat, 10 Oct 2020 14:20:54 GMT
取得したnonceは、リクエストボディのJWSの中に記載します。 具体的には以下のような形で使用します。
POST /acme/new-account HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{"protected": base64url(
{"alg": "ES256",
"jwk": {...},
"nonce": "6S8IqOGY7eL2lsGoTZYifg",
"url": "https://example.com/acme/new-account" }),
"payload": base64url(
{"termsOfServiceAgreed": true,
"contact": ["mailto:cert-admin@example.org",
"mailto:admin@example.org"]}),
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
}
Create account
ディレクトリのnewAccountにて取得したURLに対して、以下のようにJWSメッセージをPOSTすることでアカウントを作成します。
$ cat createaccount_message | jq .
{
"protected": "eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiWjBMeVpkUWtlNzlURndmdDRYMExJdyIsInVybCI6Imh0dHBzOi8vbG9jYWxob3N0OjE0MDAwL3NpZ24tbWUtdXAiLCJqd2siOnsibiI6IndRcFJQVDZPN1RNSkExWURtazdVLVhsSnZ6TElMSTl3U0ZKSmQ2Z1ZmTzBuOVRUU2VGbHlMYXBVMWZYeU4xU2xJM3dhWm9pTElSSnN3ME9OMUd3NkFLVDhvd1BVZ0lPWTFVLXZRTHF2OTVKOXJVcUlnRGdMQ3dsQmNiVzZJUV9Dckdlb2RuR3VnWFZKYXRoTmt4NmJIS2xOT2ZHUmpMTTJFOWZvbWhReDVIWkRvUTV2R3NtUUVsX01SOUVkd042SzNnVGx5YzBTb1FxU0dwOTJEZ19XODdhelg0eF9hU0pNUDFrLXJSU0V5anc2cVVyVUlKcGdZazdYY3BiS01ZTGR0Si1QdmtlM2x6c0pHdGV3YnRRSWhmZzczZF90WU1Kd3ZfMHlZQ3kySGRaekVkRU43RjZZQjczaFBjQVB1MUdhMjNjVzRZNUFHZFJfSlNRbDFEWTRuUSIsImUiOiJBUUFCIiwia3R5IjoiUlNBIn19",
"signature": "VrYli0NVuAY4YeX8X97fBPHP4a6KQv0fLph4WB6e4J1tu-aCaZtrWpB2vICCjfjQH9rjnjTO5cdNjZAJSn_C-rYH2WIuIcfqH4k73aU6YPJnLHKB8CBTOLUWGtS3bSlOCuA7sKT294lpcc7VOE30yRbit65OxaPW7m45junVtOQ4SdZveP-11Kiw0QPlhFXgcjT4y4n23g4AOw3JBtjPirUYUnAiF3L15wqMWSIr8tUrbj4rNCO37esxvOKQO8gXpI6udSGZ2D9u1jQhSEocFP0tEGG4YMG5b_D_H56YPWDeJxpZx-zqU7wwnV2wYv5R3aiRvV4K6m_ewrdRr3OHAg",
"payload": "eyJ0ZXJtc09mU2VydmljZUFncmVlZCI6dHJ1ZSwiY29udGFjdCI6WyJtYWlsdG86Y2VydC1hZG1pbjFAZXhhbXBsZS5vcmciXX0"
}
$ curl -v --insecure -H'Content-Type: application/jose+json' -X POST --data-binary @createaccount_message https://localhost:14000/sign-me-up
< HTTP/2 201
< cache-control: public, max-age=0, no-cache
< content-type: application/json; charset=utf-8
< link: <https://localhost:14000/dir>;rel="index"
< location: https://localhost:14000/my-account/1
< replay-nonce: 9IQ52Zb5rNG7J0AwAoV-pA
< content-length: 552
< date: Sun, 11 Oct 2020 17:19:26 GMT
<
{
"status": "valid",
"contact": [
"mailto:cert-admin1@example.org"
],
"orders": "https://localhost:14000/list-orderz/1",
"key": {
"kty": "RSA",
"n": "wQpRPT6O7TMJA1YDmk7U-XlJvzLILI9wSFJJd6gVfO0n9TTSeFlyLapU1fXyN1SlI3waZoiLIRJsw0ON1Gw6AKT8owPUgIOY1U-vQLqv95J9rUqIgDgLCwlBcbW6IQ_CrGeodnGugXVJathNkx6bHKlNOfGRjLM2E9fomhQx5HZDoQ5vGsmQEl_MR9EdwN6K3gTlyc0SoQqSGp92Dg_W87azX4x_aSJMP1k-rRSEyjw6qUrUIJpgYk7XcpbKMYLdtJ-Pvke3lzsJGtewbtQIhfg73d_tYMJwv_0yYCy2HdZzEdEN7F6YB73hPcAPu1Ga23cW4Y5AGdR_JSQl1DY4nQ", "e": "AQAB"
}
}
上記JWSのメッセージは、Pythonのauthlibを利用して作成しました。
ちなみにACMEクライアントのJWSメッセージの全てに共通ですが、以下のフィールドは必須となります。
- alg
- nonce
- url
更に、jwkまたは、kidのどちらかが必要です。newAccountの場合は、jwkが必要になります。