From 599bd71e7b49c67a81e950cd3631d378dcefa76a Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 17:02:46 +0900 Subject: [PATCH 1/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 1ef31f5..9791e19 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -63,6 +63,26 @@ function activitypub_load_library($name, $callback) { )); } +function activitypub_create_keypair() { + $keypair = array('', ''); + + $privateKeyResource = openssl_pkey_new(array( + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA + )); + + // Generate the public key for the private key + $privateKeyDetailsArray = openssl_pkey_get_details($privateKeyResource); + + // Export keys to variable + $keypair = array($privateKeyResource, $privateKeyDetailsArray['key']); + + // Free the key from memory. + openssl_free_key($privateKeyResource); + + return $keypair; +} + function activitypub_get_library_data($name) { global $activitypub_loaded_libraries; From 86ab1be93c501211529c0ca20f7d2df57a98c5b5 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 17:28:20 +0900 Subject: [PATCH 2/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 9791e19..6d8dea9 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -242,7 +242,7 @@ function activitypub_add_memo($mb_id, $recv_mb_id, $me_memo) { $sql = " insert into {$g5['memo_table']} ( me_recv_mb_id, me_send_mb_id, me_send_datetime, me_memo, me_read_datetime, me_type, me_send_ip ) values ( '$recv_mb_id', '$mb_id', '".G5_TIME_YMDHIS."', '$me_memo', '0000-00-00 00:00:00' , 'recv', '{$_SERVER['REMOTE_ADDR']}' ) "; sql_query($sql); - return ($me_id == sql_insert_id()); + return ($me_id == sql_insert_id() ? $me_id : 0); } function activitypub_set_liked($good, $bo_table, $wr_id) { From bfc6ea593958075190eff57ec1c08d64b229ebf2 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 17:57:57 +0900 Subject: [PATCH 3/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 6d8dea9..3daaab0 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -26,7 +26,8 @@ define("ACTIVITYPUB_G5_BOARDNAME", "apstreams"); define("ACTIVITYPUB_G5_TABLENAME", $g5['write_prefix'] . ACTIVITYPUB_G5_BOARDNAME); define("ACTIVITYPUB_G5_USERNAME", "apstreams"); define("ACTIVITYPUB_G5_NEW_DAYS", (empty($config['cf_new_del']) ? 30 : $config['cf_new_del'])); -define("ACTIVITYPUB_ACCESS_TOKEN", "server1.example.org=xxuPtHDkMgYQfcy9; server2.example.org=PC6ujkjQXhL6lUtS;"); +define("ACTIVITYPUB_ACCESS_TOKEN", "server1.example.org=YOUR_ACCESS_TOKEN; server2.example.org=YOUR_ACCESS_TOKEN;"); +define("ACTIVITYPUB_CERTIFICATE_DATAFIELD", "mb_9"); // 회원별 인증서(공개키, 개인키)를 저장할 필드 (기본: mb_9) define("OAUTH2_GRANT_DATAFIELD", "mb_10"); // 회원별 인증 정보를 저장할 필드 (기본: mb_10) define("DEFAULT_HTML_ENTITY_FLAGS", ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401); define("NAMESPACE_ACTIVITYSTREAMS", "https://www.w3.org/ns/activitystreams"); From caefc2a88f31270eb369847cfff56473524fff9b Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 17:58:30 +0900 Subject: [PATCH 4/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 1 + 1 file changed, 1 insertion(+) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 3daaab0..2530a84 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -16,6 +16,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 // * https://socialhub.activitypub.rocks/t/posting-to-pleroma-inbox/1184 // * https://github.com/broidHQ/integrations/tree/master/broid-schemas#readme // * https://github.com/autogestion/pubgate-telegram +// * https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/ define("ACTIVITYPUB_INSTANCE_ID", md5_file(G5_DATA_PATH . "/dbconfig.php")); define("ACTIVITYPUB_INSTANCE_VERSION", "0.1.14-dev"); From 45883d6f21d6caca85feba0f95bd8271066f11cd Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 18:01:50 +0900 Subject: [PATCH 5/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 2530a84..11da3ae 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -33,6 +33,7 @@ define("OAUTH2_GRANT_DATAFIELD", "mb_10"); // 회원별 인증 정보를 저 define("DEFAULT_HTML_ENTITY_FLAGS", ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401); define("NAMESPACE_ACTIVITYSTREAMS", "https://www.w3.org/ns/activitystreams"); define("NAMESPACE_ACTIVITYSTREAMS_PUBLIC", "https://www.w3.org/ns/activitystreams#Public"); +define("NAMESPACE_W3ID_SECURITY_V1", "https://w3id.org/security/v1"); define("ACTIVITYPUB_ENABLED_GEOLOCATION", false); // 위치정보 활성화 (https://lite.ip2location.com/) define("NAVERCLOUD_ENABLED_GEOLOCATION", false); // 국내용 위치정보 활성화 (https://www.ncloud.com/product/applicationService/geoLocation) define("NAVERCLOUD_API_ACCESS_KEY", ""); // 네이버 클라우드 API 키 설정 @@ -946,7 +947,7 @@ class _GNUBOARD_ActivityPub { } $context = array( - "@context" => array(NAMESPACE_ACTIVITYSTREAMS, array("@language" => "ko")), + "@context" => array(NAMESPACE_ACTIVITYSTREAMS, NAMESPACE_W3ID_SECURITY_V1, array("@language" => "ko")), "type" => "Person", "id" => activitypub_get_url("user", array("mb_id" => $mb['mb_id'])), "name" => $mb['mb_name'], From 71cac1fcda969f4c2645c83eb6d4ed00b8f06e32 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 18:37:21 +0900 Subject: [PATCH 6/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 44 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 11da3ae..66c54b8 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -4,7 +4,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 // ActivityPub implementation for GNUBOARD 5 // Go Namhyeon // MIT License -// 2022-09-28 (version 0.1.14-dev) +// 2023-02-15 (version 0.1.14-dev) // References: // * https://www.w3.org/TR/activitypub/ @@ -109,7 +109,7 @@ function activitypub_json_decode($arr) { return json_decode($arr, true); } -function activitypub_get_stored_data($s) { +function activitypub_parse_stored_data($s) { $data = array(); $terms = array_filter(array_map("trim", explode(";", $s))); @@ -242,12 +242,17 @@ function activitypub_add_memo($mb_id, $recv_mb_id, $me_memo) { $tmp_row = sql_fetch(" select max(me_id) as max_me_id from {$g5['memo_table']} "); $me_id = $tmp_row['max_me_id'] + 1; - $sql = " insert into {$g5['memo_table']} ( me_recv_mb_id, me_send_mb_id, me_send_datetime, me_memo, me_read_datetime, me_type, me_send_ip ) values ( '$recv_mb_id', '$mb_id', '".G5_TIME_YMDHIS."', '$me_memo', '0000-00-00 00:00:00' , 'recv', '{$_SERVER['REMOTE_ADDR']}' ) "; + $sql = " insert into {$g5['memo_table']} ( me_recv_mb_id, me_send_mb_id, me_send_datetime, me_memo, me_read_datetime, me_type, me_send_ip ) values ( '{$recv_mb_id}', '{$mb_id}', '" . G5_TIME_YMDHIS . "', '{$me_memo}', '0000-00-00 00:00:00' , 'recv', '{$_SERVER['REMOTE_ADDR']}' ) "; sql_query($sql); return ($me_id == sql_insert_id() ? $me_id : 0); } +function activitypub_get_memo($me_id) { + $me = sql_fetch(" select me_memo from {$g5['memo_table']} where me_id = '{$me_id}' ");\ + return $me['me_memo']; +} + function activitypub_set_liked($good, $bo_table, $wr_id) { global $g5; @@ -591,7 +596,7 @@ function activitypub_publish_content($content, $object_id, $mb, $_added_object = // 엑세스 토큰(Access Token)이 존재하는 목적지인 경우 $access_token = ''; - $access_token_data = activitypub_get_stored_data(ACTIVITYPUB_ACCESS_TOKEN); + $access_token_data = activitypub_parse_stored_data(ACTIVITYPUB_ACCESS_TOKEN); foreach($access_token_data as $k=>$v) { if(strpos($_to, "http://" . $k . "/") !== false || strpos($_to, "https://" . $k . "/") !== false) { $access_token = $v; @@ -946,10 +951,34 @@ class _GNUBOARD_ActivityPub { return activitypub_json_encode(array("message" => "Could not find the user")); } + // 인증서 정보가 없으면 생성 + if (!$mb[ACTIVITYPUB_CERTIFICATE_DATAFIELD]) { + $keypair = activitypub_create_keypair(); // 인증서(공개키, 개인키) 생성 + $private_key_id = activitypub_add_memo(ACTIVITYPUB_G5_USERNAME, $mb['mb_id'], $keypair[0]); // 개인키(Private Key) + $public_key_id = activitypub_add_memo(ACTIVITYPUB_G5_USERNAME, $mb['mb_id'], $keypair[1]); // 공개키(Public Key) + + // 회원 정보에 등록 + if ($private_key_id > 0 && $public_key_id > 0) { + $stored_certificate_data = activitypub_build_stored_data(array( + "PrivateKeyId" => $private_key_id, + "PublicKeyId" => $public_key_id + )); + $sql = "update set {$g5['member_table']} " . ACTIVITYPUB_CERTIFICATE_DATAFIELD . " = '{$stored_certificate_data}' where mb_id = '{$mb['mb_id']}'"; + sql_query($sql); + } + } + + // 인증서 정보 불러오기 + $certificate_data = activitypub_parse_stored_data($mb[ACTIVITYPUB_CERTIFICATE_DATAFIELD]); + $private_key = activitypub_get_memo($certificate_data['PrivateKeyId']); // 개인키(Private Key) + $public_key = activitypub_get_memo($certificate_data['PublicKeyId']); // 공개키(Public Key) + + // 본문 생성 + $activitypub_user_id = activitypub_get_url("user", array("mb_id" => $mb['mb_id'])); $context = array( "@context" => array(NAMESPACE_ACTIVITYSTREAMS, NAMESPACE_W3ID_SECURITY_V1, array("@language" => "ko")), "type" => "Person", - "id" => activitypub_get_url("user", array("mb_id" => $mb['mb_id'])), + "id" => $activitypub_user_id, "name" => $mb['mb_name'], "preferredUsername" => $mb['mb_nick'], "summary" => $mb['mb_profile'], @@ -963,6 +992,11 @@ class _GNUBOARD_ActivityPub { ), "endpoints" => array( "sharedInbox" => activitypub_get_url("inbox") + ), + "publicKey" => array( + "id" => $activitypub_user_id . "#main-key", + "owner" => $activitypub_user_id, + "publicKeyPem" => $public_key ) ); From 001b585bb0489a21a7b069a1dfa79c0141b65769 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Wed, 15 Feb 2023 18:42:21 +0900 Subject: [PATCH 7/8] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8fc3b46..b08c918 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,8 @@ ActivityPub implementation for GNUBOARD 5 - [x] 환율 (koreaexim.go.kr) ## 향후 지원 예정 -- [ ] `publicKey` 필드 지원 - [ ] OAuth 2.0 -- [ ] w3id.org 표준 지원 +- [ ] w3id.org (`publicKey` 필드) 지원 - [ ] 메시지 큐(Redis, RebbitMQ, Kafka 등) 지원 ## 전문 예시 From 58fb822d6f310ea2d22e9d329b2c245d548278e6 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Thu, 16 Feb 2023 10:54:59 +0900 Subject: [PATCH 8/8] Update activitypub.extend.php --- extend/activitypub.extend.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/extend/activitypub.extend.php b/extend/activitypub.extend.php index 66c54b8..2bf5c8b 100644 --- a/extend/activitypub.extend.php +++ b/extend/activitypub.extend.php @@ -4,7 +4,7 @@ if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가 // ActivityPub implementation for GNUBOARD 5 // Go Namhyeon // MIT License -// 2023-02-15 (version 0.1.14-dev) +// 2023-02-16 (version 0.1.14-dev) // References: // * https://www.w3.org/TR/activitypub/ @@ -73,12 +73,16 @@ function activitypub_create_keypair() { 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA )); + + // Export the private key + openssl_pkey_export($privateKeyResource, $privateKey); // Generate the public key for the private key $privateKeyDetailsArray = openssl_pkey_get_details($privateKeyResource); + $publicKey = $privateKeyDetailsArray['key']; // Export keys to variable - $keypair = array($privateKeyResource, $privateKeyDetailsArray['key']); + $keypair = array($privateKey, $publicKey); // Free the key from memory. openssl_free_key($privateKeyResource); @@ -116,7 +120,7 @@ function activitypub_parse_stored_data($s) { foreach($terms as $term) { list($k, $v) = explode('=', $term); $k = html_entity_decode($k, DEFAULT_HTML_ENTITY_FLAGS, 'UTF-8'); - $v = html_entity_decode($k, DEFAULT_HTML_ENTITY_FLAGS, 'UTF-8'); + $v = html_entity_decode($v, DEFAULT_HTML_ENTITY_FLAGS, 'UTF-8'); $data[$k] = $v; } @@ -249,7 +253,9 @@ function activitypub_add_memo($mb_id, $recv_mb_id, $me_memo) { } function activitypub_get_memo($me_id) { - $me = sql_fetch(" select me_memo from {$g5['memo_table']} where me_id = '{$me_id}' ");\ + global $g5; + + $me = sql_fetch(" select me_memo from {$g5['memo_table']} where me_id = '{$me_id}' "); return $me['me_memo']; } @@ -945,6 +951,8 @@ class _GNUBOARD_ActivityPub { } public static function user() { + global $g5; + $mb = get_member($_GET['mb_id']); if (!$mb['mb_id']) { @@ -963,11 +971,11 @@ class _GNUBOARD_ActivityPub { "PrivateKeyId" => $private_key_id, "PublicKeyId" => $public_key_id )); - $sql = "update set {$g5['member_table']} " . ACTIVITYPUB_CERTIFICATE_DATAFIELD . " = '{$stored_certificate_data}' where mb_id = '{$mb['mb_id']}'"; + $sql = " update {$g5['member_table']} set " . ACTIVITYPUB_CERTIFICATE_DATAFIELD . " = '{$stored_certificate_data}' where mb_id = '{$mb['mb_id']}' "; sql_query($sql); } } - + // 인증서 정보 불러오기 $certificate_data = activitypub_parse_stored_data($mb[ACTIVITYPUB_CERTIFICATE_DATAFIELD]); $private_key = activitypub_get_memo($certificate_data['PrivateKeyId']); // 개인키(Private Key)