6 Commits
Author SHA1 Message Date
net909 31e0686273 version 2024-04-03 21:33:12 +08:00
net909 9e3f934701 1.8.0 2024-04-03 21:29:42 +08:00
net909 9fd3a7d3b7 version 2023-11-24 23:07:34 +08:00
net909 53b1421aba fix 2023-11-24 22:59:29 +08:00
net909 e9b08fdbf2 1.7 2023-11-24 22:45:30 +08:00
net909 3df2bc15e2 fix 2023-10-09 21:51:39 +08:00
59 changed files with 2696 additions and 1192 deletions
+3 -3
View File
@@ -75,7 +75,7 @@ class Index extends Base
$stars = request()->user['stars']; $stars = request()->user['stars'];
if(strlen($stars)>0){ if(strlen($stars)>0){
$stars = explode(',',$stars); $stars = explode(',',$stars);
$tool = Db::name('plugin')->cache('plugins', self::CACHE_TIME)->field('id,title,alias,keyword,request_count,category_id')->where('enable', 1)->order('weight','desc')->select(); $tool = Db::name('plugin')->cache('plugins', self::CACHE_TIME)->field('id,title,alias,keyword,request_count,category_id,level')->where('enable', 1)->order('weight','desc')->select();
$list = []; $list = [];
foreach($category as $item){ foreach($category as $item){
foreach($tool as $row){ foreach($tool as $row){
@@ -110,15 +110,15 @@ class Index extends Base
$history = cookie('tools'); $history = cookie('tools');
if(strlen($history)>0){ if(strlen($history)>0){
$history = array_reverse(array_unique(explode(',',$history))); $history = array_reverse(array_unique(explode(',',$history)));
$tool = Db::name('plugin')->cache('plugins', self::CACHE_TIME)->field('id,title,alias,keyword,request_count,category_id')->where('enable', 1)->order('weight','desc')->select(); $tool = Db::name('plugin')->cache('plugins', self::CACHE_TIME)->field('id,title,alias,keyword,request_count,category_id,level')->where('enable', 1)->order('weight','desc')->select();
$newtool = []; $newtool = [];
foreach($tool as $row){ foreach($tool as $row){
$newtool[$row['id']] = $row; $newtool[$row['id']] = $row;
} }
$list = []; $list = [];
foreach($history as $id){ foreach($history as $id){
if(!isset($newtool[$id])) continue;
$row = $newtool[$id]; $row = $newtool[$id];
if(!$row) continue;
if(!plugin_userlevel($row['level'])) continue; if(!plugin_userlevel($row['level'])) continue;
$row['url'] = get_plugin_url($row['alias']); $row['url'] = get_plugin_url($row['alias']);
$row['out'] = substr($row['alias'],0,1) == '/' || substr($row['alias'],0,7) == 'http://' || substr($row['alias'],0,8) == 'https://'; $row['out'] = substr($row['alias'],0,1) == '/' || substr($row['alias'],0,7) == 'http://' || substr($row['alias'],0,8) == 'https://';
+1 -1
View File
@@ -18,7 +18,7 @@ class ViewOutput
{ {
View::assign('islogin', $request->islogin); View::assign('islogin', $request->islogin);
View::assign('user', $request->user); View::assign('user', $request->user);
View::assign('cdn_cdnjs', config_get('cdn_cdnjs', '//cdn.staticfile.org/')); View::assign('cdn_cdnjs', config_get('cdn_cdnjs', '//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/'));
View::assign('cdn_npm', config_get('cdn_npm', 'https://unpkg.com/')); View::assign('cdn_npm', config_get('cdn_npm', 'https://unpkg.com/'));
View::config(['view_path' => template_path_get()]); View::config(['view_path' => template_path_get()]);
return $next($request)->header([ return $next($request)->header([
Generated
+60 -57
View File
@@ -8,20 +8,20 @@
"packages": [ "packages": [
{ {
"name": "cccyun/think-captcha", "name": "cccyun/think-captcha",
"version": "3.0.7", "version": "3.0.9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/netcccyun/think-captcha.git", "url": "https://github.com/netcccyun/think-captcha.git",
"reference": "ba2997f1c3fbcf2aae3dd5a80c8285a957121523" "reference": "dcbc3f2cc29749c962cc8ad5d12ae59a30d73b24"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/netcccyun/think-captcha/zipball/ba2997f1c3fbcf2aae3dd5a80c8285a957121523", "url": "https://api.github.com/repos/netcccyun/think-captcha/zipball/dcbc3f2cc29749c962cc8ad5d12ae59a30d73b24",
"reference": "ba2997f1c3fbcf2aae3dd5a80c8285a957121523", "reference": "dcbc3f2cc29749c962cc8ad5d12ae59a30d73b24",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"topthink/framework": "^6.0.0" "topthink/framework": "^6.0|^8.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@@ -54,9 +54,9 @@
], ],
"description": "captcha package for thinkphp", "description": "captcha package for thinkphp",
"support": { "support": {
"source": "https://github.com/netcccyun/think-captcha/tree/3.0.7" "source": "https://github.com/netcccyun/think-captcha/tree/3.0.9"
}, },
"time": "2022-10-21T03:33:53+00:00" "time": "2023-06-02T03:38:07+00:00"
}, },
{ {
"name": "khanamiryan/qrcode-detector-decoder", "name": "khanamiryan/qrcode-detector-decoder",
@@ -163,25 +163,25 @@
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
"version": "1.0.1", "version": "1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/http-message.git", "url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.0" "php": "^7.2 || ^8.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.0.x-dev" "dev-master": "1.1.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -210,9 +210,9 @@
"response" "response"
], ],
"support": { "support": {
"source": "https://github.com/php-fig/http-message/tree/master" "source": "https://github.com/php-fig/http-message/tree/1.1"
}, },
"time": "2016-08-06T14:39:51+00:00" "time": "2023-04-04T09:50:52+00:00"
}, },
{ {
"name": "psr/log", "name": "psr/log",
@@ -317,16 +317,16 @@
}, },
{ {
"name": "topthink/framework", "name": "topthink/framework",
"version": "v6.1.2", "version": "v6.1.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/top-think/framework.git", "url": "https://github.com/top-think/framework.git",
"reference": "67235be5b919aaaf1de5aed9839f65d8e766aca3" "reference": "66eb9cf4d627df12911344cd328faf9bb596bf2c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/67235be5b919aaaf1de5aed9839f65d8e766aca3", "url": "https://api.github.com/repos/top-think/framework/zipball/66eb9cf4d627df12911344cd328faf9bb596bf2c",
"reference": "67235be5b919aaaf1de5aed9839f65d8e766aca3", "reference": "66eb9cf4d627df12911344cd328faf9bb596bf2c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -376,9 +376,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/top-think/framework/issues", "issues": "https://github.com/top-think/framework/issues",
"source": "https://github.com/top-think/framework/tree/v6.1.2" "source": "https://github.com/top-think/framework/tree/v6.1.4"
}, },
"time": "2023-02-08T02:24:01+00:00" "time": "2023-07-11T15:16:03+00:00"
}, },
{ {
"name": "topthink/think-helper", "name": "topthink/think-helper",
@@ -428,24 +428,27 @@
}, },
{ {
"name": "topthink/think-migration", "name": "topthink/think-migration",
"version": "v3.0.5", "version": "v3.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/top-think/think-migration.git", "url": "https://github.com/top-think/think-migration.git",
"reference": "7a5ec3952ea97bf1a5d8872e1bd10f2abf6294ca" "reference": "22c44058e1454f3af1d346e7f6524fbe654de7fb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/top-think/think-migration/zipball/7a5ec3952ea97bf1a5d8872e1bd10f2abf6294ca", "url": "https://api.github.com/repos/top-think/think-migration/zipball/22c44058e1454f3af1d346e7f6524fbe654de7fb",
"reference": "7a5ec3952ea97bf1a5d8872e1bd10f2abf6294ca", "reference": "22c44058e1454f3af1d346e7f6524fbe654de7fb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"topthink/framework": "^6.0", "php": ">=7.2",
"topthink/framework": "^6.0 || ^8.0",
"topthink/think-helper": "^3.0.3" "topthink/think-helper": "^3.0.3"
}, },
"require-dev": { "require-dev": {
"fzaninotto/faker": "^1.8" "composer/composer": "^2.5.8",
"fzaninotto/faker": "^1.8",
"robmorgan/phinx": "^0.13.4"
}, },
"suggest": { "suggest": {
"fzaninotto/faker": "Required to use the factory builder (^1.8)." "fzaninotto/faker": "Required to use the factory builder (^1.8)."
@@ -460,7 +463,7 @@
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Phinx\\": "phinx/src/Phinx", "Phinx\\": "phinx",
"think\\migration\\": "src" "think\\migration\\": "src"
} }
}, },
@@ -476,22 +479,22 @@
], ],
"support": { "support": {
"issues": "https://github.com/top-think/think-migration/issues", "issues": "https://github.com/top-think/think-migration/issues",
"source": "https://github.com/top-think/think-migration/tree/v3.0.5" "source": "https://github.com/top-think/think-migration/tree/v3.1.1"
}, },
"time": "2023-02-26T13:16:22+00:00" "time": "2023-09-14T05:51:31+00:00"
}, },
{ {
"name": "topthink/think-orm", "name": "topthink/think-orm",
"version": "v2.0.60", "version": "v2.0.61",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/top-think/think-orm.git", "url": "https://github.com/top-think/think-orm.git",
"reference": "8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46" "reference": "10528ebf4a5106b19c3bac9c6deae7a67ff49de6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46", "url": "https://api.github.com/repos/top-think/think-orm/zipball/10528ebf4a5106b19c3bac9c6deae7a67ff49de6",
"reference": "8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46", "reference": "10528ebf4a5106b19c3bac9c6deae7a67ff49de6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -531,9 +534,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/top-think/think-orm/issues", "issues": "https://github.com/top-think/think-orm/issues",
"source": "https://github.com/top-think/think-orm/tree/v2.0.60" "source": "https://github.com/top-think/think-orm/tree/v2.0.61"
}, },
"time": "2023-03-19T04:51:56+00:00" "time": "2023-04-20T14:27:51+00:00"
}, },
{ {
"name": "topthink/think-template", "name": "topthink/think-template",
@@ -621,16 +624,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" "reference": "42292d99c55abe617799667f454222c54c60e229"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -645,7 +648,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -684,7 +687,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -700,20 +703,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-07-28T09:04:16+00:00"
}, },
{ {
"name": "symfony/polyfill-php72", "name": "symfony/polyfill-php72",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php72.git", "url": "https://github.com/symfony/polyfill-php72.git",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97" "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97", "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -722,7 +725,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -760,7 +763,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -776,20 +779,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -798,7 +801,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -843,7 +846,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -859,7 +862,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/var-dumper", "name": "symfony/var-dumper",
+2 -2
View File
@@ -32,6 +32,6 @@ return [
// 定义404错误的模板文件地址 // 定义404错误的模板文件地址
404 => \think\facade\App::getRootPath() . 'public/404.html', 404 => \think\facade\App::getRootPath() . 'public/404.html',
], ],
'version' => '1.6', 'version' => '1.8',
'ver' => 1060 'ver' => 1080
]; ];
+6 -2
View File
@@ -48,7 +48,7 @@ INSERT INTO `toolbox_config` (`key`, `value`) VALUES
('captcha_id', ''), ('captcha_id', ''),
('captcha_key', ''), ('captcha_key', ''),
('ip_type', '0'), ('ip_type', '0'),
('cdn_cdnjs', '//cdn.staticfile.org/'), ('cdn_cdnjs', '//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/'),
('cdn_npm', 'https://unpkg.com/'), ('cdn_npm', 'https://unpkg.com/'),
('description', '这是一个非常Nice的在线工具箱'), ('description', '这是一个非常Nice的在线工具箱'),
('foot_code', ''), ('foot_code', ''),
@@ -179,7 +179,11 @@ INSERT INTO `toolbox_plugin` (`id`, `title`, `alias`, `class`, `keyword`, `weigh
(92, '银行卡归属地查询', 'bankcard', 'utility\\bankcard', 'yinhangkaguishudichaxun,yhkgsdcx', 89, 1, 0, 0, 0, 3, '', '2022-10-14 16:55:08', '2022-10-21 17:35:56'), (92, '银行卡归属地查询', 'bankcard', 'utility\\bankcard', 'yinhangkaguishudichaxun,yhkgsdcx', 89, 1, 0, 0, 0, 3, '', '2022-10-14 16:55:08', '2022-10-21 17:35:56'),
(93, '微博图片反查', 'wbimg', 'utility\\wbimg', 'weibotupianfancha,wbtpfc', 67, 1, 0, 0, 0, 3, '', '2022-10-21 17:34:20', '2022-11-08 22:06:30'), (93, '微博图片反查', 'wbimg', 'utility\\wbimg', 'weibotupianfancha,wbtpfc', 67, 1, 0, 0, 0, 3, '', '2022-10-21 17:34:20', '2022-11-08 22:06:30'),
(94, 'QQ等级查询', 'qqlevel', 'wqq\\qqlevel', 'qqdengjichaxun,qqdjcx', 0, 0, 0, 0, 0, 5, '', '2023-04-23 18:06:05', '2023-04-23 18:06:05'), (94, 'QQ等级查询', 'qqlevel', 'wqq\\qqlevel', 'qqdengjichaxun,qqdjcx', 0, 0, 0, 0, 0, 5, '', '2023-04-23 18:06:05', '2023-04-23 18:06:05'),
(95, '短视频去水印解析', 'videoparse', 'utility\\videoparse', 'duanshipinqushuiyinjiexi,dspqsyjx', 95, 1, 0, 0, 0, 3, '', '2023-09-22 23:25:17', '2023-09-28 15:29:46'); (95, '短视频去水印解析', 'videoparse', 'utility\\videoparse', 'duanshipinqushuiyinjiexi,dspqsyjx', 95, 1, 0, 0, 0, 3, '', '2023-09-22 23:25:17', '2023-09-28 15:29:46'),
(96, '国密SM2工具', 'guomi', 'dev\\guomi', 'guomism2,gmsm2', 0, 1, 0, 0, 19, 2, '', '2023-10-26 17:50:38', '2023-10-26 21:35:10'),
(97, '证书信息查看', 'cert', 'dev\\cert', 'zhengshuxinxichakan,zsxxck', 0, 1, 0, 0, 18, 2, '', '2023-10-27 16:05:08', '2023-10-27 16:05:08'),
(98, '证书格式转换', 'cert_convert', 'dev\\cert_convert', 'rsazhengshugeshizhuanhuan,rsazsgszh', 0, 1, 0, 0, 8, 2, '', '2023-10-24 21:25:15', '2023-10-27 16:03:56'),
(99, 'CSR生成与查看', 'csr', 'dev\\csr', 'csrshengchengchakan,csrscck', 0, 1, 0, 0, 8, 2, '', '2023-11-09 20:18:19', '2023-11-10 17:14:02');
DROP TABLE IF EXISTS `toolbox_querycache`; DROP TABLE IF EXISTS `toolbox_querycache`;
+106
View File
@@ -0,0 +1,106 @@
<?php
/**
* 证书信息查看
*/
namespace plugin\dev\cert;
use app\Plugin;
use Exception;
error_reporting(0);
class App extends Plugin
{
private static $key_type = ['RSA', 'DSA', 'DH', 'EC'];
public function index()
{
return $this->view();
}
public function query(){
if(input('post.type/d') == 2){
$domain = input('post.domain', null, 'trim');
if(!$domain) return msg('error','no domain');
$port = 443;
if(strpos($domain, ':')){
$a = explode(':', $domain);
$domain = $a[0];
if(is_numeric($a[1])) $port = intval($a[1]);
}
if(!checkdomain($domain)) return msg('error','域名格式不正确');
try{
$cert = $this->getSSLCertificate($domain, $port);
}catch(Exception $e){
return msg('error', $e->getMessage());
}
}else{
$cert = input('post.cert');
if(!$cert) return msg('error','no cert');
}
$arr = openssl_x509_parse($cert);
if(!$arr){
return msg('error', '证书文件错误');
}
$pkey_detail = openssl_pkey_get_details(openssl_pkey_get_public($cert));
$subject_info = [
'通用名称(CN)' => $this->format($arr['subject']['CN']),
'国家(C)' => $this->format($arr['subject']['C']),
'省份(S)' => $this->format($arr['subject']['ST']),
'城市(L)' => $this->format($arr['subject']['L']),
'组织(O)' => $this->format($arr['subject']['O']),
'部门(OU)' => $this->format($arr['subject']['OU']),
];
$issuer_info = [
'通用名称(CN)' => $this->format($arr['issuer']['CN']),
'国家(C)' => $this->format($arr['issuer']['C']),
'组织(O)' => $this->format($arr['issuer']['O']),
];
$cert_info = [
'序列号' => $arr['serialNumberHex'],
'签名算法' => $arr['signatureTypeSN'],
'密钥类型' => isset(self::$key_type[$pkey_detail['type']]) ? self::$key_type[$pkey_detail['type']] : '未知',
'密钥强度' => $pkey_detail['bits'],
'颁发时间' => date('Y-m-d H:i:s', $arr['validFrom_time_t']),
'过期时间' => date('Y-m-d H:i:s', $arr['validTo_time_t']),
'有效期' => round(($arr['validTo_time_t']-time()) / 3600 / 24).'天',
'备用名' => $arr['extensions']['subjectAltName'],
];
return msg('ok','success',['subject_info'=>$subject_info, 'issuer_info'=>$issuer_info, 'cert_info'=>$cert_info]);
}
private function format($mixed){
if(is_array($mixed)){
return $mixed[array_key_first($mixed)];
}
return $mixed;
}
private function getSSLCertificate($domain, $port = 443) {
$context = stream_context_create([
"ssl" => [
"capture_peer_cert" => true,
],
]);
$socket = stream_socket_client('ssl://'.$domain.':'.$port, $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context);
if ($socket) {
$params = stream_context_get_params($socket);
if(!isset($params["options"]["ssl"]["peer_certificate"])){
throw new Exception('域名未找到SSL证书信息');
}
fclose($socket);
return $params["options"]["ssl"]["peer_certificate"];
} else {
$errstr = mb_convert_encoding($errstr, 'UTF-8', 'GB2312');
throw new Exception('域名443端口连接失败,'.$errstr);
}
}
}
+180
View File
@@ -0,0 +1,180 @@
{extend name="common/plugin_layout" /}
{block name="title"}{$plugin.title} - {:config_get('title')}{/block}
{block name="main"}
<style>
.query-title{text-align: right; }
@media (min-width: 576px) { .query-title{width: 150px;} }
</style>
<div class="container-xl" id="app">
<div class="col-sm-12 col-md-10 col-xl-8 center-block">
<div class="card card-preview">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">证书信息查看</span>
</div>
<div class="form-group mt-3">
<div class="form-control-wrap">
<div class="custom-control custom-radio mr-3">
<input type="radio" class="custom-control-input" v-model="inputtype" id="customRadio1" :value="0">
<label class="custom-control-label" for="customRadio1">粘贴文件内容</label>
</div>
<div class="custom-control custom-radio mr-3">
<input type="radio" class="custom-control-input" v-model="inputtype" id="customRadio2" :value="1">
<label class="custom-control-label" for="customRadio2">上传文件</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" v-model="inputtype" id="customRadio3" :value="2">
<label class="custom-control-label" for="customRadio3">输入域名</label>
</div>
</div>
</div>
<div class="form-group" v-show="inputtype==0">
<label class="form-label">证书PEM文件:</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="cert" placeholder="-----BEGIN CERTIFICATE-----"></textarea>
</div>
</div>
<div class="form-group" v-show="inputtype==1">
<label class="form-label">证书PEM文件:</label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile1" accept=".pem,.crt,.cer" required @change="handleFile">
<label class="custom-file-label" for="customFile1">点击选择文件(.pem .crt .cer</label>
</div>
</div>
</div>
<div class="form-group" v-show="inputtype==2">
<label class="form-label">查询域名的SSL证书信息:</label>
<div class="form-control-wrap">
<input type="text" v-model="domain" placeholder="请输入支持https访问的域名,支持加端口" class="form-control form-control-lg" @keyup.enter="query" ref="input" autocomplete="off">
</div>
</div>
<button class="btn btn-dim btn-outline-primary btn-block card-link mb-3" @click="query">
查看证书信息
</button>
</div>
</div>
<div class="card card-preview" v-show="showresult" style="display:none">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">查询结果</span>
</div>
<h6>主题信息</h6>
<div class="table-responsive mb-2">
<table class="table table-hover table-bordered">
<tbody>
<tr v-for="(item,index) in subject_info"><td class="query-title">{{index}}</td><td>{{item}}</td></tr>
</tbody>
</table>
</div>
<h6>签发者信息</h6>
<div class="table-responsive mb-2">
<table class="table table-hover table-bordered">
<tbody>
<tr v-for="(item,index) in issuer_info"><td class="query-title">{{index}}</td><td>{{item}}</td></tr>
</tbody>
</table>
</div>
<h6>证书信息</h6>
<div class="table-responsive">
<table class="table table-hover table-bordered">
<tbody>
<tr v-for="(item,index) in cert_info"><td class="query-title">{{index}}</td><td>{{item}}</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
inputtype: 0,
cert: '',
domain: '',
showresult: false,
subject_info: [],
issuer_info: [],
cert_info: [],
},
methods: {
handleFile(event) {
var file = event.target.files[0];
layer.load(0, {shade:0.1});
var that = this;
that.cert = '';
var reader = new FileReader();
reader.onload = () => {
layer.closeAll();
var cert = reader.result;
if(cert.indexOf('BEGIN CERTIFICATE') == -1){
layer.alert('证书文件错误', {icon: 5});return;
}
that.cert = cert;
};
reader.onerror = () => {
layer.closeAll();
layer.alert('证书文件读取失败', {icon: 5});return;
};
reader.readAsText(file);
},
query() {
if(this.inputtype == 2){
if(this.domain == ''){
layer.alert('域名不能为空');return;
}
this.checkURL();
}else if(this.inputtype < 2 && this.cert == ''){
layer.alert('证书内容不能为空');return;
}
layer.load(0, {shade:0.1});
var that=this;
$.ajax({
url: '/api/{$plugin.alias}/query',
type: 'post',
dataType: 'json',
data: {type: that.inputtype, cert: that.cert, domain: that.domain},
cache: false,
success: function (data) {
layer.closeAll();
if(data.status=='ok'){
that.subject_info = data.data.subject_info;
that.issuer_info = data.data.issuer_info;
that.cert_info = data.data.cert_info;
that.showresult = true;
}else{
layer.alert(data.message, {icon: 5});
}
},
error: function () {
layer.closeAll();
layer.msg('服务器错误', {icon: 5});
}
});
},
checkURL()
{
var url = this.domain.trim();
if (url.indexOf(" ")>=0){
url = url.replace(/ /g,"");
}
if (url.toLowerCase().indexOf("http://")==0){
url = url.slice(7);
}
if (url.toLowerCase().indexOf("https://")==0){
url = url.slice(8);
}
if (url.slice(url.length-1)=="/"){
url = url.slice(0,url.length-1);
}
this.domain = url;
},
},
})
</script>
{/block}
+210
View File
@@ -0,0 +1,210 @@
<?php
/**
* RSA证书格式转换
*/
namespace plugin\dev\cert_convert;
use app\Plugin;
use CURLFile;
use Exception;
class App extends Plugin
{
private static $apiurl = 'http://keytool.odata.cc/api.php';
public function index()
{
return $this->view();
}
public function convert()
{
$from = input('post.from');
$to = input('post.to');
if(empty($from) || empty($to)) return msg('error', '必填项不能为空');
try{
$params = [];
if($from == 'PEM' && $to != 'DER' && $to != 'PKCS7'){
if(!isset($_FILES['private'])) return msg('error', 'PEM私钥文件不存在');
$private_path = $_FILES['private']['tmp_name'];
$this->check_cert('PEM私钥', $private_path);
if(!isset($_FILES['cert'])) return msg('error', 'PEM证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('PEM证书', $cert_path);
if(isset($_FILES['cacert'])){
$cacert_path = $_FILES['cacert']['tmp_name'];
$this->check_cert('PEM证书', $cacert_path);
file_put_contents($cert_path, PHP_EOL.file_get_contents($cacert_path), FILE_APPEND);
}
$params['private'] = new CURLFile($private_path);
$params['cert'] = new CURLFile($cert_path);
$params['pass'] = input('post.pass', null, 'trim');
$this->check_pass('证书密码', $params['pass'], true);
$act = $to == 'JKS' ? 'pem_to_jks' : 'pem_to_pfx';
}elseif($from == 'PEM' && $to == 'DER'){
if(!isset($_FILES['cert'])) return msg('error', 'PEM证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('PEM证书', $cert_path);
$params['cert'] = new CURLFile($cert_path);
$act = 'pem_to_der';
}elseif($from == 'DER' && $to == 'PEM'){
if(!isset($_FILES['cert'])) return msg('error', 'DER证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('DER证书', $cert_path);
$params['cert'] = new CURLFile($cert_path);
$act = 'der_to_pem';
}elseif($from == 'PKCS12'){
if(!isset($_FILES['cert'])) return msg('error', 'PKCS12证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('PKCS12证书', $cert_path);
$params['cert'] = new CURLFile($cert_path);
if($to == 'JKS'){
$params['srcpass'] = input('post.srcpass', null, 'trim');
$params['destpass'] = input('post.destpass', null, 'trim');
$this->check_pass('原证书密码', $params['srcpass'], true);
$this->check_pass('新证书密码', $params['destpass']);
$act = 'pfx_to_jks';
}elseif($to == 'PEM'){
$params['pass'] = input('post.pass', null, 'trim');
$this->check_pass('原证书密码', $params['pass'], true);
$act = 'pfx_to_pem';
}
}elseif($from == 'JKS'){
if(!isset($_FILES['cert'])) return msg('error', 'JKS证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('JKS证书', $cert_path);
$params['cert'] = new CURLFile($cert_path);
if($to == 'PKCS12'){
$params['srcpass'] = input('post.srcpass', null, 'trim');
$params['destpass'] = input('post.destpass', null, 'trim');
$this->check_pass('原证书密码', $params['srcpass']);
$this->check_pass('新证书密码', $params['destpass'], true);
if(empty($params['destpass'])) $params['destpass'] = $params['srcpass'];
$act = 'jks_to_pfx';
}elseif($to == 'PEM'){
$params['pass'] = input('post.pass', null, 'trim');
$this->check_pass('原证书密码', $params['pass']);
$act = 'jks_to_pem';
}
}elseif($from == 'PEM' && $to == 'PKCS7'){
if(!isset($_FILES['cert'])) return msg('error', 'PEM证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('PEM证书', $cert_path);
if(isset($_FILES['cacert'])){
$cacert_path = $_FILES['cacert']['tmp_name'];
$this->check_cert('PEM证书', $cacert_path);
file_put_contents($cert_path, PHP_EOL.file_get_contents($cacert_path), FILE_APPEND);
}
$params['cert'] = new CURLFile($cert_path);
$act = 'pem_to_p7b';
}elseif($from == 'PKCS7' && $to == 'PEM'){
if(!isset($_FILES['cert'])) return msg('error', 'PKCS7证书文件不存在');
$cert_path = $_FILES['cert']['tmp_name'];
$this->check_cert('PKCS7证书', $cert_path);
$params['cert'] = new CURLFile($cert_path);
$act = 'p7b_to_pem';
}else{
return msg('error', '转换类型不存在');
}
$response = $this->requestApi($act, $params);
$files = [];
if($to == 'PEM' && $from != 'DER' && $from != 'PKCS7' && substr($response, 0, 1) == '{'){
$arr = json_decode($response, true);
$files[] = ['name'=>'PEM 服务器证书', 'file'=>'cert.pem', 'data'=>base64_encode($arr['cert'])];
if(!empty($arr['extra_certs'])){
$cacert = implode(PHP_EOL, $arr['extra_certs']);
$files[] = ['name'=>'PEM 中间/根证书', 'file'=>'cacert.pem', 'data'=>base64_encode($cacert)];
}
$files[] = ['name'=>'PEM 私钥', 'file'=>'privatekey.pem', 'data'=>base64_encode($arr['private_key'])];
$files[] = ['name'=>'PEM 公钥', 'file'=>'publickey.pem', 'data'=>base64_encode($arr['public_key'])];
}elseif($to == 'PEM'){
$files[] = ['name'=>'PEM 证书文件', 'file'=>'cert.pem', 'data'=>base64_encode($response)];
}elseif($to == 'DER'){
$files[] = ['name'=>'DER 证书文件', 'file'=>'cert.der', 'data'=>base64_encode($response)];
}elseif($to == 'PKCS12'){
$files[] = ['name'=>'PKCS12 证书文件', 'file'=>'cert.pfx', 'data'=>base64_encode($response)];
}elseif($to == 'JKS'){
$files[] = ['name'=>'JKS 证书文件', 'file'=>'cert.jks', 'data'=>base64_encode($response)];
}elseif($to == 'PKCS7'){
$files[] = ['name'=>'PKCS7 证书文件', 'file'=>'cert.p7b', 'data'=>base64_encode($response)];
}
return msg('ok', 'success', ['files'=>$files]);
}catch(Exception $e){
return msg('error', $e->getMessage());
}
}
private function check_cert($name, $path){
$file_size = filesize($path);
if($file_size < 100 || $file_size > 50 * 1024){
throw new Exception($name.'文件大小超过限制');
}
}
private function check_pass($name, $value, $empty = false){
if($empty && empty($value)) return;
if(empty($value)){
throw new Exception($name.'不能为空');
}
if(strlen($value) < 6){
throw new Exception($name.'至少6个字符');
}
if(strlen($value) > 32){
throw new Exception($name.'最多32个字符');
}
}
private function requestApi($act, $params){
$requrl = self::$apiurl . '?act=' . $act;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $requrl);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$httpheader[] = "Accept: */*";
$httpheader[] = "Accept-Encoding: gzip,deflate,sdch";
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
$httpheader[] = "Connection: close";
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
if (curl_errno($ch) > 0) {
curl_close($ch);
throw new Exception('接口请求失败');
}
$httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpStatusCode != 200) {
curl_close($ch);
$arr = json_decode($response, true);
throw new Exception($arr ? $arr['msg'] : '接口返回数据解析失败');
}
curl_close($ch);
return $response;
}
}
+327
View File
@@ -0,0 +1,327 @@
{extend name="common/plugin_layout" /}
{block name="title"}{$plugin.title} - {:config_get('title')}{/block}
{block name="main"}
<div class="container-xl" id="app">
<div class="col-sm-12 col-md-10 col-xl-8 center-block">
<div class="card card-preview">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">RSA证书格式转换</span>
</div>
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">从格式 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<select class="form-control" v-model="input.from">
<option value="">请选择</option>
<option v-for="value in convert_from" :value="value">{{value}}</option>
</select>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">转换至 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<select class="form-control" v-model="input.to">
<option value="">请选择</option>
<option v-for="value in convert_to" :value="value">{{value}}</option>
</select>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='PEM'&&input.to!='DER'&&input.to!='PKCS7'">
<label class="form-label" for="customFileLabel">PEM 私钥文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile1" accept=".pem,.key" required @change="handleFile('pem_private', event)">
<label class="custom-file-label" for="customFile1">点击选择文件</label>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='PEM'&&input.to!='DER'">
<label class="form-label" for="customFileLabel">PEM 服务器证书文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile2" accept=".pem,.crt,.cer" required @change="handleFile('pem_mycert', event)">
<label class="custom-file-label" for="customFile2">点击选择文件</label>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='PEM'&&input.to!='DER'">
<label class="form-label" for="customFileLabel">PEM 中间/根证书文件</label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile3" accept=".pem,.crt,.cer" @change="handleFile('pem_cacert', event)">
<label class="custom-file-label" for="customFile3">点击选择文件(可选)</label>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='PEM'&&input.to=='PKCS12'">
<label class="form-label">PKCS12 证书密码</label>
<div class="form-control-wrap">
<input type="text" v-model="input.pkcs12_pass" placeholder="密码可为空" class="form-control">
</div>
</div>
<div class="form-group" v-show="input.from=='PEM'&&input.to=='JKS'">
<label class="form-label">JKS 证书密码 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<input type="text" v-model="input.jks_pass" placeholder="" class="form-control" required>
</div>
</div>
<div class="form-group" v-show="input.from=='PKCS12'">
<label class="form-label" for="customFileLabel">PKCS12 证书文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile4" accept=".pfx,.p12" required @change="handleFile('pkcs12_cert', event)">
<label class="custom-file-label" for="customFile4">点击选择文件(支持pfx、p12</label>
</div>
</div>
</div>
<div class="d-flex flex-wrap" v-if="input.from=='PKCS12'">
<div class="form-group flex-fill mr-2">
<label class="form-label">原PKCS12证书密码</label>
<div class="form-control-wrap">
<input type="text" v-model="input.pkcs12_pass" placeholder="没有密码请留空" class="form-control">
</div>
</div>
<div class="form-group flex-fill" v-show="input.to=='JKS'">
<label class="form-label">新JKS证书密码 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<input type="text" v-model="input.jks_pass" placeholder="填写JKS证书密码" class="form-control" required>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='JKS'">
<label class="form-label" for="customFileLabel">JKS 证书文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile5" accept=".jks,.ks,.keystore" required @change="handleFile('jks_cert', event)">
<label class="custom-file-label" for="customFile5">点击选择文件(支持jks、ks、keystore</label>
</div>
</div>
</div>
<div class="d-flex flex-wrap" v-if="input.from=='JKS'">
<div class="form-group flex-fill">
<label class="form-label">原JKS证书密码 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<input type="text" v-model="input.jks_pass" placeholder="填写JKS证书密码" class="form-control" required>
</div>
</div>
<div class="form-group flex-fill ml-2" v-show="input.to=='PKCS12'">
<label class="form-label">新PKCS12证书密码</label>
<div class="form-control-wrap">
<input type="text" v-model="input.pkcs12_pass" placeholder="留空则与原密码相同" class="form-control">
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='PEM'&&input.to=='DER'">
<label class="form-label" for="customFileLabel">PEM 证书文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile6" accept=".pem,.crt,.cer" required @change="handleFile('pem_cert', event)">
<label class="custom-file-label" for="customFile6">点击选择文件</label>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='DER'">
<label class="form-label" for="customFileLabel">DER 证书文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile7" accept=".der,.crt,.cer" required @change="handleFile('der_cert', event)">
<label class="custom-file-label" for="customFile7">点击选择文件</label>
</div>
</div>
</div>
<div class="form-group" v-show="input.from=='PKCS7'">
<label class="form-label" for="customFileLabel">PKCS7 证书文件 <span class="text-danger">*</span></label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile8" accept=".p7b,.p7c,.spc" required @change="handleFile('pkcs7_cert', event)">
<label class="custom-file-label" for="customFile8">点击选择文件(支持p7b、p7c、spc)</label>
</div>
</div>
</div>
<button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="submit">
开始转换
</button>
</div>
</div>
<div class="card card-preview" v-show="show_result" style="display:none">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">转换结果</span>
</div>
<ul class="list-group">
<li class="list-group-item" v-for="(item,index) in result_file"><b>{{item.name}}</b><a href="javascript:" @click="download(index)"><em class="icon ni ni-download"></em>点击下载</a></li>
</ul>
</div>
</div>
<div class="card card-preview">
<div class="card-inner">
<h6><em class="icon ni ni-info"></em> 工具说明</h6>
<div class="accordion-inner">
<p>本工具支持PEM、DER、PKCS12、PKCS7、JKS等格式的证书转换</p>
<p><b>证书格式介绍</b></p>
<p><b>PEM</b>以base64的形式存放证书、私钥,常见后缀是.pem .crt .cer .key</p>
<p><b>DER</b>以二进制的形式存放证书、私钥,常见后缀是.der .crt .cer</p>
<p><b>PKCS#12</b>以二进制的形式存放证书+私钥,通常包含保护密码,常见后缀是.pfx .p12</p>
<p><b>PKCS#7</b>以树状展示证书链,也支持单个证书,不含私钥,常见后缀是.p7b .p7c .spc</p>
<p><b>JKS</b>是Java平台使用的证书存储库文件,存放证书+私钥,有密码保护,常见后缀是.jks .ks .keystore</p>
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="{$cdn_cdnjs}FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
mode: 0,
convert_from: [
'PEM',
'DER',
'PKCS12',
'PKCS7',
'JKS',
],
convert_to: [
],
input: {
from:'',
to:'',
pem_private: null,
pem_mycert: null,
pem_cacert: null,
pkcs12_pass: '',
jks_pass: '',
pkcs12_cert: null,
jks_cert: null,
pem_cert: null,
der_cert: null,
pkcs7_cert: null,
},
show_result: false,
result_file: [
]
},
watch: {
"input.from"(newVal) {
if(newVal == 'PEM'){
this.convert_to = ['PKCS12','PKCS7','DER','JKS'];
}else if(newVal == 'PKCS12'){
this.convert_to = ['PEM','JKS'];
}else if(newVal == 'JKS'){
this.convert_to = ['PEM','PKCS12'];
}else if(newVal == 'DER'){
this.convert_to = ['PEM'];
}else if(newVal == 'PKCS7'){
this.convert_to = ['PEM'];
}else{
this.convert_to = [];
}
this.input.to = ''
}
},
methods: {
handleFile(name, event) {
this.input[name] = event.target.files[0];
},
submit() {
if(this.input.from == ''){
layer.alert('转换源格式不能为空');return;
}
if(this.input.to == ''){
layer.alert('转换目标格式不能为空');return;
}
var data = new FormData();
data.append('from', this.input.from);
data.append('to', this.input.to);
if(this.input.from == 'PEM' && this.input.to != 'DER'){
if(this.input.to != 'PKCS7'){
if(this.input.pem_private == null){layer.alert('PEM私钥不能为空');return;}
data.append('private', this.input.pem_private);
}
if(this.input.pem_mycert == null){layer.alert('PEM服务器证书不能为空');return;}
data.append('cert', this.input.pem_mycert);
data.append('cacert', this.input.pem_cacert);
if(this.input.to == 'PKCS12'){
data.append('pass', this.input.pkcs12_pass);
}else if(this.input.to == 'JKS'){
if(this.input.jks_pass == ''){layer.alert('JKS证书密码不能为空');return;}
data.append('pass', this.input.jks_pass);
}
}else if(this.input.from == 'PEM' && this.input.to == 'DER'){
if(this.input.pem_cert == null){layer.alert('PEM证书不能为空');return;}
data.append('cert', this.input.pem_cert);
}else if(this.input.from == 'DER' && this.input.to == 'PEM'){
if(this.input.der_cert == null){layer.alert('DER证书不能为空');return;}
data.append('cert', this.input.der_cert);
}else if(this.input.from == 'PKCS12'){
if(this.input.pkcs12_cert == null){layer.alert('PKCS12证书文件不能为空');return;}
data.append('cert', this.input.pkcs12_cert);
if(this.input.to == 'PEM'){
data.append('pass', this.input.pkcs12_pass);
}else if(this.input.to == 'JKS'){
if(this.input.jks_pass == ''){layer.alert('新JKS证书密码不能为空');return;}
data.append('srcpass', this.input.pkcs12_pass);
data.append('destpass', this.input.jks_pass);
}
}else if(this.input.from == 'JKS'){
if(this.input.jks_cert == null){layer.alert('JKS证书文件不能为空');return;}
data.append('cert', this.input.jks_cert);
if(this.input.to == 'PEM'){
data.append('pass', this.input.jks_pass);
}else if(this.input.to == 'PKCS12'){
if(this.input.jks_pass == ''){layer.alert('原JKS证书密码不能为空');return;}
data.append('srcpass', this.input.jks_pass);
data.append('destpass', this.input.pkcs12_pass);
}
}else if(this.input.from == 'PKCS7' && this.input.to == 'PEM'){
if(this.input.pkcs7_cert == null){layer.alert('PKCS7证书不能为空');return;}
data.append('cert', this.input.pkcs7_cert);
}
this.show_result = false;
var ii = layer.load();
var that = this;
$.ajax({
type : "POST",
url : "/api/{$plugin.alias}/convert",
data : data,
processData: false,
contentType: false,
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.status == 'ok'){
that.result_file = data.data.files;
that.show_result = true;
}else{
layer.alert(data.message, {icon: 7});
}
},
error : function(){
layer.close(ii);
layer.msg('服务器错误', {icon: 5});
}
});
},
download(index) {
var file_info = this.result_file[index]
if(!file_info) return;
var byteCharacters = window.atob(file_info.data)
var byteNumbers = new Array(byteCharacters.length)
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i)
}
var byteArray = new Uint8Array(byteNumbers)
var blob = new Blob([byteArray], {type: "application/force-download"});
saveAs(blob, file_info.file);
}
},
})
</script>
{/block}
+107
View File
@@ -0,0 +1,107 @@
<?php
/**
* CSR生成与查看
*/
namespace plugin\dev\csr;
use app\Plugin;
use Exception;
error_reporting(0);
class App extends Plugin
{
private static $key_type = ['RSA', 'DSA', 'DH', 'EC'];
public function index()
{
return $this->view();
}
public function query(){
$csr = input('post.csr');
if(!$csr) return msg('error','no csr');
$subject_info = openssl_csr_get_subject($csr);
if(!$subject_info){
return msg('error', 'CSR文件错误');
}
$pkey_detail = openssl_pkey_get_details(openssl_csr_get_public_key($csr));
$info = [
'通用名称(CN)' => $this->format($subject_info['CN']),
'国家(C)' => $this->format($subject_info['C']),
'省份(S)' => $this->format($subject_info['ST']),
'城市(L)' => $this->format($subject_info['L']),
'组织(O)' => $this->format($subject_info['O']),
'部门(OU)' => $this->format($subject_info['OU']),
'密钥类型' => isset(self::$key_type[$pkey_detail['type']]) ? self::$key_type[$pkey_detail['type']] : '未知',
'密钥强度' => $pkey_detail['bits'],
];
return msg('ok','success',['info'=>$info]);
}
private function format($mixed){
if(is_array($mixed)){
return $mixed[array_key_first($mixed)];
}
return $mixed;
}
public function generate(){
$commonName = input('post.commonName', null, 'trim');//通用名
$countryName = input('post.countryName', null, 'trim');//国家
$stateOrProvinceName = input('post.stateOrProvinceName', null, 'trim');//省份
$localityName = input('post.localityName', null, 'trim');//城市
$organizationName = input('post.organizationName', null, 'trim');//组织
$organizationalUnitName = input('post.organizationalUnitName', null, 'trim');//部门
$subjectAltName = input('post.subjectAltName', null, 'trim');//备用名
$emailAddress = input('post.emailAddress', null, 'trim');//邮箱
$private_key_type = input('post.private_key_type/d');//密钥算法
$private_key_bits = input('post.private_key_bits/d');//密钥长度
$curve_name = input('post.curve_name');//曲线名称
$digest_alg = input('post.digest_alg');//签名算法
$pass_phrase = input('post.pass_phrase', null, 'trim');//私钥密码
//生成公私钥
$config = [
"private_key_bits" => $private_key_bits,
"private_key_type" => $private_key_type,
"curve_name" => $curve_name
];
$privkey = openssl_pkey_new($config);
if(!$privkey){
return msg('error', '私钥生成失败'.openssl_error_string());
}
$privateKey = '';
openssl_pkey_export($privkey, $privateKey, $pass_phrase?$pass_phrase:null, $config);
$pubKey = openssl_pkey_get_details($privkey);
$publicKey = $pubKey['key'];
//生成CSR
$dn = array(
"countryName" => $countryName,
"stateOrProvinceName" => $stateOrProvinceName,
"localityName" => $localityName,
"organizationName" => $organizationName,
"organizationalUnitName" => $organizationalUnitName,
"commonName" => $commonName,
"emailAddress" => $emailAddress,
"subjectAltName" => $subjectAltName
);
$dn = array_filter($dn);
$csr = openssl_csr_new($dn, $privkey, array('digest_alg' => $digest_alg));
if(!$csr){
return msg('error', 'CSR生成失败'.openssl_error_string());
}
openssl_csr_export($csr, $csrout);
return msg('ok', 'success', ['public_key'=>$publicKey, 'private_key'=>$privateKey, 'csr'=>$csrout]);
}
}
+382
View File
@@ -0,0 +1,382 @@
{extend name="common/plugin_layout" /}
{block name="title"}{$plugin.title} - {:config_get('title')}{/block}
{block name="main"}
<style>
.query-title{text-align: right; }
@media (min-width: 576px) { .query-title{width: 150px;} }
</style>
<div class="container-xl" id="app">
<div class="col-sm-12 col-md-10 col-xl-8 center-block">
<div class="card card-preview">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">CSR生成与查看</span>
</div>
<ul class="nav nav-tabs">
<li class="nav-item"><a class="nav-link" @click="show=0" :class="{'active':show===0}" href="javascript:">CSR查看</a></li>
<li class="nav-item"><a class="nav-link" @click="show=1" :class="{'active':show===1}" href="javascript:">CSR生成</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="tabItem1" :class="{'active':show===0}">
<div class="form-group mt-3">
<div class="form-control-wrap">
<div class="custom-control custom-radio mr-3">
<input type="radio" class="custom-control-input" v-model="inputtype" id="customRadio1" :value="0">
<label class="custom-control-label" for="customRadio1">粘贴文件内容</label>
</div>
<div class="custom-control custom-radio mr-3">
<input type="radio" class="custom-control-input" v-model="inputtype" id="customRadio2" :value="1">
<label class="custom-control-label" for="customRadio2">上传文件</label>
</div>
</div>
</div>
<div class="form-group" v-show="inputtype==0">
<label class="form-label">CSR请求文件:</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="csr" placeholder="-----BEGIN CERTIFICATE REQUEST-----"></textarea>
</div>
</div>
<div class="form-group" v-show="inputtype==1">
<label class="form-label">CSR请求文件:</label>
<div class="form-control-wrap">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile1" accept=".csr" required @change="handleFile">
<label class="custom-file-label" for="customFile1">点击选择文件</label>
</div>
</div>
</div>
<button class="btn btn-dim btn-outline-primary btn-block card-link mb-3" @click="query">
查看CSR信息
</button>
</div>
<div class="tab-pane" id="tabItem2" :class="{'active':show===1}">
<h6 class="text-info">主题信息(以下均为选填)</h6>
<div class="row">
<div class="form-group col-md-4 col-6">
<label class="form-label">通用名(CN)</label>
<div class="form-control-wrap">
<input type="text" v-model="input.commonName" placeholder="站点的域名" class="form-control">
</div>
</div>
<div class="form-group col-md-4 col-6">
<label class="form-label">组织(O)</label>
<div class="form-control-wrap">
<input type="text" v-model="input.organizationName" placeholder="机构名称,例如:Tencent" class="form-control">
</div>
</div>
<div class="form-group col-md-4 col-6">
<label class="form-label">部门(OU)</label>
<div class="form-control-wrap">
<input type="text" v-model="input.organizationalUnitName" placeholder="部门名称" class="form-control">
</div>
</div>
<div class="form-group col-md-4 col-6">
<label class="form-label">国家(C)</label>
<div class="form-control-wrap">
<input type="text" v-model="input.countryName" placeholder="国家简称,例如:CN" class="form-control">
</div>
</div>
<div class="form-group col-md-4 col-6">
<label class="form-label">省份(S)</label>
<div class="form-control-wrap">
<input type="text" v-model="input.stateOrProvinceName" placeholder="省级地区,例如:Guangzhou" class="form-control">
</div>
</div>
<div class="form-group col-md-4 col-6">
<label class="form-label">城市(L)</label>
<div class="form-control-wrap">
<input type="text" v-model="input.localityName" placeholder="市级地区,例如:Shenzhen" class="form-control">
</div>
</div>
<div class="form-group col-md-8 col-6">
<label class="form-label">备用名</label>
<div class="form-control-wrap">
<input type="text" v-model="input.subjectAltName" placeholder="备用域名,多个用,分隔" class="form-control">
</div>
</div>
<div class="form-group col-md-4 col-6">
<label class="form-label">邮箱</label>
<div class="form-control-wrap">
<input type="text" v-model="input.emailAddress" placeholder="邮箱" class="form-control">
</div>
</div>
</div>
<h6 class="text-info">密钥信息</h6>
<div class="row">
<div class="form-group col-6">
<label class="form-label">密钥算法</label>
<select class="form-control" v-model="input.private_key_type">
<option v-for="v,index in private_key_type" :value="index">{{v}}</option>
</select>
</div>
<div class="form-group col-6" v-if="input.private_key_type!=3">
<label class="form-label">密钥长度</label>
<select class="form-control" v-model="input.private_key_bits">
<option v-for="v in private_key_bits" :value="v">{{v}} bit</option>
</select>
</div>
<div class="form-group col-6" v-if="input.private_key_type==3">
<label class="form-label">曲线名称</label>
<select class="form-control" v-model="input.curve_name">
<option v-for="v in curve_name" :value="v">{{v}}</option>
</select>
</div>
<div class="form-group col-6">
<label class="form-label">签名算法</label>
<select class="form-control" v-model="input.digest_alg">
<option v-for="v in digest_alg" :value="v">{{v}}</option>
</select>
</div>
<div class="form-group col-6">
<label class="form-label">私钥密码</label>
<div class="form-control-wrap">
<input type="text" v-model="input.pass_phrase" placeholder="可不设置" class="form-control">
</div>
</div>
</div>
<button class="btn btn-dim btn-outline-primary btn-block card-link mb-3" @click="generate">
生成CSR文件
</button>
</div>
</div>
</div>
</div>
<div class="card card-preview" v-show="show==0&&showresult" style="display:none">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">查询结果</span>
</div>
<div class="table-responsive mb-2">
<table class="table table-hover table-bordered">
<tbody>
<tr v-for="(item,index) in info"><td class="query-title">{{index}}</td><td>{{item}}</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card card-preview" v-show="show==1&&showresult2" style="display:none">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">生成结果</span>
</div>
<div class="table-responsive mb-2">
<div class="form-group">
<label class="form-label">CSRPEM格式) <em class="icon ni ni-download" title="下载保存" v-show="result.csr" @click="download('csr')"></em></label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="result.csr"></textarea>
</div>
</div>
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">公钥 <em class="icon ni ni-download" title="下载保存" v-show="result.public_key" @click="download('public_key')"></em></label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="result.public_key"></textarea>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">私钥 <em class="icon ni ni-download" title="下载保存" v-show="result.private_key" @click="download('private_key')"></em></label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="result.private_key"></textarea>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">公私钥格式</label>
<select class="form-control" v-model="result.key_type">
<option value="pem">PEM格式</option>
<option value="base64">Base64无换行</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="{$cdn_cdnjs}FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script>
function pemToBase64(pem) {
var lines = pem.split('\n');
var encoded = '';
for (var i = 0; i < lines.length; i++) {
if (lines[i].trim().length > 0 && lines[i].indexOf('-----BEGIN') === -1 && lines[i].indexOf('-----END') === -1) {
encoded += lines[i].trim();
}
}
return encoded;
}
function base64ToPem(base64Key, type) {
var pemHeader = '-----BEGIN ' + type + '-----\n';
var pemFooter = '\n-----END ' + type + '-----\n';
var chunkSize = 64;
var base64KeyWithBreaks = base64Key.match(new RegExp('.{1,' + chunkSize + '}', 'g')).join('\n');
var pemKey = pemHeader + base64KeyWithBreaks + pemFooter;
return pemKey;
}
new Vue({
el: '#app',
data: {
show: 0,
inputtype: 0,
csr: '',
showresult: false,
showresult2: false,
info: [],
private_key_type: [
'RSA',
'DSA',
'DH',
'ECC'
],
private_key_bits: [512,1024,2048,4096],
curve_name: [
'prime256v1',
'secp256k1',
'secp384r1',
'secp521r1',
],
digest_alg: [
'md5',
'sha1',
'sha224',
'sha256',
'sha384',
'sha512',
],
input: {
commonName: '',
countryName: '',
stateOrProvinceName: '',
localityName: '',
organizationName: '',
organizationalUnitName: '',
subjectAltName: '',
emailAddress: '',
private_key_type: 0,
private_key_bits: 2048,
curve_name: 'prime256v1',
digest_alg: 'sha256',
pass_phrase: '',
},
result:{
csr: '',
key_type: 'pem',
public_key: '',
private_key: '',
}
},
watch: {
"input.private_key_type"(newVal) {
if(newVal == 2){
this.private_key_bits = [512,1024,2048]
}else{
this.private_key_bits = [512,1024,2048,4096]
}
},
"result.key_type"(newVal) {
if(this.result.public_key != ''){
if(newVal == 'pem'){
this.result.public_key = base64ToPem(this.result.public_key, 'PUBLIC KEY')
}else{
this.result.public_key = pemToBase64(this.result.public_key)
}
}
if(this.result.private_key != ''){
if(newVal == 'pem'){
this.result.private_key = base64ToPem(this.result.private_key, this.input.pass_phrase ? 'ENCRYPTED PRIVATE KEY' : 'PRIVATE KEY')
}else{
this.result.private_key = pemToBase64(this.result.private_key)
}
}
},
},
methods: {
handleFile(event) {
var file = event.target.files[0];
layer.load(0, {shade:0.1});
var that = this;
that.csr = '';
var reader = new FileReader();
reader.onload = () => {
layer.closeAll();
var csr = reader.result;
if(csr.indexOf('BEGIN CERTIFICATE REQUEST') == -1){
layer.alert('证书文件错误', {icon: 5});return;
}
that.csr = csr;
};
reader.onerror = () => {
layer.closeAll();
layer.alert('证书文件读取失败', {icon: 5});return;
};
reader.readAsText(file);
},
query() {
if(this.csr == ''){
layer.alert('证书内容不能为空');return;
}
layer.load(0, {shade:0.1});
var that=this;
$.ajax({
url: '/api/{$plugin.alias}/query',
type: 'post',
dataType: 'json',
data: {csr: that.csr},
cache: false,
success: function (data) {
layer.closeAll();
if(data.status=='ok'){
that.info = data.data.info;
that.showresult = true;
}else{
layer.alert(data.message, {icon: 5});
}
},
error: function () {
layer.closeAll();
layer.msg('服务器错误', {icon: 5});
}
});
},
generate() {
layer.load(0, {shade:0.1});
var that=this;
$.ajax({
url: '/api/{$plugin.alias}/generate',
type: 'post',
dataType: 'json',
data: {...that.input},
cache: false,
success: function (data) {
layer.closeAll();
if(data.status=='ok'){
that.result.csr = data.data.csr;
that.result.public_key = data.data.public_key;
that.result.private_key = data.data.private_key;
that.showresult2 = true;
}else{
layer.alert(data.message, {icon: 5});
}
},
error: function () {
layer.closeAll();
layer.msg('服务器错误', {icon: 5});
}
});
},
download(name) {
var content = this.result[name]
if(!content) return;
var ext = this.result.key_type == 'base64' ? '.txt' : '.pem';
if(name == 'csr') ext = '.csr';
var filename = name + ext
var blob = new Blob([content], {type: "application/force-download"});
saveAs(blob, filename);
}
},
})
</script>
{/block}
+105 -51
View File
@@ -11,45 +11,45 @@
<div class="form-group"> <div class="form-group">
<label class="form-label" for="input">字符串</label> <label class="form-label" for="input">字符串</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<textarea class="form-control" id="input" rows="8" placeholder="需要加密/解密的字符串"></textarea> <textarea class="form-control" id="input" rows="8" placeholder="需要加密/解密的字符串" v-model="set.input"></textarea>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">加密方式</label> <label class="form-label">加密方式</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio1" value="AES" checked><label class="custom-control-label" for="customRadio1">AES</label></div> <div class="custom-control custom-radio m-1" v-for="(value,index) in encrypt_types"><input type="radio" class="custom-control-input" name="encrypt_type" :id="'customRadio'+index" :value="value" v-model="encrypt_type"><label class="custom-control-label" :for="'customRadio'+index">{{value}}</label></div>
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio2" value="DES"><label class="custom-control-label" for="customRadio2">DES</label></div>
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio3" value="RC4"><label class="custom-control-label" for="customRadio3">RC4</label></div>
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio4" value="Rabbit"><label class="custom-control-label" for="customRadio4">Rabbit</label></div>
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio5" value="TripleDES"><label class="custom-control-label" for="customRadio5">TripleDES</label></div>
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio6" value="RC4Drop"><label class="custom-control-label" for="customRadio6">RC4Drop</label></div>
<div class="custom-control custom-radio m-1"><input type="radio" class="custom-control-input" name="encrypt_type" id="customRadio7" value="RabbitLegacy"><label class="custom-control-label" for="customRadio7">RabbitLegacy</label></div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">密钥</label> <label class="form-label">密钥</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<input type="text" id="passphrase" placeholder="输入加密/解密的密钥" class="form-control"> <input type="text" v-model="set.passphrase" placeholder="输入加密/解密的密钥" class="form-control">
</div>
</div>
<div class="form-group" v-show="show_iv">
<label class="form-label">IV</label>
<div class="form-control-wrap">
<input type="text" v-model="set.iv" placeholder="输入IV值" class="form-control">
</div> </div>
</div> </div>
<div class="row pt-1 pb-1"> <div class="row pt-1 pb-1">
<div class="col-3"> <div class="col-3">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" onclick="encrypt()"> <button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="encrypt">
<em class="icon ni ni-lock"></em>加密 <em class="icon ni ni-lock"></em>加密
</button> </button>
</div> </div>
<div class="col-3"> <div class="col-3">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" onclick="decrypt()"> <button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="decrypt">
<em class="icon ni ni-unlock"></em>解密 <em class="icon ni ni-unlock"></em>解密
</button> </button>
</div> </div>
<div class="col-3"> <div class="col-3">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" onclick="exchange()"> <button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="exchange">
<em class="icon ni ni-exchange-v"></em>交换 <em class="icon ni ni-exchange-v"></em>交换
</button> </button>
</div> </div>
<div class="col-3"> <div class="col-3">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" onclick="reset()"> <button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="reset">
<em class="icon ni ni-trash-empty"></em>清空 <em class="icon ni ni-trash-empty"></em>清空
</button> </button>
</div> </div>
@@ -57,7 +57,7 @@
<div class="form-group mt-3"> <div class="form-group mt-3">
<label class="form-label" for="output">结果</label> <label class="form-label" for="output">结果</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<textarea class="form-control" id="output" rows="8" placeholder="加密/解密的结果"></textarea> <textarea class="form-control" v-model="set.output" rows="8" placeholder="加密/解密的结果"></textarea>
</div> </div>
</div> </div>
</div> </div>
@@ -66,44 +66,98 @@
</div> </div>
{/block} {/block}
{block name="script"} {block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="{$cdn_cdnjs}crypto-js/4.1.1/crypto-js.min.js"></script> <script src="{$cdn_cdnjs}crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="/assets/sm/sm4.js"></script>
<script> <script>
function encrypt() { new Vue({
var input = $("#input").val(); el: '#app',
var passphrase = $("#passphrase").val(); data: {
var encrypt_type = $("input[name='encrypt_type']").val(); encrypt_type: 'AES',
if(input == '' || passphrase == '') return; encrypt_types: ['AES', 'DES', 'RC4', 'Rabbit', 'TripleDES', 'RC4Drop', 'RabbitLegacy', 'SM4-ECB', 'SM4-CBC'],
try { set: {
var output = CryptoJS[encrypt_type].encrypt(input, passphrase).toString(); input: '',
$("#output").val(output); output: '',
} catch (e) { passphrase: '',
layer.msg(e.message, {icon:2}); iv: '0123456789abcdef',
$("#output").val(''); },
} show_iv: false
} },
function decrypt() { watch: {
var input = $("#input").val(); encrypt_type(newVal) {
var passphrase = $("#passphrase").val(); if(newVal == 'SM4-CBC'){
var encrypt_type = $("input[name='encrypt_type']").val(); this.show_iv = true;
if(input == '' || passphrase == '') return; }else{
try { this.show_iv = false;
input = CryptoJS[encrypt_type].decrypt(input, passphrase).toString(CryptoJS.enc.Utf8); }
$("#output").val(input); },
} catch (e) { },
layer.msg(e.message, {icon:2}); methods: {
$("#output").val(''); encrypt() {
} if(this.set.input == '' || this.set.passphrase == '') return;
} try {
function exchange() { if(this.encrypt_type == 'SM4-ECB'){
var input = $("#input").val(); var key = this.stringToHex(this.set.passphrase);
var output = $("#output").val(); if(key.length != 32){layer.msg('SM4加密密钥必须是16个字符', {icon:0, time:1500});return;}
$("#input").val(output); this.set.output = sm4.encrypt(this.set.input, key);
$("#output").val(input); return;
} }else if(this.encrypt_type == 'SM4-CBC'){
function reset() { if(this.set.iv == '') return;
$("#input").val(''); var key = this.stringToHex(this.set.passphrase);
$("#output").val(''); var iv = this.stringToHex(this.set.iv);
$("#passphrase").val(''); if(key.length != 32){layer.msg('SM4加密密钥必须填是16个字符', {icon:0, time:1500});return;}
} if(iv.length != 32){layer.msg('SM4加密IV必须填是16个字符', {icon:0, time:1500});return;}
this.set.output = sm4.encrypt(this.set.input, key, {mode: 'cbc', iv: iv});
return;
}
this.set.output = CryptoJS[`${this.encrypt_type}`].encrypt(this.set.input, this.set.passphrase).toString();
} catch (e) {
layer.msg(e.message, {icon:2});
this.set.output = ''
}
},
decrypt() {
if(this.set.input == '' || this.set.passphrase == '') return;
try {
if(this.encrypt_type == 'SM4-ECB'){
var key = this.stringToHex(this.set.passphrase);
if(key.length != 32){layer.msg('SM4解密密钥必须是16个字符', {icon:0, time:1500});return;}
this.set.output = sm4.decrypt(this.set.input, key);
return;
}else if(this.encrypt_type == 'SM4-CBC'){
if(this.set.iv == '') return;
var key = this.set.passphrase;
var iv = this.set.iv;
if(key.length != 32){layer.msg('SM4解密密钥必须填是16个字符', {icon:0, time:1500});return;}
if(iv.length != 32){layer.msg('SM4解密IV必须填是16个字符', {icon:0, time:1500});return;}
this.set.output = sm4.decrypt(this.set.input, key, {mode: 'cbc', iv: iv});
return;
}
this.set.input = CryptoJS[`${this.encrypt_type}`].decrypt(this.set.output, this.set.passphrase).toString(CryptoJS.enc.Utf8);
} catch (e) {
layer.msg(e.message, {icon:2});
this.set.output = ''
}
},
reset() {
this.set.input = ''
this.set.output = ''
this.set.passphrase = ''
},
exchange() {
var input = this.set.input;
var output = this.set.output;
this.set.input = output;
this.set.output = input;
},
stringToHex(str){
    var val="";
    for(var i = 0; i < str.length; i++){
      val += str.charCodeAt(i).toString(16);
    }
    return val;
  }
},
})
</script> </script>
{/block} {/block}
+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* 国密公私钥工具
*/
namespace plugin\dev\guomi;
use app\Plugin;
class App extends Plugin
{
public function index()
{
return $this->view();
}
public function output()
{
$type = input('post.type/d');
if($type == 1){
$cert = input('post.cert');
$pkey_res = openssl_pkey_get_public($cert);
if(!$pkey_res){
return msg('error', '导出失败,公钥证书不正确');
}
$pkey_detail = openssl_pkey_get_details($pkey_res);
$pkey_hex = bin2hex($this->pem2der($pkey_detail['key']));
if(strlen($pkey_hex) > 130) $pkey_hex = substr($pkey_hex, -130);
return msg('ok', '导出成功', $pkey_hex);
}elseif($type == 2){
$csr = input('post.csr');
$pkey_res = openssl_csr_get_public_key($csr);
if(!$pkey_res){
return msg('error', '导出失败,CSR不正确');
}
$pkey_detail = openssl_pkey_get_details($pkey_res);
$pkey_hex = bin2hex($this->pem2der($pkey_detail['key']));
if(strlen($pkey_hex) > 130) $pkey_hex = substr($pkey_hex, -130);
return msg('ok', '导出成功', $pkey_hex);
}
}
private function pem2der($pem_data)
{
$begin = "-----";
$end = "-----END";
$pem_data = substr($pem_data, strpos($pem_data, $begin, 6) + strlen($begin));
$pem_data = substr($pem_data, 0, strpos($pem_data, $end));
$der = base64_decode($pem_data);
return $der;
}
}
+478
View File
@@ -0,0 +1,478 @@
{extend name="common/plugin_layout" /}
{block name="title"}{$plugin.title} - {:config_get('title')}{/block}
{block name="main"}
<div class="container-xl" id="app">
<div class="col-sm-12 col-md-10 col-xl-8 center-block">
<div class="card card-preview">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">国密SM2公私钥生成、签名、加解密</span>
</div>
<ul class="nav nav-tabs">
<li class="nav-item"><a class="nav-link" @click="show=0" :class="{'active':show===0}" href="javascript:">公私钥生成</a></li>
<li class="nav-item"><a class="nav-link" @click="show=1" :class="{'active':show===1}" href="javascript:">加密解密</a></li>
<li class="nav-item"><a class="nav-link" @click="show=2" :class="{'active':show===2}" href="javascript:">签名验签</a></li>
<li class="nav-item"><a class="nav-link" @click="show=3" :class="{'active':show===3}" href="javascript:">公私钥对验证</a></li>
<li class="nav-item"><a class="nav-link" @click="show=4" :class="{'active':show===4}" href="javascript:">公钥导出</a></li>
<li class="nav-item"><a class="nav-link" @click="show=5" :class="{'active':show===5}" href="javascript:">公钥压缩</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="tabItem1" :class="{'active':show===0}">
<button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="generate">
生成
</button>
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">SM2公钥 <em class="icon ni ni-download" title="下载保存" v-show="mutipart_from.public_key" @click="download('public_key')"></em></label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.public_key"></textarea>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">SM2私钥 <em class="icon ni ni-download" title="下载保存" v-show="mutipart_from.private_key" @click="download('private_key')"></em></label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.private_key"></textarea>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="hex">Hex(十六进制)</option>
<option value="base64">Base64</option>
</select>
</div>
</div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===1}">
<div class="form-group">
<label class="form-label">密文数据顺序</label>
<select class="form-control" v-model="crypto_from.type">
<option :value="1">新标准(C1C3C2</option>
<option :value="0">老标准(C1C2C3</option>
</select>
</div>
<div class="form-group">
<label class="form-label">密文数据类型</label>
<select class="form-control" v-model="crypto_from.data_type">
<option value="hex">Hex(十六进制)</option>
<option value="base64">Base64</option>
</select>
</div>
<div class="row pt-1 pb-1 mb-3">
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="encrypt">
加密
</button>
</div>
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="decrypt">
解密
</button>
</div>
</div>
<div class="form-group">
<label class="form-label">原文,未加密的内容</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="crypto_from.origin"></textarea>
</div>
</div>
<div class="form-group">
<label class="form-label">密文,加密后的内容</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="crypto_from.coded"></textarea>
</div>
</div>
</div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===2}">
<div class="form-group">
<label class="form-label">是否SM3计算Hash</label>
<select class="form-control" v-model="sign_form.hash">
<option :value="1"></option>
<option :value="0"></option>
</select>
</div>
<div class="form-group">
<label class="form-label">签名结果类型</label>
<select class="form-control" v-model="sign_form.type">
<option :value="1">ASN1(R,S)</option>
<option :value="0">R+S</option>
</select>
</div>
<div class="form-group">
<label class="form-label">签名数据类型</label>
<select class="form-control" v-model="sign_form.data_type">
<option value="hex">Hex(十六进制)</option>
<option value="base64">Base64</option>
</select>
</div>
<div class="form-group">
<label class="form-label">UserId</label>
<div class="form-control-wrap">
<input type="text" v-model="sign_form.userid" class="form-control">
</div>
</div>
<div class="row pt-1 pb-1 mb-3">
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="sign">
签名
</button>
</div>
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="verify">
验签
</button>
</div>
</div>
<div class="form-group">
<label class="form-label">待签名,验签原始内容</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="sign_form.data"></textarea>
</div>
</div>
<div class="form-group">
<label class="form-label">签名,已签名内容</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="3" v-model="sign_form.sign"></textarea>
</div>
</div>
</div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===3}">
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">SM2公钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.public_key"></textarea>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">SM2私钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.private_key"></textarea>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="hex">Hex(十六进制)</option>
<option value="base64">Base64</option>
</select>
</div>
<button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="check">
验证
</button>
</div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===4}">
<div class="form-group">
<label class="form-label">导出方式</label>
<select class="form-control" v-model="convert_from.type">
<option :value="0">从私钥导出公钥</option>
<option :value="1">从公钥证书导出公钥</option>
<option :value="2">从CSR导出公钥</option>
</select>
</div>
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">SM2公钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.public_key" placeholder="导出结果"></textarea>
</div>
</div>
<div class="form-group flex-fill" v-show="convert_from.type==0">
<label class="form-label">SM2私钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.private_key"></textarea>
</div>
</div>
<div class="form-group flex-fill" v-show="convert_from.type==1">
<label class="form-label">SM2公钥证书</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="convert_from.cert" placeholder="-----BEGIN CERTIFICATE-----"></textarea>
</div>
</div>
<div class="form-group flex-fill" v-show="convert_from.type==2">
<label class="form-label">CSR</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="convert_from.csr" placeholder="-----BEGIN CERTIFICATE REQUEST-----"></textarea>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="hex">Hex(十六进制)</option>
<option value="base64">Base64</option>
</select>
</div>
<button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="output">
导出
</button>
</div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===5}">
<div class="form-group">
<label class="form-label">SM2公钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="6" v-model="mutipart_from.public_key"></textarea>
</div>
</div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="hex">Hex(十六进制)</option>
<option value="base64">Base64</option>
</select>
</div>
<div class="row pt-1 pb-1 mb-3">
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="key_compress">
压缩
</button>
</div>
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="key_uncompress">
还原
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="{$cdn_cdnjs}FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="/assets/sm/sm2.js"></script>
<script src="/assets/sm/sm2_utils.js"></script>
<script>
new Vue({
el: '#app',
data: {
show: 0,
mutipart_from: JSON.parse(localStorage.getItem('{$plugin.alias}_mutipart_from')) || {
public_key: '',
private_key: '',
key_type: 'hex'
},
crypto_from: {
type: 1,
data_type: 'hex',
origin: '',
coded: ''
},
sign_form: {
hash: 1,
type: 1,
data_type: 'hex',
data: '',
sign: '',
userid: '1234567812345678'
},
convert_from: {
type: 0,
cert: '',
csr: ''
},
},
watch: {
mutipart_from: {
handler: (newVal) => {
localStorage.setItem('{$plugin.alias}_mutipart_from', JSON.stringify(newVal))
},
deep: true,
},
"mutipart_from.key_type"(newVal) {
if(this.mutipart_from.public_key != ''){
if(newVal == 'hex'){
this.mutipart_from.public_key = base64ToHex(this.mutipart_from.public_key)
}else{
this.mutipart_from.public_key = hexToBase64(this.mutipart_from.public_key)
}
}
if(this.mutipart_from.private_key != ''){
if(newVal == 'hex'){
this.mutipart_from.private_key = base64ToHex(this.mutipart_from.private_key)
}else{
this.mutipart_from.private_key = hexToBase64(this.mutipart_from.private_key)
}
}
}
},
methods: {
generate() {
var keypair = sm2.generateKeyPairHex()
this.mutipart_from.public_key = keypair.publicKey
this.mutipart_from.private_key = keypair.privateKey
if(this.mutipart_from.key_type == 'base64'){
this.mutipart_from.public_key = hexToBase64(this.mutipart_from.public_key)
this.mutipart_from.private_key = hexToBase64(this.mutipart_from.private_key)
}
},
getPublicKey(){
if(this.mutipart_from.public_key == '') return;
if(this.mutipart_from.key_type == 'base64'){
return base64ToHex(this.mutipart_from.public_key)
}else{
return this.mutipart_from.public_key
}
},
getPrivateKey(){
if(this.mutipart_from.private_key == '') return;
if(this.mutipart_from.key_type == 'base64'){
return base64ToHex(this.mutipart_from.private_key)
}else{
return this.mutipart_from.private_key
}
},
encrypt() {
var that=this;
if (this.mutipart_from.public_key == '' || this.mutipart_from.private_key == ''){
layer.alert("SM2公钥和私钥不能为空");return
}
if (this.crypto_from.origin === "") {
layer.alert("原文不得为空")
return
}
this.crypto_from.coded = sm2.doEncrypt(this.crypto_from.origin, this.getPublicKey(), this.crypto_from.type);
if(this.crypto_from.data_type == 'base64'){
this.crypto_from.coded = hexToBase64(this.crypto_from.coded)
}
},
decrypt() {
var that=this;
if (this.mutipart_from.public_key == '' || this.mutipart_from.private_key == ''){
layer.alert("SM2公钥和私钥不能为空");return
}
if (this.crypto_from.coded === "") {
layer.alert("密文不得为空")
return
}
var coded = this.crypto_from.coded;
if(this.crypto_from.data_type == 'base64'){
coded = base64ToHex(coded)
}
this.crypto_from.origin = sm2.doDecrypt(coded, this.getPrivateKey(), this.crypto_from.type);
},
verify() {
if (this.mutipart_from.public_key == '' || this.mutipart_from.private_key == ''){
layer.alert("SM2公钥和私钥不能为空");return
}
if (this.sign_form.sign === "") {
layer.alert("签名不得为空")
return
}
if (this.sign_form.data === "") {
layer.alert("待签名内容不得为空")
return
}
var sign = this.sign_form.sign;
if(this.sign_form.data_type == 'base64'){
sign = base64ToHex(sign)
}
var verifyResult = sm2.doVerifySignature(this.sign_form.data, sign, this.getPublicKey(), {der: this.sign_form.type, hash: this.sign_form.hash, userId: this.sign_form.userid})
if(verifyResult){
layer.alert('验签成功,签名正确', {icon:1})
}else{
layer.alert('验签失败,签名不正确', {icon:2})
}
},
sign() {
if (this.mutipart_from.public_key == '' || this.mutipart_from.private_key == ''){
layer.alert("SM2公钥和私钥不能为空");return
}
if (this.sign_form.data === "") {
layer.alert("待签名内容不得为空")
return
}
this.sign_form.sign = sm2.doSignature(this.sign_form.data, this.getPrivateKey(), {der: this.sign_form.type, hash: this.sign_form.hash, userId: this.sign_form.userid});
if(this.sign_form.data_type == 'base64'){
this.sign_form.sign = hexToBase64(this.sign_form.sign)
}
},
check() {
if (this.mutipart_from.public_key == '' || this.mutipart_from.private_key == ''){
layer.alert("SM2公钥和私钥不能为空");return
}
if(!sm2.verifyPublicKey(this.getPublicKey())){
layer.alert("SM2公钥不正确");return
}
var sign = sm2.doSignature('cccyun', this.getPrivateKey(), {hash: true});
var verifyResult = sm2.doVerifySignature('cccyun', sign, this.getPublicKey(), {hash: true})
if(verifyResult){
layer.alert('验证成功,公私钥匹配', {icon:1})
}else{
layer.alert('公私钥不匹配', {icon:2})
}
},
output() {
if(this.convert_from.type == 1 || this.convert_from.type == 2){
if (this.convert_from.type == 1 && this.convert_from.cert == ''){
layer.alert("SM2公钥证书不能为空");return
}
if (this.convert_from.type == 2 && this.convert_from.csr == ''){
layer.alert("CSR不能为空");return
}
var that=this;
return httpPost('/api/{$plugin.alias}/output', {
...this.convert_from
}, function(res){
if (res.status === "ok") {
that.mutipart_from.public_key = res.data
if(that.mutipart_from.key_type == 'base64'){
that.mutipart_from.public_key = hexToBase64(that.mutipart_from.public_key)
}
layer.alert(res.message, {icon:1})
}else{
layer.alert(res.message, {icon:2})
}
}, true)
}else{
if (this.mutipart_from.private_key == ''){
layer.alert("SM2私钥不能为空");return
}
this.mutipart_from.public_key = sm2.getPublicKeyFromPrivateKey(this.getPrivateKey());
if(this.mutipart_from.key_type == 'base64'){
this.mutipart_from.public_key = hexToBase64(this.mutipart_from.public_key)
}
layer.alert('导出公钥成功', {icon:1})
}
},
key_compress() {
if (this.mutipart_from.public_key == ''){
layer.alert("SM2公钥不能为空");return
}
if (this.mutipart_from.public_key.length == 66) return;
try{
this.mutipart_from.public_key = sm2.compressPublicKeyHex(this.getPublicKey())
if(this.mutipart_from.key_type == 'base64'){
this.mutipart_from.public_key = hexToBase64(this.mutipart_from.public_key)
}
layer.alert('公钥压缩成功', {icon:1})
} catch (e) {
layer.msg(e.message, {icon:2});
}
},
key_uncompress() {
if (this.mutipart_from.public_key == ''){
layer.alert("SM2公钥不能为空");return
}
if (this.mutipart_from.public_key.length == 130) return;
this.mutipart_from.public_key = decompressPublicKey(this.getPublicKey())
if(this.mutipart_from.key_type == 'base64'){
this.mutipart_from.public_key = hexToBase64(this.mutipart_from.public_key)
}
layer.alert('公钥还原成功', {icon:1})
},
download(name) {
var content = this.mutipart_from[name]
if(!content) return;
var filename = name + '.txt'
var blob = new Blob([content], {type: "application/force-download"});
saveAs(blob, filename);
}
},
})
</script>
{/block}
+60 -5
View File
@@ -19,6 +19,7 @@ class App extends Plugin
"private_key_bits" => 1024, "private_key_bits" => 1024,
//选择在创建CSR时应该使用哪些扩展。可选值有 OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_RSA 或 OPENSSL_KEYTYPE_EC. 默认值是 OPENSSL_KEYTYPE_RSA. //选择在创建CSR时应该使用哪些扩展。可选值有 OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_RSA 或 OPENSSL_KEYTYPE_EC. 默认值是 OPENSSL_KEYTYPE_RSA.
"private_key_type" => OPENSSL_KEYTYPE_RSA, "private_key_type" => OPENSSL_KEYTYPE_RSA,
"curve_name" => ''
]; ];
private $passPhrase; private $passPhrase;
private $rsaPrivateKey; private $rsaPrivateKey;
@@ -28,6 +29,7 @@ class App extends Plugin
private $origin; private $origin;
private $coded; private $coded;
private $opensslPadding; private $opensslPadding;
private $opensslAlgo = 1;
public function __construct() public function __construct()
@@ -35,13 +37,16 @@ class App extends Plugin
$this->passPhrase = request()->param("pass_phrase"); $this->passPhrase = request()->param("pass_phrase");
if($this->passPhrase == '') $this->passPhrase = null; if($this->passPhrase == '') $this->passPhrase = null;
$this->rsaPrivateKey = request()->param("private_key"); $this->rsaPrivateKey = request()->param("private_key");
$this->rsaPublicKey = request()->param("public_key"); $this->rsaPublicKey = $this->getPemKey(request()->param("public_key"), 'PUBLIC KEY');
$this->config["private_key_bits"] = request()->param("private_key_bits"); $this->config["private_key_type"] = request()->param("private_key_type/d");
$this->config["private_key_bits"] = request()->param("private_key_bits/d");
$this->config["curve_name"] = request()->param("curve_name");
$this->data = request()->param("data"); $this->data = request()->param("data");
$this->origin = request()->param("origin"); $this->origin = request()->param("origin");
$this->coded = request()->param("coded"); $this->coded = request()->param("coded");
$this->sign = request()->param("sign"); $this->sign = request()->param("sign");
$this->opensslPadding = request()->param("openssl_padding"); $this->opensslPadding = request()->param("openssl_padding");
$this->opensslAlgo = intval(request()->param("openssl_algo"));
} }
public function index() public function index()
@@ -179,7 +184,7 @@ class App extends Plugin
public function sign() public function sign()
{ {
try { try {
if (openssl_sign($this->data, $ret, $this->getRsaPrivateKey())) { if (openssl_sign($this->data, $ret, $this->getRsaPrivateKey(), $this->opensslAlgo)) {
$ret = base64_encode($ret); $ret = base64_encode($ret);
return msg('ok', 'success', $ret); return msg('ok', 'success', $ret);
} }
@@ -203,7 +208,7 @@ class App extends Plugin
$ret = false; $ret = false;
$sign = base64_decode($this->sign); $sign = base64_decode($this->sign);
if ($sign !== false) { if ($sign !== false) {
if (openssl_verify($this->data, $sign, $this->rsaPublicKey) === 1) { if (openssl_verify($this->data, $sign, $this->rsaPublicKey, $this->opensslAlgo) === 1) {
$ret = true; $ret = true;
} }
} }
@@ -231,7 +236,25 @@ class App extends Plugin
public function output() public function output()
{ {
$type = input('post.type/d');
try { try {
if($type == 1){
$cert = input('post.cert');
$pkey_res = openssl_pkey_get_public($cert);
if(!$pkey_res){
return msg('error', '导出失败,公钥证书不正确');
}
$pkey_detail = openssl_pkey_get_details($pkey_res);
return msg('ok', '导出成功', $pkey_detail['key']);
}elseif($type == 2){
$csr = input('post.csr');
$pkey_res = openssl_csr_get_public_key($csr);
if(!$pkey_res){
return msg('error', '导出失败,CSR不正确');
}
$pkey_detail = openssl_pkey_get_details($pkey_res);
return msg('ok', '导出成功', $pkey_detail['key']);
}
$openssl_pkey_get_details = openssl_pkey_get_details($this->getRsaPrivateKey()); $openssl_pkey_get_details = openssl_pkey_get_details($this->getRsaPrivateKey());
if ($openssl_pkey_get_details && !empty($openssl_pkey_get_details['key'])) { if ($openssl_pkey_get_details && !empty($openssl_pkey_get_details['key'])) {
return msg('ok', '导出成功', $openssl_pkey_get_details['key']); return msg('ok', '导出成功', $openssl_pkey_get_details['key']);
@@ -242,15 +265,47 @@ class App extends Plugin
} }
} }
public function key_encrypt()
{
try {
$privateKey = openssl_pkey_get_private($this->getPemKey($this->rsaPrivateKey, 'PRIVATE KEY'));
if ($privateKey === false) {
throw new \Exception('私钥不正确或已被加密');
}
openssl_pkey_export($privateKey, $encrypted, $this->passPhrase);
return msg('ok', '加密成功', $encrypted);
} catch (\Exception $e) {
return msg('error', $e->getMessage());
}
}
public function key_decrypt()
{
try {
openssl_pkey_export($this->getRsaPrivateKey(), $decrypted);
return msg('ok', '解密成功', $decrypted);
} catch (\Exception $e) {
return msg('error', $e->getMessage());
}
}
/** /**
* @return array|mixed|null * @return array|mixed|null
*/ */
public function getRsaPrivateKey() public function getRsaPrivateKey()
{ {
$privateKey = openssl_pkey_get_private($this->rsaPrivateKey, $this->passPhrase); $privateKey = openssl_pkey_get_private($this->getPemKey($this->rsaPrivateKey, $this->passPhrase ? 'ENCRYPTED PRIVATE KEY' : 'PRIVATE KEY'), $this->passPhrase);
if ($privateKey === false) { if ($privateKey === false) {
throw new \Exception('私钥不正确或密码错误'); throw new \Exception('私钥不正确或密码错误');
} }
return $privateKey; return $privateKey;
} }
private function getPemKey($str, $type){
if(empty($str) || strpos($str, '-----BEGIN')!==false) return $str;
$pem = "-----BEGIN ".$type."-----\n" .
wordwrap($str, 64, "\n", true) .
"\n-----END ".$type."-----";
return $pem;
}
} }
+245 -73
View File
@@ -6,21 +6,34 @@
<div class="card card-preview"> <div class="card card-preview">
<div class="card-inner mt-3"> <div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating"> <div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">RSA公私钥生成、验证、加密和解密</span> <span class="nk-menu-text font-weight-bold">RSA公私钥生成、签名、加解密</span>
</div> </div>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"><a class="nav-link" @click="show=0" :class="{'active':show===0}" href="javascript:">RSA公私钥生成</a></li> <li class="nav-item"><a class="nav-link" @click="show=0" :class="{'active':show===0}" href="javascript:">公私钥生成</a></li>
<li class="nav-item"><a class="nav-link" @click="show=1" :class="{'active':show===1}" href="javascript:">RSA加密解密</a></li> <li class="nav-item"><a class="nav-link" @click="show=1" :class="{'active':show===1}" href="javascript:">加密解密</a></li>
<li class="nav-item"><a class="nav-link" @click="show=2" :class="{'active':show===2}" href="javascript:">RSA签名验签</a></li> <li class="nav-item"><a class="nav-link" @click="show=2" :class="{'active':show===2}" href="javascript:">签名验签</a></li>
<li class="nav-item"><a class="nav-link" @click="show=3" :class="{'active':show===3}" href="javascript:">RSA公私钥对验证</a></li> <li class="nav-item"><a class="nav-link" @click="show=3" :class="{'active':show===3}" href="javascript:">公私钥对验证</a></li>
<li class="nav-item"><a class="nav-link" @click="show=4" :class="{'active':show===4}" href="javascript:">RSA公钥导出</a></li> <li class="nav-item"><a class="nav-link" @click="show=4" :class="{'active':show===4}" href="javascript:">公钥导出</a></li>
<li class="nav-item"><a class="nav-link" @click="show=5" :class="{'active':show===5}" href="javascript:">私钥加解密</a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane" id="tabItem1" :class="{'active':show===0}"> <div class="tab-pane" id="tabItem1" :class="{'active':show===0}">
<div class="form-group"> <div class="form-group">
<label class="form-label">密钥算法</label>
<select class="form-control" v-model="generate_from.private_key_type">
<option v-for="v,index in private_key_type" :value="index">{{v}}</option>
</select>
</div>
<div class="form-group" v-if="generate_from.private_key_type!=3">
<label class="form-label">密钥长度</label> <label class="form-label">密钥长度</label>
<select class="form-control" v-model="generate_from.private_key_bits"> <select class="form-control" v-model="generate_from.private_key_bits">
<option v-for="v in [512,1024,2048,4096]" :value="v">{{v}} bit</option> <option v-for="v in private_key_bits" :value="v">{{v}} bit</option>
</select>
</div>
<div class="form-group" v-if="generate_from.private_key_type==3">
<label class="form-label">曲线名称</label>
<select class="form-control" v-model="generate_from.curve_name">
<option v-for="v in curve_name" :value="v">{{v}}</option>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -34,18 +47,25 @@
</button> </button>
<div class="d-flex flex-wrap"> <div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2"> <div class="form-group flex-fill mr-2">
<label class="form-label">RSA公钥</label> <label class="form-label">RSA公钥 <em class="icon ni ni-download" title="下载保存" v-show="mutipart_from.public_key" @click="download('public_key')"></em></label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea> <textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea>
</div> </div>
</div> </div>
<div class="form-group flex-fill"> <div class="form-group flex-fill">
<label class="form-label">RSA私钥</label> <label class="form-label">RSA私钥 <em class="icon ni ni-download" title="下载保存" v-show="mutipart_from.private_key" @click="download('private_key')"></em></label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea> <textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="pem">PEM标准格式</option>
<option value="base64">Base64无换行</option>
</select>
</div>
</div> </div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===1}"> <div class="tab-pane" id="tabItem1" :class="{'active':show===1}">
<div class="form-group"> <div class="form-group">
@@ -61,12 +81,6 @@
<option :value="1">使用私钥加密,使用公钥解密</option> <option :value="1">使用私钥加密,使用公钥解密</option>
</select> </select>
</div> </div>
<div class="form-group">
<label class="form-label">私钥密码</label>
<div class="form-control-wrap">
<input type="text" v-model="mutipart_from.pass_phrase" placeholder="没有密码请留空" class="form-control">
</div>
</div>
<div class="row pt-1 pb-1 mb-3"> <div class="row pt-1 pb-1 mb-3">
<div class="col-6"> <div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="encrypt"> <button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="encrypt">
@@ -79,20 +93,6 @@
</button> </button>
</div> </div>
</div> </div>
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">RSA公钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">RSA私钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">原文,未加密的内容</label> <label class="form-label">原文,未加密的内容</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
@@ -113,12 +113,6 @@
<option v-for="v,index in openssl_algo" :value="index+1">{{v}}</option> <option v-for="v,index in openssl_algo" :value="index+1">{{v}}</option>
</select> </select>
</div> </div>
<div class="form-group">
<label class="form-label">私钥密码</label>
<div class="form-control-wrap">
<input type="text" v-model="mutipart_from.pass_phrase" placeholder="没有密码请留空" class="form-control">
</div>
</div>
<div class="row pt-1 pb-1 mb-3"> <div class="row pt-1 pb-1 mb-3">
<div class="col-6"> <div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="sign"> <button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="sign">
@@ -131,20 +125,6 @@
</button> </button>
</div> </div>
</div> </div>
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">RSA公钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">RSA私钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">待签名,验签原始内容</label> <label class="form-label">待签名,验签原始内容</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
@@ -159,52 +139,119 @@
</div> </div>
</div> </div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===3}"> <div class="tab-pane" id="tabItem1" :class="{'active':show===3}">
<div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2">
<label class="form-label">RSA公钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea>
</div>
</div>
<div class="form-group flex-fill">
<label class="form-label">RSA私钥</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">私钥密码</label> <label class="form-label">私钥密码</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<input type="text" v-model="mutipart_from.pass_phrase" placeholder="没有密码请留空" class="form-control"> <input type="text" v-model="mutipart_from.pass_phrase" placeholder="没有密码请留空" class="form-control">
</div> </div>
</div> </div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="pem">PEM标准格式</option>
<option value="base64">Base64无换行</option>
</select>
</div>
<button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="check"> <button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="check">
验证 验证
</button> </button>
</div>
<div class="tab-pane" id="tabItem1" :class="{'active':show===4}">
<div class="form-group">
<label class="form-label">导出方式</label>
<select class="form-control" v-model="convert_from.type">
<option :value="0">从私钥导出公钥</option>
<option :value="1">从公钥证书导出公钥</option>
<option :value="2">从CSR导出公钥</option>
</select>
</div>
<div class="d-flex flex-wrap"> <div class="d-flex flex-wrap">
<div class="form-group flex-fill mr-2"> <div class="form-group flex-fill mr-2">
<label class="form-label">RSA公钥</label> <label class="form-label">RSA公钥</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea> <textarea class="form-control" rows="8" v-model="mutipart_from.public_key" placeholder="导出结果"></textarea>
</div> </div>
</div> </div>
<div class="form-group flex-fill"> <div class="form-group flex-fill" v-show="convert_from.type==0">
<label class="form-label">RSA私钥</label> <label class="form-label">RSA私钥</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea> <textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea>
</div> </div>
</div> </div>
<div class="form-group flex-fill" v-show="convert_from.type==1">
<label class="form-label">RSA公钥证书</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="convert_from.cert" placeholder="-----BEGIN CERTIFICATE-----"></textarea>
</div>
</div>
<div class="form-group flex-fill" v-show="convert_from.type==2">
<label class="form-label">CSR</label>
<div class="form-control-wrap">
<textarea class="form-control" rows="8" v-model="convert_from.csr" placeholder="-----BEGIN CERTIFICATE REQUEST-----"></textarea>
</div>
</div>
</div> </div>
</div> <div class="form-group" v-show="convert_from.type==0">
<div class="tab-pane" id="tabItem1" :class="{'active':show===4}">
<div class="form-group">
<label class="form-label">私钥密码</label> <label class="form-label">私钥密码</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<input type="text" v-model="mutipart_from.pass_phrase" placeholder="没有密码请留空" class="form-control"> <input type="text" v-model="mutipart_from.pass_phrase" placeholder="没有密码请留空" class="form-control">
</div> </div>
</div> </div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="pem">PEM标准格式</option>
<option value="base64">Base64无换行</option>
</select>
</div>
<button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="output"> <button class="btn btn-dim btn-outline-secondary btn-block card-link mb-3" @click="output">
导出 导出
</button> </button>
<div class="d-flex flex-wrap"> </div>
<div class="form-group flex-fill mr-2"> <div class="tab-pane" id="tabItem1" :class="{'active':show===5}">
<label class="form-label">RSA公钥</label> <div class="form-group">
<div class="form-control-wrap"> <label class="form-label">RSA私钥</label>
<textarea class="form-control" rows="8" v-model="mutipart_from.public_key"></textarea> <div class="form-control-wrap">
</div> <textarea class="form-control" rows="6" v-model="mutipart_from.private_key"></textarea>
</div> </div>
<div class="form-group flex-fill"> </div>
<label class="form-label">RSA私钥</label> <div class="form-group">
<div class="form-control-wrap"> <label class="form-label">私钥密码</label>
<textarea class="form-control" rows="8" v-model="mutipart_from.private_key"></textarea> <div class="form-control-wrap">
</div> <input type="text" v-model="mutipart_from.pass_phrase" placeholder="" class="form-control">
</div>
</div>
<div class="form-group">
<label class="form-label">密钥数据类型</label>
<select class="form-control" v-model="mutipart_from.key_type">
<option value="pem">PEM标准格式</option>
<option value="base64">Base64无换行</option>
</select>
</div>
<div class="row pt-1 pb-1 mb-3">
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="key_encrypt">
加密
</button>
</div>
<div class="col-6">
<button class="btn btn-dim btn-outline-secondary btn-block card-link" @click="key_decrypt">
解密
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -216,11 +263,43 @@
{/block} {/block}
{block name="script"} {block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script> <script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="{$cdn_cdnjs}FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script> <script>
function pemToBase64(pem) {
var lines = pem.split('\n');
var encoded = '';
for (var i = 0; i < lines.length; i++) {
if (lines[i].trim().length > 0 && lines[i].indexOf('-----BEGIN') === -1 && lines[i].indexOf('-----END') === -1) {
encoded += lines[i].trim();
}
}
return encoded;
}
function base64ToPem(base64Key, type) {
var pemHeader = '-----BEGIN ' + type + '-----\n';
var pemFooter = '\n-----END ' + type + '-----\n';
var chunkSize = 64;
var base64KeyWithBreaks = base64Key.match(new RegExp('.{1,' + chunkSize + '}', 'g')).join('\n');
var pemKey = pemHeader + base64KeyWithBreaks + pemFooter;
return pemKey;
}
new Vue({ new Vue({
el: '#app', el: '#app',
data: { data: {
show: parseInt(localStorage.getItem('{$plugin.alias}_show') || 0), show: 0,
private_key_type: [
'RSA',
'DSA',
'DH',
'ECC'
],
private_key_bits: [512,1024,2048,4096],
curve_name: [
'prime256v1',
'secp256k1',
'secp384r1',
'secp521r1',
],
openssl_padding: [ openssl_padding: [
'OPENSSL_PKCS1_PADDING', 'OPENSSL_PKCS1_PADDING',
'OPENSSL_SSLV23_PADDING', 'OPENSSL_SSLV23_PADDING',
@@ -239,13 +318,21 @@
'OPENSSL_ALGO_SHA512', 'OPENSSL_ALGO_SHA512',
'OPENSSL_ALGO_RMD160', 'OPENSSL_ALGO_RMD160',
], ],
mutipart_from: JSON.parse(localStorage.getItem('{$plugin.alias}_mutipart_from')) || { mutipart_from: JSON.parse(localStorage.getItem('{$plugin.alias}_from')) || {
public_key: '', public_key: '',
private_key: '', private_key: '',
pass_phrase: '', pass_phrase: '',
key_type: 'pem',
},
convert_from: {
type: 0,
cert: '',
csr: ''
}, },
generate_from: { generate_from: {
private_key_bits: 1024, private_key_type: 0,
private_key_bits: 2048,
curve_name: 'prime256v1'
}, },
crypto_from: { crypto_from: {
openssl_padding: 1, openssl_padding: 1,
@@ -260,14 +347,34 @@
}, },
}, },
watch: { watch: {
show(newVal) {
localStorage.setItem('{$plugin.alias}_show', newVal)
},
mutipart_from: { mutipart_from: {
handler: (newVal) => { handler: (newVal) => {
localStorage.setItem('{$plugin.alias}_mutipart_from', JSON.stringify(newVal)) localStorage.setItem('{$plugin.alias}_from', JSON.stringify(newVal))
}, },
deep: true, deep: true,
},
"mutipart_from.key_type"(newVal) {
if(this.mutipart_from.public_key != ''){
if(newVal == 'pem'){
this.mutipart_from.public_key = base64ToPem(this.mutipart_from.public_key, 'PUBLIC KEY')
}else{
this.mutipart_from.public_key = pemToBase64(this.mutipart_from.public_key)
}
}
if(this.mutipart_from.private_key != ''){
if(newVal == 'pem'){
this.mutipart_from.private_key = base64ToPem(this.mutipart_from.private_key, this.mutipart_from.pass_phrase ? 'ENCRYPTED PRIVATE KEY' : 'PRIVATE KEY')
}else{
this.mutipart_from.private_key = pemToBase64(this.mutipart_from.private_key)
}
}
},
"generate_from.private_key_type"(newVal) {
if(newVal == 2){
this.private_key_bits = [512,1024,2048]
}else{
this.private_key_bits = [512,1024,2048,4096]
}
} }
}, },
methods: { methods: {
@@ -279,6 +386,10 @@
}, function(data){ }, function(data){
that.mutipart_from.public_key = data.public_key that.mutipart_from.public_key = data.public_key
that.mutipart_from.private_key = data.private_key that.mutipart_from.private_key = data.private_key
if(that.mutipart_from.key_type == 'base64'){
that.mutipart_from.public_key = pemToBase64(that.mutipart_from.public_key)
that.mutipart_from.private_key = pemToBase64(that.mutipart_from.private_key)
}
}) })
}, },
encrypt() { encrypt() {
@@ -367,21 +478,82 @@
}, true) }, true)
}, },
output() { output() {
if (this.mutipart_from.private_key == ''){ if (this.convert_from.type == 0 && this.mutipart_from.private_key == ''){
layer.alert("RSA私钥不能为空");return layer.alert("RSA私钥不能为空");return
} }
if (this.convert_from.type == 1 && this.convert_from.cert == ''){
layer.alert("RSA公钥证书不能为空");return
}
if (this.convert_from.type == 2 && this.convert_from.csr == ''){
layer.alert("CSR不能为空");return
}
var that=this; var that=this;
return httpPost('/api/{$plugin.alias}/output', { return httpPost('/api/{$plugin.alias}/output', {
...this.convert_from,
...this.mutipart_from ...this.mutipart_from
}, function(res){ }, function(res){
if (res.status === "ok") { if (res.status === "ok") {
that.mutipart_from.public_key = res.data that.mutipart_from.public_key = res.data
if(that.mutipart_from.key_type == 'base64'){
that.mutipart_from.public_key = pemToBase64(that.mutipart_from.public_key)
}
layer.alert(res.message, {icon:1}) layer.alert(res.message, {icon:1})
}else{ }else{
layer.alert(res.message, {icon:2}) layer.alert(res.message, {icon:2})
} }
}, true) }, true)
}, },
key_encrypt() {
if (this.mutipart_from.private_key == ''){
layer.alert("RSA私钥不能为空");return
}
if (this.mutipart_from.pass_phrase == ''){
layer.alert("私钥密码不能为空");return
}
var that=this;
return httpPost('/api/{$plugin.alias}/key_encrypt', {
...this.mutipart_from
}, function(res){
if (res.status === "ok") {
that.mutipart_from.private_key = res.data
if(that.mutipart_from.key_type == 'base64'){
that.mutipart_from.private_key = pemToBase64(that.mutipart_from.private_key)
}
layer.alert(res.message, {icon:1})
}else{
layer.alert(res.message, {icon:2})
}
}, true)
},
key_decrypt() {
if (this.mutipart_from.private_key == ''){
layer.alert("RSA私钥不能为空");return
}
if (this.mutipart_from.pass_phrase == ''){
layer.alert("私钥密码不能为空");return
}
var that=this;
return httpPost('/api/{$plugin.alias}/key_decrypt', {
...this.mutipart_from
}, function(res){
if (res.status === "ok") {
that.mutipart_from.private_key = res.data
if(that.mutipart_from.key_type == 'base64'){
that.mutipart_from.private_key = pemToBase64(that.mutipart_from.private_key)
}
layer.alert(res.message, {icon:1})
}else{
layer.alert(res.message, {icon:2})
}
}, true)
},
download(name) {
var content = this.mutipart_from[name]
if(!content) return;
var filename = name + (this.mutipart_from.key_type == 'base64' ? '.txt' : '.pem')
var blob = new Blob([content], {type: "application/force-download"});
saveAs(blob, filename);
}
}, },
}) })
</script> </script>
+40 -5
View File
@@ -29,6 +29,36 @@
<div class="form-group"> <div class="form-group">
<label class="form-label">下载链接</label> <label class="form-label">下载链接</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<select v-model="set.nodeurl" type="text" class="form-control">
<optgroup label="运营商">
<option value="https://freeserver.migufun.com/resource/beta/apk/20231114094513/MiguPlay-V3.69.1.1_miguzsj.apk">
咪咕快游①</option>
<option value="https://freeserver.migufun.com/resource/beta/video/system/20210924112351666.mp4">
咪咕快游②</option>
<option value="https://im.zdxlz.com/downloadmi/file-im-sdk/apk/mobile/%E9%87%8F%E5%AD%90%E5%AF%86%E4%BF%A1.apk">
量子密信</option>
<option value="https://img.mcloud.139.com/material_prod/material_media/20221128/1669626861087.png">
和彩云</option>
<option value="https://vod-origin-rjzy.gdoss.xstore.ctyun.cn/1de5da90486f42e8939deed107fd64a3.mp4">
天翼云</option>
</optgroup>
<optgroup label="其他">
<option value="https://vipspeedtest8.wuhan.net.cn:8080/download?size=1073741824" selected>
电信测速</option>
<option value="https://mirrors.sjtug.sjtu.edu.cn/speedtest/garbage?ckSize=1024">
上海交大</option>
</optgroup>
<optgroup label="全球[Global]">
<option value="https://speed.cloudflare.com/__down?bytes=104857600">
Cloudflare</option>
<option value="https://akamtrans-a.akamaihd.net/delivery/2023/08/30/brand-protector-2500k-00004.ts">
Akamai</option>
<option value="https://gh.con.sh/https://github.com/AaronFeng753/Waifu2x-Extension-GUI/releases/download/v2.21.12/Waifu2x-Extension-GUI-v2.21.12-Portable.7z">
Cloudflare Workers</option>
<option value="https://img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RW16Ptm">
Microsoft Akamai</option>
</optgroup>
</select>
<input type="text" v-model="set.input" placeholder="下载链接" class="form-control" autocomplete="off"> <input type="text" v-model="set.input" placeholder="下载链接" class="form-control" autocomplete="off">
</div> </div>
</div> </div>
@@ -91,12 +121,14 @@ new Vue({
el: '#app', el: '#app',
data: { data: {
set: { set: {
input: 'https://cachefly.cachefly.net/100mb.test', nodeurl: 'https://vipspeedtest8.wuhan.net.cn:8080/download?size=1073741824',
input: '',
output: '', output: '',
infinite: true, infinite: true,
status: false, status: false,
thread: 1, thread: 4,
}, },
node_list: [],
tasks: [], tasks: [],
speed: '0.00B/s', speed: '0.00B/s',
spend: 0, spend: 0,
@@ -127,8 +159,14 @@ new Vue({
clearInterval(this.timer) clearInterval(this.timer)
this.cancelSource.cancel() this.cancelSource.cancel()
} }
},
'set.nodeurl'(newVal) {
this.set.input = newVal
} }
}, },
mounted() {
this.set.input = this.set.nodeurl
},
methods: { methods: {
run() { run() {
this.set.status = !this.set.status this.set.status = !this.set.status
@@ -141,9 +179,6 @@ new Vue({
const index = this.tasks.push(speed) - 1 const index = this.tasks.push(speed) - 1
return axios.request({ return axios.request({
url: this.set.input, url: this.set.input,
params: {
[id]: id,
},
cancelToken: this.cancelSource.token, cancelToken: this.cancelSource.token,
onDownloadProgress: function (progressEvent) { onDownloadProgress: function (progressEvent) {
// 处理原生进度事件 // 处理原生进度事件
@@ -41,6 +41,9 @@ class dianping implements api
if($url){ if($url){
$visitid = getSubstr($url, 'visitId=', '&'); $visitid = getSubstr($url, 'visitId=', '&');
if($visitid){ if($visitid){
$url = 'https://kf.dianping.com/api/portal/message/init?visitId='.$visitid.'&accessToken=undefined';
$post = '{"type":"Init","parameters":{"isPreview":true,"build":null}}';
get_curl($url, $post);
cache('dianping_visitid', $visitid, 86400); cache('dianping_visitid', $visitid, 86400);
return $visitid; return $visitid;
} }
+13 -2
View File
@@ -50,6 +50,7 @@
{block name="script"} {block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script> <script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="{$cdn_cdnjs}crypto-js/4.1.1/crypto-js.min.js"></script> <script src="{$cdn_cdnjs}crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="/assets/sm/sm3.js"></script>
<script> <script>
new Vue({ new Vue({
el: '#app', el: '#app',
@@ -73,20 +74,30 @@
'sha512', 'sha512',
'sha384', 'sha384',
'ripemd160', 'ripemd160',
'sm3',
]; ];
let arr = {} let arr = {}
if(this.set.key == ''){ if(this.set.key == ''){
for (const v of list) { for (const v of list) {
arr[v] = CryptoJS[v.toUpperCase()](this.set.text).toString() if(v == 'sm3') arr[v] = sm3(this.set.text)
else arr[v] = CryptoJS[v.toUpperCase()](this.set.text).toString()
} }
}else{ }else{
for (const v of list) { for (const v of list) {
arr['hmac-' + v] = CryptoJS['Hmac' + v.toUpperCase()](this.set.text, this.set.key).toString() if(v == 'sm3') arr['hmac-' + v] = sm3(this.set.text, {key: this.stringToHex(this.set.key)})
else arr['hmac-' + v] = CryptoJS['Hmac' + v.toUpperCase()](this.set.text, this.set.key).toString()
} }
} }
this.result = arr this.result = arr
}, },
stringToHex(str){
    var val="";
    for(var i = 0; i < str.length; i++){
      val += str.charCodeAt(i).toString(16);
    }
    return val;
  }
}, },
}) })
-87
View File
@@ -1,87 +0,0 @@
<?php
/**
* uniCloud文件快传
*/
namespace plugin\utility\unicloud;
use app\Plugin;
use think\facade\Db;
use plugin\utility\unicloud\UnicloudClient;
use Exception;
class App extends Plugin
{
private $unicloud;
public function index()
{
return $this->view();
}
public function preUpload(){
if(!input('?post.filename'))exit('{"code":-1,"msg":"请选择文件"}');
try{
$this->getSession();
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>'获取AccessToken失败:' . $e->getMessage()];
}
try{
$result = $this->unicloud->pre_upload_file(input('post.filename'));
$fileurl = 'https://'.$result['cdnDomain'].'/'.$result['ossPath'];
session('unicloud_filename', input('post.filename'));
session('unicloud_fileurl', $fileurl);
return json(['code'=>0, 'data'=>$result]);
} catch (Exception $e) {
return json(['code'=>-1, 'msg'=>'准备文件上传失败:' . $e->getMessage()]);
}
}
public function completeUpload(){
if(!input('?post.id'))exit('{"code":-1,"msg":"no id"}');
try{
$this->getSession();
} catch (Exception $e) {
return ['code'=>-1, 'msg'=>'获取AccessToken失败:' . $e->getMessage()];
}
try{
$result = $this->unicloud->complete_upload_file(input('post.id'));
Db::name('uploadlog')->insert([
'uid' => request()->islogin ? request()->user['id'] : 0,
'type' => 'file',
'source' => 'unicloud',
'filename' => session('unicloud_filename'),
'fileurl' => session('unicloud_fileurl'),
'ip' => $this->clientip,
'addtime' => date("Y-m-d H:i:s")
]);
session('unicloud_filename', null);
session('unicloud_fileurl', null);
return json(['code'=>0, 'data'=>$result]);
} catch (Exception $e) {
return json(['code'=>-1, 'msg'=>'完成文件上传失败:' . $e->getMessage()]);
}
}
private function getSession(){
include dirname(__FILE__).'/config.php';
$this->unicloud = new UnicloudClient($spaceId, $clientSecret);
if(session('access_token') && session('access_token_expire') && session('access_token_expire')>time()){
$access_token = session('access_token');
$this->unicloud->set_access_token($access_token);
}else{
$access_token = $this->unicloud->get_access_token();
session('access_token', $access_token);
session('access_token_expire', time()+600);
}
return null;
}
}
-144
View File
@@ -1,144 +0,0 @@
<?php
// uniCloud云存储客户端
namespace plugin\utility\unicloud;
use Exception;
class UnicloudClient {
private $spaceId;
private $clientSecret;
private $endpoint = "https://api.bspapp.com";
private $accessToken = null;
function __construct($spaceId, $clientSecret){
$this->spaceId = $spaceId;
$this->clientSecret = $clientSecret;
}
public function set_access_token($accessToken){
$this->accessToken = $accessToken;
}
// 获取AccessToken
public function get_access_token(){
if(!empty($this->accessToken)) return $this->accessToken;
$param = [
'method' => 'serverless.auth.user.anonymousAuthorize',
'params' => '{}',
'spaceId' => $this->spaceId,
'timestamp' => $this->msec_time()
];
$sign = $this->get_sign($param);
$payload = json_encode($param);
$header = [
'x-serverless-sign: '.$sign
];
$result = $this->curl_post($payload, $header);
$this->accessToken = $result['accessToken'];
return $this->accessToken;
}
// 获取文件上传信息
public function pre_upload_file($filename){
$method = 'serverless.file.resource.generateProximalSign';
$params = [
'env' => 'public',
'filename' => $filename
];
$result = $this->send_request($method, $params);
return $result;
}
// 完成文件上传
public function complete_upload_file($id){
$method = 'serverless.file.resource.report';
$params = [
'id' => $id
];
$this->send_request($method, $params);
return true;
}
// 删除文件(不支持阿里云)
public function delete_file($id){
$method = 'serverless.file.resource.delete';
$params = [
'id' => $id
];
$this->send_request($method, $params);
return true;
}
private function send_request($method, $params){
$access_token = $this->get_access_token();
$postparam = [
'method' => $method,
'params' => json_encode($params),
'spaceId' => $this->spaceId,
'timestamp' => $this->msec_time(),
'token' => $access_token
];
$sign = $this->get_sign($postparam);
$payload = json_encode($postparam);
$header = [
'x-basement-token: '.$access_token,
'x-serverless-sign: '.$sign
];
$result = $this->curl_post($payload, $header);
return $result;
}
private function get_sign($param){
$signPars = "";
ksort($param);
foreach ($param as $k => $v) {
if ($v != '') {
$signPars .= $k . '=' . $v . '&';
}
}
$signPars = substr($signPars, 0, -1);
$sign = hash_hmac('md5', $signPars, $this->clientSecret);
return $sign;
}
private function msec_time() {
list($msec, $sec) = explode(' ', microtime());
$msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
private function curl_post($payload, $header){
$url = $this->endpoint.'/client';
$httpheader[] = "accept: */*";
$httpheader[] = "accept-encoding: gzip,deflate,sdch";
$httpheader[] = "accept-language: zh-CN,zh;q=0.8";
$httpheader[] = "cache-control: no-cache";
$httpheader[] = "content-type: application/json";
$httpheader[] = "pragma: no-cache";
$httpheader[] = "connection: close";
$httpheader = array_merge($httpheader, $header);
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36');
$json=curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if($httpCode==200){
$arr=json_decode($json,true);
if($arr['success'] == true){
return $arr['data'];
}else{
throw new Exception($arr['error']['message'] ? $arr['error']['message'] : '未知错误');
}
}else{
throw new Exception('curl error! httpcode='.$httpCode);
}
}
}
-5
View File
@@ -1,5 +0,0 @@
<?php
$spaceId = '10b3891b-be67-4103-a60f-9da1d057470c';
$clientSecret = '6H2YuHYH4Cju+/5K8YIJjA==';
-312
View File
@@ -1,312 +0,0 @@
{extend name="common/plugin_layout" /}
{block name="title"}{$plugin.title} - {:config_get('title')}{/block}
{block name="main"}
<style>
.btn-block {
white-space:normal;
}
.btn-active {background-color: #1c2b46;color: #fff;}
</style>
<div class="container-xl" id="app">
<div class="col-sm-12 col-md-10 col-xl-9 center-block">
<div class="card card-preview">
<div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">uniCloud文件快传</span>
</div>
<div class="progress progress-lg mt-1 mb-1">
<div class="progress-bar progress-bar-striped progress-bar-animated" v-bind:style="{ width: progress + '%' }">{{progress}}%</div>
</div>
<div class="form-group">
<div class="text-center pt-5 pb-5 btn btn-lg btn-block btn-outline-light mb-4 d-block" id="fileInput">
<div class="preview-icon-wrap"><em class="ni ni-upload"></em></div><span>点击选择文件/Ctrl+V粘贴/拖拽到此处</span>
<input type="file" id="file" style="opacity: 0;position: absolute;cursor: pointer;width: 100%;height: 100%;left: 0;top: 0;" @change="selectFile">
</div>
</div>
<div class="form-group">
<div class="btn-group">
<button v-for="v in set.output_types.items" class="btn btn-outline-dark btn-sm"
:class="{'btn-active':set.output_types.current===v.key}"
@click="set.output_types.current=v.key"
>
{{v.title}}
</button>
</div>
<div class="form-control-wrap">
<textarea class="form-control" id="output" v-model="result" rows="8" placeholder="这里显示上传的结果"></textarea>
</div>
<div class="text-center"><button class="btn btn-sm btn-outline-light" @click="copy"><em class="icon ni ni-copy"></em>点此复制</button>&nbsp;&nbsp;&nbsp;&nbsp;<button class="btn btn-sm btn-outline-light" @click="reset"><em class="icon ni ni-reload"></em>清空</button></div>
</div>
</div>
</div>
<div class="card card-preview">
<div class="card-inner">
<h6><em class="icon ni ni-info"></em> 工具说明</h6>
<div class="accordion-inner">
<p>uniCloud文件快传底层使用阿里云OSS存储,支持任何格式的文件,也可以当图床使用。</p>
<p>一次只能上传1个文件,文件大小限制100M。一经上传,将无法删除,本站也不会保存。</p>
</div>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
set: {
output_types: {
current: 'URL',
items: [
{
title: 'URL',
key: 'URL',
template: '#url#',
},
{
title: 'HTML',
key: 'HTML',
template: '<a href="#url#" target="_blank">#name#</a>',
},
{
title: 'BBCode',
key: 'BBCode',
template: '[url=#url#]#name#[/url]',
},
{
title: 'Markdown',
key: 'Markdown',
template: '[#name#](#url#)',
},
]
},
output: [],
},
progress: 0,
urls: {},
result: '',
},
mounted() {
var that=this;
document.addEventListener('paste', function(e) {
var items = ((e.clipboardData || window.clipboardData).items) || [];
var file = null;
if (items && items.length) {
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('text/') === -1) {
file = items[i].getAsFile();
break;
}
}
}
if (!file) {
return;
}
that.pasteFile(file)
});
},
watch: {
'set.output'(newVal) {
let list = {}
for (item of this.set.output_types.items) {
let arr = []
for (const v of newVal) {
arr.push(item.template.replaceAll('#url#', v.url).replaceAll('#name#', v.name))
}
list[item.key] = arr;
}
this.urls = list
},
'urls'(newVal) {
this.result = newVal[this.set.output_types.current].join('\n')
},
'set.output_types.current'(newVal) {
this.result = this.urls[newVal] ? this.urls[newVal].join('\n') : '';
}
},
methods: {
async preUpload(filename){
var that = this;
return new Promise((resolve, reject) => {
$.ajax({
type : "POST",
url : "/api/{$plugin.alias}/preUpload",
data : {filename: filename},
dataType : 'json',
success : function(data) {
if(data.code == 0){
resolve(data.data);
}else{
reject(data.msg);
}
},
error : function(){
reject('准备文件上传失败:接口错误');
}
});
})
},
async completeUpload(id){
var that = this;
return new Promise((resolve, reject) => {
$.ajax({
type : "POST",
url : "/api/{$plugin.alias}/completeUpload",
data : {id: id},
dataType : 'json',
success : function(data) {
if(data.code == 0){
resolve(data.data);
}else{
reject(data.msg);
}
},
error : function(){
reject('完成文件上传失败:接口错误');
}
});
})
},
async uploadFile(url, postdata, file){
var that = this;
return new Promise((resolve, reject) => {
var data = new FormData();
for(key in postdata){
data.append(key, postdata[key]);
}
data.append('file', file);
$.ajax({
type : "POST",
url : url,
data : data,
processData: false,
contentType: false,
dataType : 'html',
success : function(data) {
resolve();
},
error : function(){
reject('文件上传失败!');
},
xhr: function() {
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function (e) {
//console.log(e);
progressRate = Math.round(e.loaded / e.total * 100);
that.progress = progressRate;
})
return xhr;
}
});
})
},
async selectFile(e) {
var total = e.target.files.length;
if(total == 0) return;
var file = e.target.files[0];
if(file.size > 104857600){
layer.alert('文件大小限制100M');return;
}
this.progress = 0;
var url,postdata,fileid,fileurl;
var loading = layer.msg('正在准备文件上传', {icon: 16,shade: 0.3,time: 0});
await this.preUpload(file.name).then(data => {
fileid = data.id;
postdata = {'Cache-Control':'max-age=2592000', 'Content-Disposition':'attachment', 'OSSAccessKeyId':data.accessKeyId, 'Signature':data.signature, 'host':data.host, 'id':data.id, 'key':data.ossPath, 'policy':data.policy, 'success_action_status':'200'};
url = 'https://' + data.host + '/';
fileurl = 'https://' + data.cdnDomain + '/' + data.ossPath;
}, error => {
layer.close(loading);
layer.alert(error, {icon: 2});
$("#file").val('');
throw Error();
});
loading = layer.msg('正在上传文件,请稍候...', {icon: 16,shade: 0.3,time: 0});
await this.uploadFile(url, postdata, file).then(() => {
}, error => {
layer.close(loading);
layer.alert(error, {icon: 2});
$("#file").val('');
throw Error();
});
loading = layer.msg('上传成功,正在保存', {icon: 16,shade: 0.3,time: 0});
await this.completeUpload(fileid).then(data => {
layer.close(loading);
}, error => {
layer.close(loading);
layer.alert(error, {icon: 2});
$("#file").val('');
throw Error();
});
var res = { url: fileurl, name: file.name };
this.set.output.push(res)
},
async pasteFile(file) {
if(file.size > 104857600){
layer.alert('文件大小限制100M');return;
}
this.progress = 0;
var url,postdata,fileid,fileurl;
var loading = layer.msg('正在准备文件上传', {icon: 16,shade: 0.3,time: 0});
await this.preUpload(file.name).then(data => {
fileid = data.id;
postdata = {'Cache-Control':'max-age=2592000', 'Content-Disposition':'attachment', 'OSSAccessKeyId':data.accessKeyId, 'Signature':data.signature, 'host':data.host, 'id':data.id, 'key':data.ossPath, 'policy':data.policy, 'success_action_status':'200'};
url = 'https://' + data.host + '/';
fileurl = 'https://' + data.cdnDomain + '/' + data.ossPath;
}, error => {
layer.close(loading);
layer.alert(error, {icon: 2});
$("#file").val('');
throw Error();
});
loading = layer.msg('正在上传文件,请稍候...', {icon: 16,shade: 0.3,time: 0});
await this.uploadFile(url, postdata, file).then(() => {
}, error => {
layer.close(loading);
layer.alert(error, {icon: 2});
$("#file").val('');
throw Error();
});
loading = layer.msg('上传成功,正在保存', {icon: 16,shade: 0.3,time: 0});
await this.completeUpload(fileid).then(data => {
layer.close(loading);
}, error => {
layer.close(loading);
layer.alert(error, {icon: 2});
$("#file").val('');
throw Error();
});
var res = { url: fileurl, name: file.name };
this.set.output.push(res)
},
copy(){
if(!this.result) return;
$("#output").select();
document.execCommand("Copy");
layer.msg('复制成功', {icon:1, time:600})
},
reset(){
this.set.output = [];
this.progress = 0;
}
},
})
</script>
{/block}
+76 -1
View File
@@ -28,7 +28,7 @@ class App extends Plugin
try{ try{
if($type == 'wx'){ if($type == 'wx'){
$msg = $this->query_wx($link); $msg = $this->query_wx2($link);
}else{ }else{
$msg = $this->query_qq($link); $msg = $this->query_qq($link);
} }
@@ -39,6 +39,21 @@ class App extends Plugin
return msg('ok','success',$msg); return msg('ok','success',$msg);
} }
public function queryqq(){
$link = input('post.url', null, 'trim');
$randstr = input('post.randstr', null, 'trim');
$ticket = input('post.ticket', null, 'trim');
if(!$link) return msg('error','no url');
try{
$msg = $this->query_qq_new($link, $ticket, $randstr);
}catch(Exception $e){
return msg('error', $e->getMessage());
}
return msg('ok','success',$msg);
}
private function query_qq_old($link){ private function query_qq_old($link){
$url = 'https://cgi.urlsec.qq.com/index.php?m=gwComplainMergeIntoWechat&a=checkBlackStatus&callback=url_query&url='.urlencode($link); $url = 'https://cgi.urlsec.qq.com/index.php?m=gwComplainMergeIntoWechat&a=checkBlackStatus&callback=url_query&url='.urlencode($link);
$data=$this->guanjia_curl($url); $data=$this->guanjia_curl($url);
@@ -97,7 +112,53 @@ class App extends Plugin
} }
}else{ }else{
$msg['检测URL'] = $link;
$msg['查询失败'] = $arr['data'];
}
return $msg;
}
private function query_qq_new($link, $ticket, $randstr){
$url='https://cgi.urlsec.qq.com/index.php?m=check&a=gw_check&callback=url_query&url='.urlencode($link).'&ticket='.$ticket.'&randstr='.urlencode($randstr).'&_='.time().'123';
$data=$this->guanjia_curl($url);
$arr = jsonp_decode($data, true);
if(!$arr) throw new Exception('查询接口返回数据解析失败');
if(isset($arr['reCode']) && $arr['reCode']==0) {
$arr = $arr['data']['results'];
//print_r($arr);
$msg['检测URL'] = $arr['url']; $msg['检测URL'] = $arr['url'];
if($arr['whitetype']==3||$arr['whitetype']==4){
$msg['域名状态'] = '<font color="green">白名单</font>';
}elseif($arr['whitetype']==2){
$msg['域名状态'] = '<font color="red">已拦截</font>';
$msg['拦截原因'] = $arr['WordingTitle'];
$msg['拦截详情'] = $arr['Wording'];
}elseif($arr['whitetype']==1){
if($arr['eviltype']!=0){
if($arr['eviltype']==2800 || $arr['eviltype']==2804)
$msg['域名状态'] = '<font color="orange">QQ内拦截</font>';
else
$msg['域名状态'] = '<font color="orange">其他拦截('.$arr['eviltype'].')</font>';
}else{
$msg['域名状态'] = '<font color="green">未拦截</font><br/>';
}
$msg['安全联盟认证'] = ($arr['certify']==1?'是':'否');
}
if($arr['detect_time']!=0){
$msg['记录时间'] = date("Y-m-d H:i:s", $arr['detect_time']);
}
if($arr['isDomainICPOk']==1){
$msg['是否已备案'] = '是';
$msg['备案主体'] = $arr['Orgnization'];
$msg['备案号'] = $arr['ICPSerial'];
}else{
$msg['是否已备案'] = '否';
}
}else{
$msg['检测URL'] = $link;
$msg['查询失败'] = $arr['data']; $msg['查询失败'] = $arr['data'];
} }
@@ -129,6 +190,20 @@ class App extends Plugin
return $msg; return $msg;
} }
private function query_wx2($link){
$url = 'https://cgi.urlsec.qq.com/index.php?m=url&a=validUrl&url='.urlencode($link);
$data = get_curl($url);
$arr = json_decode($data, true);
if(!$arr) throw new Exception('查询接口返回数据解析失败');
$msg['检测URL'] = $link;
if(isset($arr['data']) && $arr['data']=='ok'){
$msg['微信拦截状态'] = '<font color="red">已拦截</font>';
}else{
$msg['微信拦截状态'] = '<font color="green">未拦截</font>';
}
return $msg;
}
private function guanjia_curl($url) private function guanjia_curl($url)
{ {
$ch = curl_init(); $ch = curl_init();
+41 -2
View File
@@ -16,7 +16,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<button class="btn btn-dim btn-outline-info btn-block btn-lg card-link" @click="query('qq')" :disabled="query_disabled"> <button class="btn btn-dim btn-outline-info btn-block btn-lg card-link" @click="queryqq()" :disabled="query_disabled">
查询管家&QQ拦截 查询管家&QQ拦截
</button> </button>
</div> </div>
@@ -44,6 +44,7 @@
{block name="script"} {block name="script"}
<script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script> <script src="{$cdn_cdnjs}vue/2.6.14/vue.min.js"></script>
<script src="https://static.geetest.com/v4/gt4.js"></script> <script src="https://static.geetest.com/v4/gt4.js"></script>
<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
<script> <script>
new Vue({ new Vue({
el: '#app', el: '#app',
@@ -53,7 +54,8 @@ new Vue({
type: '', type: '',
showresult: false, showresult: false,
result_info: [], result_info: [],
captcha: null captcha: null,
captcha1: null,
}, },
mounted() { mounted() {
this.$refs.input.focus(); this.$refs.input.focus();
@@ -101,6 +103,36 @@ new Vue({
alert('验证码加载失败,请刷新页面重试'); alert('验证码加载失败,请刷新页面重试');
}) })
}); });
var tcaptchaCallback = function(res){
if (res.ret === 0) {
layer.load(0, {shade:0.1});
var data = { url: that.input, ticket: res.ticket, randstr: res.randstr};
$.ajax({
url: '/api/{$plugin.alias}/queryqq',
type: 'post',
dataType: 'json',
data: data,
cache: false,
success: function (data) {
layer.closeAll();
if(data.status=='ok'){
that.showresult = true;
that.result_info = data.data;
}else{
layer.alert(data.message, {icon: 5});
}
},
error: function () {
layer.closeAll();
layer.msg('服务器错误', {icon: 5});
}
});
}else{
layer.msg('请先完成验证',{time:800,icon:2});
}
}
this.captcha1 = new TencentCaptcha('2046626881', tcaptchaCallback);
}, },
methods: { methods: {
checkURL() checkURL()
@@ -123,6 +155,13 @@ new Vue({
this.type = type; this.type = type;
this.captcha.showCaptcha(); this.captcha.showCaptcha();
}, },
queryqq(){
if(this.input.trim() == ''){
layer.alert('要查询的网址不能为空');return;
}
this.checkURL();
this.captcha1.show();
}
}, },
}) })
</script> </script>
+28 -101
View File
@@ -22,7 +22,7 @@ class App extends Plugin
public function query(){ public function query(){
$domain = input('post.domain', null, 'trim'); $domain = input('post.domain', null, 'trim');
if(!$domain) return msg('error','no domain'); if(!$domain) return msg('error','no domain');
if(strpos($domain,'.') && !checkdomain($domain)){ if(!checkdomain($domain)){
return msg('error', '域名格式不正确!'); return msg('error', '域名格式不正确!');
} }
@@ -31,121 +31,48 @@ class App extends Plugin
return msg('error', '验证失败,请重新验证'); return msg('error', '验证失败,请重新验证');
} }
$cache = Db::name('querycache')->where('type', 'icplist')->where('key|subkey', $domain)->find(); $cache = Db::name('querycache')->where('type', 'icp')->where('key', $domain)->find();
if($cache && time() - strtotime($cache['uptime']) <= self::CACHE_TIME){ if($cache && time() - strtotime($cache['uptime']) <= self::CACHE_TIME){
$array = json_decode($cache['content'], true); $array = json_decode($cache['content'], true);
$data = Db::name('querycache')->where('type', 'icpitem')->whereIn('id', implode(',',$array['list']))->select(); return msg('ok','success',$array);
$list = [];
foreach($data as $row){
$list[] = json_decode($row['content'], true);
}
return msg('ok','success',['total'=>$array['total'], 'list'=>$list]);
}
$cache = Db::name('querycache')->where('type', 'icpitem')->where('key|subkey', $domain)->find();
if($cache && time() - strtotime($cache['uptime']) <= self::CACHE_TIME){
$array = json_decode($cache['content'], true);
return msg('ok','success',['total'=>1, 'list'=>[$array]]);
} }
try{ try{
$result = $this->execapi($domain); $result = $this->queryapi($domain);
if(!$result){
return msg('ok','success',null);
}
}catch(Exception $e){ }catch(Exception $e){
return msg('error', $e->getMessage()); return msg('error', $e->getMessage());
} }
if($result['total'] > 1 && count($result['data']) > 1){ Db::name('querycache')->duplicate([
$i = 0; 'type' => 'icp',
foreach($result['data'] as $row){ 'key' => $result['Domain'],
$id = Db::name('querycache')->duplicate([ 'content' => json_encode($result),
'subkey' => $row['webLicence'], 'uptime' => date('Y-m-d H:i:s')
'content' => json_encode($row), ])->insert([
'uptime' => date('Y-m-d H:i:s') 'type' => 'icp',
])->insertGetId([ 'key' => $result['Domain'],
'type' => 'icpitem', 'content' => json_encode($result),
'key' => $row['domain'], 'uptime' => date('Y-m-d H:i:s')
'subkey' => $row['webLicence'], ]);
'content' => json_encode($row),
'uptime' => date('Y-m-d H:i:s')
]);
$result['data'][$i++]['id'] = $id;
$ids[] = $id;
}
Db::name('querycache')->duplicate([
'subkey' => $result['data'][0]['mainLicence'],
'content' => json_encode(['total'=>$result['total'], 'list'=>$ids]),
'uptime' => date('Y-m-d H:i:s')
])->insert([
'type' => 'icplist',
'key' => $result['data'][0]['unitName'],
'subkey' => $result['data'][0]['mainLicence'],
'content' => json_encode(['total'=>$result['total'], 'list'=>$ids]),
'uptime' => date('Y-m-d H:i:s')
]);
}elseif($result['total'] == 1 && count($result['data']) > 0){
$id = Db::name('querycache')->duplicate([
'subkey' => $result['data'][0]['webLicence'],
'content' => json_encode($result['data'][0]),
'uptime' => date('Y-m-d H:i:s')
])->insertGetId([
'type' => 'icpitem',
'key' => $result['data'][0]['domain'],
'subkey' => $result['data'][0]['webLicence'],
'content' => json_encode($result['data'][0]),
'uptime' => date('Y-m-d H:i:s')
]);
$result['data'][0]['id'] = $id;
}
return msg('ok','success',['total'=>$result['total'], 'list'=>$result['data']]); return msg('ok','success',$result);
} }
public function item(){ private function queryapi($domain){
$id = input('post.id'); $url = config_get('qqapi_url').'api.php?act=icpquery';
if(!$id) return msg('error','no id'); $post = 'key='.config_get('qqapi_key').'&domain='.$domain;
$cache = Db::name('querycache')->where('id', $id)->find(); $data = get_curl($url, $post);
if($cache){ $arr = json_decode($data, true);
$array = json_decode($cache['content'], true); if(isset($arr['code']) && $arr['code']==0){
return msg('ok','success',['total'=>1, 'list'=>[$array]]); return $arr['data'];
}else{
return msg('ok','success',['total'=>0, 'list'=>[]]);
}
}
private function execapi($domain){
$timeStamp = time();
$authKey = md5("testtest" . $timeStamp);
$referer = 'https://beian.miit.gov.cn/';
$headers = ['Origin: https://beian.miit.gov.cn'];
$url = 'https://hlwicpfwc.miit.gov.cn/icpproject_query/api/auth';
$post = 'authKey='.$authKey.'&timeStamp='.$timeStamp;
$response = get_curl($url, $post, $referer, 0, 0, 0, 0, $headers);
$arr = json_decode($response, true);
if(isset($arr['code']) && $arr['code']==200){
$token = $arr['params']['bussiness'];
$url = 'https://hlwicpfwc.miit.gov.cn/icpproject_query/api/icpAbbreviateInfo/queryByCondition';
$post = json_encode(['pageNum'=>'','pageSize'=>'','unitName'=>$domain,'serviceType'=>1]);
$headers[] = 'Content-Type: application/json; charset=UTF-8';
$headers[] = 'token: '.$token;
$response = get_curl($url, $post, $referer, 0, 0, 0, 0, $headers);
$arr = json_decode($response, true);
if(isset($arr['code']) && $arr['code']==200){
$list = [];
foreach($arr['params']['list'] as $row){
$list[] = ['domain'=>$row['domain'], 'mainLicence'=>$row['mainLicence'], 'webLicence'=>$row['serviceLicence'], 'unitName'=>$row['unitName'], 'unitType'=>$row['natureName'], 'updateTime'=>$row['updateRecordTime'], 'limitAccess'=>$row['limitAccess'], 'contentTypeName'=>$row['contentTypeName']];
}
return ['code'=>0, 'total'=>$arr['params']['total'], 'data'=>$list];
}elseif(isset($arr['msg'])){
throw new Exception($arr['msg']);
}else{
throw new Exception('查询接口(query)请求失败');
}
}elseif(isset($arr['msg'])){ }elseif(isset($arr['msg'])){
throw new Exception($arr['msg']); throw new Exception($arr['msg']);
}else{ }else{
throw new Exception('查询接口(auth)请求失败'); throw new Exception('接口请求失败');
} }
} }
} }
+23 -71
View File
@@ -2,22 +2,19 @@
{block name="title"}{$plugin.title} - {:config_get('title')}{/block} {block name="title"}{$plugin.title} - {:config_get('title')}{/block}
{block name="main"} {block name="main"}
<style> <style>
.query-title { td{text-align: center;}
text-align: right;
}
.table-title th{word-break: keep-all;}
</style> </style>
<div class="container-xl" id="app"> <div class="container-xl" id="app">
<div class="col-md-12 col-xl-10 center-block"> <div class="col-sm-12 col-md-10 col-xl-8 center-block">
<div class="card card-preview"> <div class="card card-preview">
<div class="card-inner mt-3"> <div class="card-inner mt-3">
<div class="nya-title nk-ibx-action-item progress-rating"> <div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">ICP备案查询</span> <span class="nk-menu-text font-weight-bold">ICP备案查询</span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">输入域名/备案号/单位名称</label> <label class="form-label">输入域名:</label>
<div class="form-control-wrap"> <div class="form-control-wrap">
<input type="text" v-model="input" placeholder="请输入域名或备案号或单位名称查询,请勿使用子域名或者带http://www等字符的网址查询" class="form-control form-control-lg" @keyup.enter="query" ref="input" autocomplete="off"> <input type="text" v-model="input" placeholder="请输入域名查询,请勿使用子域名或者带http://www等字符的网址查询" class="form-control form-control-lg" @keyup.enter="query" ref="input" autocomplete="off">
</div> </div>
</div> </div>
<button class="btn btn-dim btn-outline-primary btn-block card-link mb-3" @click="query" :disabled="query_disabled"> <button class="btn btn-dim btn-outline-primary btn-block card-link mb-3" @click="query" :disabled="query_disabled">
@@ -30,48 +27,20 @@
<div class="nya-title nk-ibx-action-item progress-rating"> <div class="nya-title nk-ibx-action-item progress-rating">
<span class="nk-menu-text font-weight-bold">查询结果</span> <span class="nk-menu-text font-weight-bold">查询结果</span>
</div> </div>
<div class="alert alert-warning text-center" v-if="result_total==0"><h6><em class="icon ni ni-info"></em> 没有查询到备案记录</h6></div> <div class="alert alert-warning text-center" v-if="result_code==0"><h6><em class="icon ni ni-info"></em> 没有查询到备案信息</h6></div>
<div v-if="result_total==1"> <div class="col-sm-12 col-md-10 col-xl-8 center-block" v-if="result_code==1">
<h6>域名 <span class="text-primary">{{result_info.domain}}</span> 的信息:</h6>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover table-bordered"> <table class="table table-hover table-bordered">
<tbody> <tbody>
<tr><td class="query-title">网站域名</td><td>{{result_info.domain}}</td></tr> <tr><td class="query-title">域名</td><td>{{result_info.Domain}}</td></tr>
<tr><td class="query-title">ICP备案/许可证</td><td>{{result_info.webLicence}}</td></tr> <tr><td class="query-title">备案</td><td>{{result_info.DomainIcpNum}}</td></tr>
<tr><td class="query-title">主办单位名称</td><td>{{result_info.unitName}}</td></tr> <tr><td class="query-title">主办单位名称</td><td>{{result_info.CompanyName}}</td></tr>
<tr><td class="query-title">主办单位性质</td><td>{{result_info.unitType}}</td></tr> <tr><td class="query-title">主办单位性质</td><td>{{result_info.CompanyType}}</td></tr>
<tr><td class="query-title">审核日期</td><td>{{result_info.updateTime}}</td></tr> <tr><td class="query-title">审核日期</td><td>{{result_info.AuditTime}}</td></tr>
<tr><td class="query-title">是否限制接入</td><td>{{result_info.limitAccess}}</td></tr> </tbody>
<tr><td class="query-title">网站前置审批项</td><td>{{result_info.contentTypeName}}</td></tr> </table>
</tbody>
</table>
</div> </div>
</div> </div>
<div v-if="result_total>1">
<h6><span class="text-primary">{{result_input}}</span> 共查询到 <span class="text-primary">{{result_total}}</span> 条备案信息:</h6>
<div class="table-responsive">
<table class="table table-hover table-bordered">
<thead class="table-title">
<th>网站域名</th><th>网站备案号</th><th>主办单位名称</th><th>审核日期</th><th>操作</th>
</thead>
<tbody>
<tr v-for="(item,index) in result_list" :key="index">
<td>{{item.domain}}</td><td>{{item.webLicence}}</td><td>{{item.unitName}}</td><td>{{item.updateTime}}</td><td><button class="btn btn-dim btn-outline-info btn-xs" @click="show_item(index)">详情</button></td>
</tr>
</tbody>
</table>
</div>
<p v-if="result_total>10" class="text-info">当前只支持查询最新10条记录,剩余记录请使用域名或网站备案号进行精确查询。</p>
</div>
</div>
</div>
<div class="card card-preview">
<div class="card-inner">
<h6><em class="icon ni ni-info"></em> 简介</h6>
<div class="accordion-inner">
<p>支持输入域名、网站备案号、主体备案号、单位名称(个人姓名、企业名称)进行查询</p>
<p>此ICP查询工具直接对接工信部官网,非第三方接口。<a href="https://blog.cccyun.cn/post-445.html" target="_blank">查询源码下载</a>,请勿对本站进行恶意抓取</p>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -86,21 +55,9 @@ new Vue({
data: { data: {
query_disabled: true, query_disabled: true,
input: '', input: '',
result_input: '',
showresult: false, showresult: false,
result_info: { result_info: [],
id: '', result_code: 0,
domain: '',
mainLicence: '',
webLicence: '',
unitName: '',
unitType: '',
updateTime: '',
limitAccess: '',
contentTypeName: '',
},
result_list: [],
result_total: 0,
captcha: null captcha: null
}, },
mounted() { mounted() {
@@ -136,13 +93,12 @@ new Vue({
success: function (data) { success: function (data) {
layer.closeAll(); layer.closeAll();
if(data.status=='ok'){ if(data.status=='ok'){
var data = data.data;
that.result_input = that.input;
that.showresult = true; that.showresult = true;
that.result_total = data.total; if(data.data == null){
that.result_list = data.list; that.result_code = 0;
if(data.list.length > 0){ }else{
that.result_info = data.list[0]; that.result_code = 1;
that.result_info = data.data;
} }
captcha.reset(); captcha.reset();
}else{ }else{
@@ -184,15 +140,11 @@ new Vue({
}, },
query() { query() {
this.checkURL(); this.checkURL();
if(this.input == ''){ if(this.input.trim() == ''){
alert('查询内容不能为空');return; alert('查询内容不能为空');return;
} }
layer.load(0, {shade:0.1}); layer.load(0, {shade:0.1});
this.captcha.showCaptcha(); this.captcha.showCaptcha();
},
show_item(index){
this.result_info = this.result_list[index];
this.result_total = 1;
} }
}, },
}) })
-12
View File
@@ -1,12 +0,0 @@
yum -y install python3
yum -y install mesa-libGL
python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install opencv-python==4.3.0.38
pip3 install requests
pip3 install flask
pip3 install gevent
nohup python3 ./server.py >> ./server.log 2>&1 &
-165
View File
@@ -1,165 +0,0 @@
# -*- coding: utf-8 -*-
import requests,hashlib,time,base64,cv2,os
def icpquery(info):
if(info == None or len(info) == 0):
return {'code':-1,'msg':'no domain'}
info_data = {
'pageNum':'',
'pageSize':'',
'unitName':info,
'serviceType':1
}
#构造AuthKey
timeStamp = int(round(time.time()*1000))
authSecret = 'testtest' + str(timeStamp)
authKey = hashlib.md5(authSecret.encode(encoding='UTF-8')).hexdigest()
#获取Cookie
cookie_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36 Edg/90.0.818.42'
}
cookie = requests.utils.dict_from_cookiejar(requests.get('https://beian.miit.gov.cn/',headers=cookie_headers,verify=False).cookies)['__jsluid_s']
#请求获取Token
t_url = 'https://hlwicpfwc.miit.gov.cn/icpproject_query/api/auth'
t_headers = {
'Host': 'hlwicpfwc.miit.gov.cn',
'Connection': 'keep-alive',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="90", "Microsoft Edge";v="90"',
'Accept': '*/*',
'DNT': '1',
'sec-ch-ua-mobile': '?0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46',
'Origin': 'https://beian.miit.gov.cn',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://beian.miit.gov.cn/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cookie': '__jsluid_s=' + cookie
}
data = {
'authKey': authKey,
'timeStamp': timeStamp
}
t_response = requests.post(t_url,data=data,headers=t_headers,verify=False)
try:
get_token = t_response.json()['params']['bussiness']
except:
return {'code':-1,'msg':'请求被禁止,请稍后或更换头部与IP后再试('+t_response.status_code+')'}
#获取验证图像、UUID
p_url = 'https://hlwicpfwc.miit.gov.cn/icpproject_query/api/image/getCheckImage'
p_headers = {
'Host': 'hlwicpfwc.miit.gov.cn',
'Connection': 'keep-alive',
'Content-Length': '0',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="90", "Microsoft Edge";v="90"',
'Accept': 'application/json, text/plain, */*',
'DNT': '1',
'sec-ch-ua-mobile': '?0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46',
'token': get_token,
'Origin': 'https://beian.miit.gov.cn',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://beian.miit.gov.cn/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cookie': '__jsluid_s=' + cookie
}
p_request = requests.post(p_url,data='',headers=p_headers,verify=False)
try:
p_uuid = p_request.json()['params']['uuid']
big_image = p_request.json()['params']['bigImage']
small_image = p_request.json()['params']['smallImage']
except KeyError:
return {'code':-1,'msg':'获取验证图像失败,请重试('+p_request.status_code+')'}
#解码图片,写入并计算图片缺口位置
with open('bigImage.jpg','wb') as f:
f.write(base64.b64decode(big_image))
f.close()
with open('smallImage.jpg','wb') as f:
f.write(base64.b64decode(small_image))
f.close()
background_image = cv2.imread('bigImage.jpg',cv2.COLOR_GRAY2RGB)
fill_image = cv2.imread('smallImage.jpg',cv2.COLOR_GRAY2RGB)
background_image_canny = cv2.Canny(background_image, 100, 200)
fill_image_canny = cv2.Canny(fill_image, 100, 300)
position_match = cv2.matchTemplate(background_image, fill_image, cv2.TM_CCOEFF_NORMED)
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(position_match)
position = max_loc
mouse_length = position[0]+1
os.remove('bigImage.jpg')
os.remove('smallImage.jpg')
#通过拼图验证,获取sign
check_url = 'https://hlwicpfwc.miit.gov.cn/icpproject_query/api/image/checkImage'
check_headers = {
'Host': 'hlwicpfwc.miit.gov.cn',
'Accept': 'application/json, text/plain, */*',
'Connection': 'keep-alive',
'Content-Length': '60',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="90", "Microsoft Edge";v="90"',
'DNT': '1',
'sec-ch-ua-mobile': '?0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36 Edg/90.0.818.42',
'token': get_token,
'Content-Type': 'application/json',
'Origin': 'https://beian.miit.gov.cn',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://beian.miit.gov.cn/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cookie': '__jsluid_s=' + cookie
}
check_data = {
'key':p_uuid,
'value':mouse_length
}
check_request = requests.post(check_url,json=check_data,headers=check_headers,verify=False)
try:
sign = check_request.json()['params']
except Exception:
return {'code':-1,'msg':'校验图片信息失败,请重试('+check_request.status_code+')'}
#获取备案信息
info_url = 'https://hlwicpfwc.miit.gov.cn/icpproject_query/api/icpAbbreviateInfo/queryByCondition'
info_headers = {
'Host': 'hlwicpfwc.miit.gov.cn',
'Connection': 'keep-alive',
'Content-Length': '78',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="90", "Microsoft Edge";v="90"',
'DNT': '1',
'sec-ch-ua-mobile': '?0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36 Edg/90.0.818.42',
'Content-Type': 'application/json',
'Accept': 'application/json, text/plain, */*',
'uuid': p_uuid,
'token': get_token,
'sign': sign,
'Origin': 'https://beian.miit.gov.cn',
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://beian.miit.gov.cn/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cookie': '__jsluid_s=' + cookie
}
info_request = requests.post(info_url,json=info_data,headers=info_headers,verify=False)
domain_total = info_request.json()['params']['total']
result_list = []
for info_base in info_request.json()['params']['list']:
result_list.append({'domain':info_base['domain'], 'mainLicence':info_base['mainLicence'], 'webLicence':info_base['serviceLicence'], 'unitName':info_base['unitName'], 'unitType':info_base['natureName'], 'updateTime':info_base['updateRecordTime'], 'limitAccess':info_base['limitAccess'], 'contentTypeName':info_base['contentTypeName']})
return {'code':0,'msg':'success','data':result_list, 'total':domain_total}
-34
View File
@@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
import icp,json,flask
HOST='127.0.0.1'
PORT=9088
app = flask.Flask(__name__)
json_header = {'Content-Type':'application/json; charset=utf-8'}
@app.route('/',methods=['GET'])
def home():
domain = flask.request.args.get('domain')
result = icp.icpquery(domain)
return flask.Response(json.dumps(result),headers=json_header)
@app.errorhandler(404)
def notfound(e):
errorStr = '''<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>server</center>
</body>
</html>'''
headers = {
"Content-Type":"text/html"
}
return flask.Response(errorStr,status=404,headers=headers)
if __name__ == '__main__':
from gevent.pywsgi import WSGIServer
http_server = WSGIServer((HOST, PORT), app)
http_server.serve_forever()
#app.run(port=PORT,host=HOST)
+2 -3
View File
@@ -49,9 +49,8 @@ class App extends Plugin
} }
private function queryapi($uin){ private function queryapi($uin){
//此处需填写QQAPI的接口地址(https://github.com/netcccyun/qqapi $url = config_get('qqapi_url').'api.php?act=getqqlevel';
$url = 'http://qqapiurl/api.php?act=getqqlevel'; $post = 'key='.config_get('qqapi_key').'&uin='.$uin;
$post = 'key=查询密钥&uin='.$uin;
$data = get_curl($url, $post); $data = get_curl($url, $post);
$arr = json_decode($data, true); $arr = json_decode($data, true);
if(isset($arr['code']) && $arr['code']==0){ if(isset($arr['code']) && $arr['code']==0){
+1 -1
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit"/> <meta name="renderer" content="webkit"/>
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700,800"> <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700,800">
<link rel="stylesheet" href="//cdn.staticfile.org/normalize/5.0.0/normalize.min.css"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/normalize/5.0.0/normalize.min.css">
<style> <style>
+5 -5
View File
@@ -10,13 +10,13 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="css/layuimini.css?v=2.0.4.2" media="all"> <link rel="stylesheet" href="css/layuimini.css?v=2.0.4.2" media="all">
<link rel="stylesheet" href="css/themes/default.css" media="all"> <link rel="stylesheet" href="css/themes/default.css" media="all">
<link rel="stylesheet" href="//cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/font-awesome/4.7.0/css/font-awesome.min.css" media="all">
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="//cdn.staticfile.org/html5shiv/r29/html5.min.js"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/html5shiv/r29/html5.min.js"></script>
<script src="//cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
<style id="layuimini-bg-color"> <style id="layuimini-bg-color">
</style> </style>
@@ -123,7 +123,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="js/lay-config.js?v=2.0.0" charset="utf-8"></script> <script src="js/lay-config.js?v=2.0.0" charset="utf-8"></script>
<script src="js/common.js" charset="utf-8"></script> <script src="js/common.js" charset="utf-8"></script>
<script> <script>
+5 -5
View File
@@ -9,10 +9,10 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="//cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
<style> <style>
html, body {width: 100%;height: 100%;overflow: hidden} html, body {width: 100%;height: 100%;overflow: hidden}
@@ -61,8 +61,8 @@
</div> </div>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/jquery/3.6.0/jquery.min.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js" charset="utf-8"></script>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="./lib/jq-module/jquery.particleground.min.js" charset="utf-8"></script> <script src="./lib/jq-module/jquery.particleground.min.js" charset="utf-8"></script>
<script> <script>
layui.use(['form'], function () { layui.use(['form'], function () {
+2 -2
View File
@@ -9,7 +9,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<style> <style>
.error .clip .shadow {height:180px;} .error .clip .shadow {height:180px;}
.error .clip:nth-of-type(2) .shadow {width:130px;} .error .clip:nth-of-type(2) .shadow {width:130px;}
@@ -71,7 +71,7 @@
</div> </div>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script> <script>
function randomNum() { function randomNum() {
return Math.floor(Math.random() * 9) + 1; return Math.floor(Math.random() * 9) + 1;
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<style> <style>
body { body {
@@ -62,7 +62,7 @@
<option value="{{item}}">{{item}}</option> <option value="{{item}}">{{item}}</option>
{{# }}} {{# }}}
</script> </script>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
</head> </head>
@@ -54,7 +54,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
layui.use(['form', 'table'], function () { layui.use(['form', 'table'], function () {
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -55,7 +55,7 @@
</div> </div>
</div> </div>
</form> </form>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -62,7 +62,7 @@
</div> </div>
</div> </div>
</form> </form>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
</head> </head>
@@ -63,7 +63,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/lay-config.js?v=2.0.0" charset="utf-8"></script> <script src="../js/lay-config.js?v=2.0.0" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -62,7 +62,7 @@
</div> </div>
</div> </div>
</form> </form>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../lib/font-awesome-4.7.0/css/font-awesome.min.css" media="all"> <link rel="stylesheet" href="../lib/font-awesome-4.7.0/css/font-awesome.min.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<style> <style>
@@ -186,7 +186,7 @@
</script> </script>
<!--</div>--> <!--</div>-->
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/lay-config.js?v=1.0.4" charset="utf-8"></script> <script src="../js/lay-config.js?v=1.0.4" charset="utf-8"></script>
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
</head> </head>
@@ -55,7 +55,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
layui.use(['form', 'table'], function () { layui.use(['form', 'table'], function () {
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -53,7 +53,7 @@
</div> </div>
</div> </div>
</form> </form>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -60,7 +60,7 @@
</div> </div>
</div> </div>
</form> </form>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
<style> <style>
@@ -90,7 +90,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -117,7 +117,7 @@
{{# }); }} {{# }); }}
</select> </select>
</script> </script>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<style> <style>
body { body {
@@ -124,7 +124,7 @@
{{# }); }} {{# }); }}
</select> </select>
</script> </script>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/api.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/api.js?v=1.0.0" charset="utf-8"></script>
<script src="../../js/common.js?v=1.0.0" charset="utf-8"></script> <script src="../../js/common.js?v=1.0.0" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../../css/public.css" media="all"> <link rel="stylesheet" href="../../css/public.css" media="all">
<link rel="stylesheet" href="../../js/lay-module/step-lay/step.css" media="all"> <link rel="stylesheet" href="../../js/lay-module/step-lay/step.css" media="all">
</head> </head>
@@ -123,7 +123,7 @@
{{# }); }} {{# }); }}
</select> </select>
</script> </script>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../../js/lay-config.js?v=1.0.4" charset="utf-8"></script> <script src="../../js/lay-config.js?v=1.0.4" charset="utf-8"></script>
<script src="../../js/api.js" charset="utf-8"></script> <script src="../../js/api.js" charset="utf-8"></script>
<script src="../../js/common.js" charset="utf-8"></script> <script src="../../js/common.js" charset="utf-8"></script>
+15 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<style> <style>
body { body {
@@ -125,6 +125,19 @@
<input type="text" name="captcha_key" placeholder="请输入极验KEY" class="layui-input"> <input type="text" name="captcha_key" placeholder="请输入极验KEY" class="layui-input">
</div> </div>
</div> </div>
<blockquote class="layui-elem-quote"><a href="https://github.com/netcccyun/qqapi" target="_blank">QQ-API</a>接口配置(ICP备案查询、QQ等级查询等工具使用)</blockquote>
<div class="layui-form-item">
<label class="layui-form-label">接口地址:</label>
<div class="layui-input-block">
<input type="text" name="qqapi_url" placeholder="必须以http://或https://开头,以/结尾" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">接口密钥:</label>
<div class="layui-input-block">
<input type="text" name="qqapi_key" placeholder="请输入API接口密钥" class="layui-input">
</div>
</div>
<blockquote class="layui-elem-quote">CDN地址配置 <blockquote class="layui-elem-quote">CDN地址配置
</blockquote> </blockquote>
<div class="layui-form-item"> <div class="layui-form-item">
@@ -159,7 +172,7 @@
<option value="{{item}}">{{item}}</option> <option value="{{item}}">{{item}}</option>
{{# }}} {{# }}}
</script> </script>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<style> <style>
body { body {
@@ -63,7 +63,7 @@
</div> </div>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
</head> </head>
@@ -70,7 +70,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/lay-config.js?v=2.0.0" charset="utf-8"></script> <script src="../js/lay-config.js?v=2.0.0" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="//cdn.staticfile.org/layui/2.6.3/css/layui.css" media="all"> <link rel="stylesheet" href="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all"> <link rel="stylesheet" href="../css/public.css" media="all">
<script src="../js/common.js" charset="utf-8"></script> <script src="../js/common.js" charset="utf-8"></script>
</head> </head>
@@ -63,7 +63,7 @@
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layui/2.6.3/layui.js" charset="utf-8"></script> <script src="//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/layui/2.6.3/layui.js" charset="utf-8"></script>
<script src="../js/api.js" charset="utf-8"></script> <script src="../js/api.js" charset="utf-8"></script>
<script> <script>
+1
View File
@@ -0,0 +1 @@
!function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.sm2=r():t.sm2=r()}("undefined"!=typeof self?self:this,function(){return function(t){function r(e){if(i[e])return i[e].exports;var n=i[e]={i:e,l:!1,exports:{}};return t[e].call(n.exports,n,n.exports,r),n.l=!0,n.exports}var i={};return r.m=t,r.c=i,r.d=function(t,i,e){r.o(t,i)||Object.defineProperty(t,i,{configurable:!1,enumerable:!0,get:e})},r.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(i,"a",i),i},r.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},r.p="",r(r.s=2)}([function(t,r,i){(function(){function i(t,r,i){null!=t&&("number"==typeof t?this.fromNumber(t,r,i):null==r&&"string"!=typeof t?this.fromString(t,256):this.fromString(t,r))}function e(){return new i(null)}function n(t,r,i,e,n,o){for(;--o>=0;){var s=r*this[t++]+i[e]+n;n=Math.floor(s/67108864),i[e++]=67108863&s}return n}function o(t,r,i,e,n,o){for(var s=32767&r,u=r>>15;--o>=0;){var h=32767&this[t],a=this[t++]>>15,f=u*h+a*s;h=s*h+((32767&f)<<15)+i[e]+(1073741823&n),n=(h>>>30)+(f>>>15)+u*a+(n>>>30),i[e++]=1073741823&h}return n}function s(t,r,i,e,n,o){for(var s=16383&r,u=r>>14;--o>=0;){var h=16383&this[t],a=this[t++]>>14,f=u*h+a*s;h=s*h+((16383&f)<<14)+i[e]+n,n=(h>>28)+(f>>14)+u*a,i[e++]=268435455&h}return n}function u(t){return pr.charAt(t)}function h(t,r){var i=vr[t.charCodeAt(r)];return null==i?-1:i}function a(t){for(var r=this.t-1;r>=0;--r)t[r]=this[r];t.t=this.t,t.s=this.s}function f(t){this.t=1,this.s=t<0?-1:0,t>0?this[0]=t:t<-1?this[0]=t+this.DV:this.t=0}function l(t){var r=e();return r.fromInt(t),r}function c(t,r){var e;if(16==r)e=4;else if(8==r)e=3;else if(256==r)e=8;else if(2==r)e=1;else if(32==r)e=5;else{if(4!=r)return void this.fromRadix(t,r);e=2}this.t=0,this.s=0;for(var n=t.length,o=!1,s=0;--n>=0;){var u=8==e?255&t[n]:h(t,n);u<0?"-"==t.charAt(n)&&(o=!0):(o=!1,0==s?this[this.t++]=u:s+e>this.DB?(this[this.t-1]|=(u&(1<<this.DB-s)-1)<<s,this[this.t++]=u>>this.DB-s):this[this.t-1]|=u<<s,(s+=e)>=this.DB&&(s-=this.DB))}8==e&&0!=(128&t[0])&&(this.s=-1,s>0&&(this[this.t-1]|=(1<<this.DB-s)-1<<s)),this.clamp(),o&&i.ZERO.subTo(this,this)}function p(){for(var t=this.s&this.DM;this.t>0&&this[this.t-1]==t;)--this.t}function v(t){if(this.s<0)return"-"+this.negate().toString(t);var r;if(16==t)r=4;else if(8==t)r=3;else if(2==t)r=1;else if(32==t)r=5;else{if(4!=t)return this.toRadix(t);r=2}var i,e=(1<<r)-1,n=!1,o="",s=this.t,h=this.DB-s*this.DB%r;if(s-- >0)for(h<this.DB&&(i=this[s]>>h)>0&&(n=!0,o=u(i));s>=0;)h<r?(i=(this[s]&(1<<h)-1)<<r-h,i|=this[--s]>>(h+=this.DB-r)):(i=this[s]>>(h-=r)&e,h<=0&&(h+=this.DB,--s)),i>0&&(n=!0),n&&(o+=u(i));return n?o:"0"}function y(){var t=e();return i.ZERO.subTo(this,t),t}function m(){return this.s<0?this.negate():this}function d(t){var r=this.s-t.s;if(0!=r)return r;var i=this.t;if(0!=(r=i-t.t))return this.s<0?-r:r;for(;--i>=0;)if(0!=(r=this[i]-t[i]))return r;return 0}function g(t){var r,i=1;return 0!=(r=t>>>16)&&(t=r,i+=16),0!=(r=t>>8)&&(t=r,i+=8),0!=(r=t>>4)&&(t=r,i+=4),0!=(r=t>>2)&&(t=r,i+=2),0!=(r=t>>1)&&(t=r,i+=1),i}function T(){return this.t<=0?0:this.DB*(this.t-1)+g(this[this.t-1]^this.s&this.DM)}function F(t,r){var i;for(i=this.t-1;i>=0;--i)r[i+t]=this[i];for(i=t-1;i>=0;--i)r[i]=0;r.t=this.t+t,r.s=this.s}function b(t,r){for(var i=t;i<this.t;++i)r[i-t]=this[i];r.t=Math.max(this.t-t,0),r.s=this.s}function w(t,r){var i,e=t%this.DB,n=this.DB-e,o=(1<<n)-1,s=Math.floor(t/this.DB),u=this.s<<e&this.DM;for(i=this.t-1;i>=0;--i)r[i+s+1]=this[i]>>n|u,u=(this[i]&o)<<e;for(i=s-1;i>=0;--i)r[i]=0;r[s]=u,r.t=this.t+s+1,r.s=this.s,r.clamp()}function B(t,r){r.s=this.s;var i=Math.floor(t/this.DB);if(i>=this.t)return void(r.t=0);var e=t%this.DB,n=this.DB-e,o=(1<<e)-1;r[0]=this[i]>>e;for(var s=i+1;s<this.t;++s)r[s-i-1]|=(this[s]&o)<<n,r[s-i]=this[s]>>e;e>0&&(r[this.t-i-1]|=(this.s&o)<<n),r.t=this.t-i,r.clamp()}function x(t,r){for(var i=0,e=0,n=Math.min(t.t,this.t);i<n;)e+=this[i]-t[i],r[i++]=e&this.DM,e>>=this.DB;if(t.t<this.t){for(e-=t.s;i<this.t;)e+=this[i],r[i++]=e&this.DM,e>>=this.DB;e+=this.s}else{for(e+=this.s;i<t.t;)e-=t[i],r[i++]=e&this.DM,e>>=this.DB;e-=t.s}r.s=e<0?-1:0,e<-1?r[i++]=this.DV+e:e>0&&(r[i++]=e),r.t=i,r.clamp()}function D(t,r){var e=this.abs(),n=t.abs(),o=e.t;for(r.t=o+n.t;--o>=0;)r[o]=0;for(o=0;o<n.t;++o)r[o+e.t]=e.am(0,n[o],r,o,0,e.t);r.s=0,r.clamp(),this.s!=t.s&&i.ZERO.subTo(r,r)}function I(t){for(var r=this.abs(),i=t.t=2*r.t;--i>=0;)t[i]=0;for(i=0;i<r.t-1;++i){var e=r.am(i,r[i],t,2*i,0,1);(t[i+r.t]+=r.am(i+1,2*r[i],t,2*i+1,e,r.t-i-1))>=r.DV&&(t[i+r.t]-=r.DV,t[i+r.t+1]=1)}t.t>0&&(t[t.t-1]+=r.am(i,r[i],t,2*i,0,1)),t.s=0,t.clamp()}function q(t,r,n){var o=t.abs();if(!(o.t<=0)){var s=this.abs();if(s.t<o.t)return null!=r&&r.fromInt(0),void(null!=n&&this.copyTo(n));null==n&&(n=e());var u=e(),h=this.s,a=t.s,f=this.DB-g(o[o.t-1]);f>0?(o.lShiftTo(f,u),s.lShiftTo(f,n)):(o.copyTo(u),s.copyTo(n));var l=u.t,c=u[l-1];if(0!=c){var p=c*(1<<this.F1)+(l>1?u[l-2]>>this.F2:0),v=this.FV/p,y=(1<<this.F1)/p,m=1<<this.F2,d=n.t,T=d-l,F=null==r?e():r;for(u.dlShiftTo(T,F),n.compareTo(F)>=0&&(n[n.t++]=1,n.subTo(F,n)),i.ONE.dlShiftTo(l,F),F.subTo(u,u);u.t<l;)u[u.t++]=0;for(;--T>=0;){var b=n[--d]==c?this.DM:Math.floor(n[d]*v+(n[d-1]+m)*y);if((n[d]+=u.am(0,b,n,T,0,l))<b)for(u.dlShiftTo(T,F),n.subTo(F,n);n[d]<--b;)n.subTo(F,n)}null!=r&&(n.drShiftTo(l,r),h!=a&&i.ZERO.subTo(r,r)),n.t=l,n.clamp(),f>0&&n.rShiftTo(f,n),h<0&&i.ZERO.subTo(n,n)}}}function S(t){var r=e();return this.abs().divRemTo(t,null,r),this.s<0&&r.compareTo(i.ZERO)>0&&t.subTo(r,r),r}function E(t){this.m=t}function A(t){return t.s<0||t.compareTo(this.m)>=0?t.mod(this.m):t}function P(t){return t}function O(t){t.divRemTo(this.m,null,t)}function R(t,r,i){t.multiplyTo(r,i),this.reduce(i)}function M(t,r){t.squareTo(r),this.reduce(r)}function C(){if(this.t<1)return 0;var t=this[0];if(0==(1&t))return 0;var r=3&t;return r=r*(2-(15&t)*r)&15,r=r*(2-(255&t)*r)&255,r=r*(2-((65535&t)*r&65535))&65535,r=r*(2-t*r%this.DV)%this.DV,r>0?this.DV-r:-r}function H(t){this.m=t,this.mp=t.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<<t.DB-15)-1,this.mt2=2*t.t}function k(t){var r=e();return t.abs().dlShiftTo(this.m.t,r),r.divRemTo(this.m,null,r),t.s<0&&r.compareTo(i.ZERO)>0&&this.m.subTo(r,r),r}function V(t){var r=e();return t.copyTo(r),this.reduce(r),r}function N(t){for(;t.t<=this.mt2;)t[t.t++]=0;for(var r=0;r<this.m.t;++r){var i=32767&t[r],e=i*this.mpl+((i*this.mph+(t[r]>>15)*this.mpl&this.um)<<15)&t.DM;for(i=r+this.m.t,t[i]+=this.m.am(0,e,t,r,0,this.m.t);t[i]>=t.DV;)t[i]-=t.DV,t[++i]++}t.clamp(),t.drShiftTo(this.m.t,t),t.compareTo(this.m)>=0&&t.subTo(this.m,t)}function j(t,r){t.squareTo(r),this.reduce(r)}function L(t,r,i){t.multiplyTo(r,i),this.reduce(i)}function z(){return 0==(this.t>0?1&this[0]:this.s)}function K(t,r){if(t>4294967295||t<1)return i.ONE;var n=e(),o=e(),s=r.convert(this),u=g(t)-1;for(s.copyTo(n);--u>=0;)if(r.sqrTo(n,o),(t&1<<u)>0)r.mulTo(o,s,n);else{var h=n;n=o,o=h}return r.revert(n)}function Z(t,r){var i;return i=t<256||r.isEven()?new E(r):new H(r),this.exp(t,i)}function U(){var t=e();return this.copyTo(t),t}function _(){if(this.s<0){if(1==this.t)return this[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this[0];if(0==this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]}function X(){return 0==this.t?this.s:this[0]<<24>>24}function Y(){return 0==this.t?this.s:this[0]<<16>>16}function G(t){return Math.floor(Math.LN2*this.DB/Math.log(t))}function J(){return this.s<0?-1:this.t<=0||1==this.t&&this[0]<=0?0:1}function Q(t){if(null==t&&(t=10),0==this.signum()||t<2||t>36)return"0";var r=this.chunkSize(t),i=Math.pow(t,r),n=l(i),o=e(),s=e(),u="";for(this.divRemTo(n,o,s);o.signum()>0;)u=(i+s.intValue()).toString(t).substr(1)+u,o.divRemTo(n,o,s);return s.intValue().toString(t)+u}function W(t,r){this.fromInt(0),null==r&&(r=10);for(var e=this.chunkSize(r),n=Math.pow(r,e),o=!1,s=0,u=0,a=0;a<t.length;++a){var f=h(t,a);f<0?"-"==t.charAt(a)&&0==this.signum()&&(o=!0):(u=r*u+f,++s>=e&&(this.dMultiply(n),this.dAddOffset(u,0),s=0,u=0))}s>0&&(this.dMultiply(Math.pow(r,s)),this.dAddOffset(u,0)),o&&i.ZERO.subTo(this,this)}function $(t,r,e){if("number"==typeof r)if(t<2)this.fromInt(1);else for(this.fromNumber(t,e),this.testBit(t-1)||this.bitwiseTo(i.ONE.shiftLeft(t-1),ut,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(r);)this.dAddOffset(2,0),this.bitLength()>t&&this.subTo(i.ONE.shiftLeft(t-1),this);else{var n=new Array,o=7&t;n.length=1+(t>>3),r.nextBytes(n),o>0?n[0]&=(1<<o)-1:n[0]=0,this.fromString(n,256)}}function tt(){var t=this.t,r=new Array;r[0]=this.s;var i,e=this.DB-t*this.DB%8,n=0;if(t-- >0)for(e<this.DB&&(i=this[t]>>e)!=(this.s&this.DM)>>e&&(r[n++]=i|this.s<<this.DB-e);t>=0;)e<8?(i=(this[t]&(1<<e)-1)<<8-e,i|=this[--t]>>(e+=this.DB-8)):(i=this[t]>>(e-=8)&255,e<=0&&(e+=this.DB,--t)),0!=(128&i)&&(i|=-256),0==n&&(128&this.s)!=(128&i)&&++n,(n>0||i!=this.s)&&(r[n++]=i);return r}function rt(t){return 0==this.compareTo(t)}function it(t){return this.compareTo(t)<0?this:t}function et(t){return this.compareTo(t)>0?this:t}function nt(t,r,i){var e,n,o=Math.min(t.t,this.t);for(e=0;e<o;++e)i[e]=r(this[e],t[e]);if(t.t<this.t){for(n=t.s&this.DM,e=o;e<this.t;++e)i[e]=r(this[e],n);i.t=this.t}else{for(n=this.s&this.DM,e=o;e<t.t;++e)i[e]=r(n,t[e]);i.t=t.t}i.s=r(this.s,t.s),i.clamp()}function ot(t,r){return t&r}function st(t){var r=e();return this.bitwiseTo(t,ot,r),r}function ut(t,r){return t|r}function ht(t){var r=e();return this.bitwiseTo(t,ut,r),r}function at(t,r){return t^r}function ft(t){var r=e();return this.bitwiseTo(t,at,r),r}function lt(t,r){return t&~r}function ct(t){var r=e();return this.bitwiseTo(t,lt,r),r}function pt(){for(var t=e(),r=0;r<this.t;++r)t[r]=this.DM&~this[r];return t.t=this.t,t.s=~this.s,t}function vt(t){var r=e();return t<0?this.rShiftTo(-t,r):this.lShiftTo(t,r),r}function yt(t){var r=e();return t<0?this.lShiftTo(-t,r):this.rShiftTo(t,r),r}function mt(t){if(0==t)return-1;var r=0;return 0==(65535&t)&&(t>>=16,r+=16),0==(255&t)&&(t>>=8,r+=8),0==(15&t)&&(t>>=4,r+=4),0==(3&t)&&(t>>=2,r+=2),0==(1&t)&&++r,r}function dt(){for(var t=0;t<this.t;++t)if(0!=this[t])return t*this.DB+mt(this[t]);return this.s<0?this.t*this.DB:-1}function gt(t){for(var r=0;0!=t;)t&=t-1,++r;return r}function Tt(){for(var t=0,r=this.s&this.DM,i=0;i<this.t;++i)t+=gt(this[i]^r);return t}function Ft(t){var r=Math.floor(t/this.DB);return r>=this.t?0!=this.s:0!=(this[r]&1<<t%this.DB)}function bt(t,r){var e=i.ONE.shiftLeft(t);return this.bitwiseTo(e,r,e),e}function wt(t){return this.changeBit(t,ut)}function Bt(t){return this.changeBit(t,lt)}function xt(t){return this.changeBit(t,at)}function Dt(t,r){for(var i=0,e=0,n=Math.min(t.t,this.t);i<n;)e+=this[i]+t[i],r[i++]=e&this.DM,e>>=this.DB;if(t.t<this.t){for(e+=t.s;i<this.t;)e+=this[i],r[i++]=e&this.DM,e>>=this.DB;e+=this.s}else{for(e+=this.s;i<t.t;)e+=t[i],r[i++]=e&this.DM,e>>=this.DB;e+=t.s}r.s=e<0?-1:0,e>0?r[i++]=e:e<-1&&(r[i++]=this.DV+e),r.t=i,r.clamp()}function It(t){var r=e();return this.addTo(t,r),r}function qt(t){var r=e();return this.subTo(t,r),r}function St(t){var r=e();return this.multiplyTo(t,r),r}function Et(){var t=e();return this.squareTo(t),t}function At(t){var r=e();return this.divRemTo(t,r,null),r}function Pt(t){var r=e();return this.divRemTo(t,null,r),r}function Ot(t){var r=e(),i=e();return this.divRemTo(t,r,i),new Array(r,i)}function Rt(t){this[this.t]=this.am(0,t-1,this,0,0,this.t),++this.t,this.clamp()}function Mt(t,r){if(0!=t){for(;this.t<=r;)this[this.t++]=0;for(this[r]+=t;this[r]>=this.DV;)this[r]-=this.DV,++r>=this.t&&(this[this.t++]=0),++this[r]}}function Ct(){}function Ht(t){return t}function kt(t,r,i){t.multiplyTo(r,i)}function Vt(t,r){t.squareTo(r)}function Nt(t){return this.exp(t,new Ct)}function jt(t,r,i){var e=Math.min(this.t+t.t,r);for(i.s=0,i.t=e;e>0;)i[--e]=0;var n;for(n=i.t-this.t;e<n;++e)i[e+this.t]=this.am(0,t[e],i,e,0,this.t);for(n=Math.min(t.t,r);e<n;++e)this.am(0,t[e],i,e,0,r-e);i.clamp()}function Lt(t,r,i){--r;var e=i.t=this.t+t.t-r;for(i.s=0;--e>=0;)i[e]=0;for(e=Math.max(r-this.t,0);e<t.t;++e)i[this.t+e-r]=this.am(r-e,t[e],i,0,0,this.t+e-r);i.clamp(),i.drShiftTo(1,i)}function zt(t){this.r2=e(),this.q3=e(),i.ONE.dlShiftTo(2*t.t,this.r2),this.mu=this.r2.divide(t),this.m=t}function Kt(t){if(t.s<0||t.t>2*this.m.t)return t.mod(this.m);if(t.compareTo(this.m)<0)return t;var r=e();return t.copyTo(r),this.reduce(r),r}function Zt(t){return t}function Ut(t){for(t.drShiftTo(this.m.t-1,this.r2),t.t>this.m.t+1&&(t.t=this.m.t+1,t.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);t.compareTo(this.r2)<0;)t.dAddOffset(1,this.m.t+1);for(t.subTo(this.r2,t);t.compareTo(this.m)>=0;)t.subTo(this.m,t)}function _t(t,r){t.squareTo(r),this.reduce(r)}function Xt(t,r,i){t.multiplyTo(r,i),this.reduce(i)}function Yt(t,r){var i,n,o=t.bitLength(),s=l(1);if(o<=0)return s;i=o<18?1:o<48?3:o<144?4:o<768?5:6,n=o<8?new E(r):r.isEven()?new zt(r):new H(r);var u=new Array,h=3,a=i-1,f=(1<<i)-1;if(u[1]=n.convert(this),i>1){var c=e();for(n.sqrTo(u[1],c);h<=f;)u[h]=e(),n.mulTo(c,u[h-2],u[h]),h+=2}var p,v,y=t.t-1,m=!0,d=e();for(o=g(t[y])-1;y>=0;){for(o>=a?p=t[y]>>o-a&f:(p=(t[y]&(1<<o+1)-1)<<a-o,y>0&&(p|=t[y-1]>>this.DB+o-a)),h=i;0==(1&p);)p>>=1,--h;if((o-=h)<0&&(o+=this.DB,--y),m)u[p].copyTo(s),m=!1;else{for(;h>1;)n.sqrTo(s,d),n.sqrTo(d,s),h-=2;h>0?n.sqrTo(s,d):(v=s,s=d,d=v),n.mulTo(d,u[p],s)}for(;y>=0&&0==(t[y]&1<<o);)n.sqrTo(s,d),v=s,s=d,d=v,--o<0&&(o=this.DB-1,--y)}return n.revert(s)}function Gt(t){var r=this.s<0?this.negate():this.clone(),i=t.s<0?t.negate():t.clone();if(r.compareTo(i)<0){var e=r;r=i,i=e}var n=r.getLowestSetBit(),o=i.getLowestSetBit();if(o<0)return r;for(n<o&&(o=n),o>0&&(r.rShiftTo(o,r),i.rShiftTo(o,i));r.signum()>0;)(n=r.getLowestSetBit())>0&&r.rShiftTo(n,r),(n=i.getLowestSetBit())>0&&i.rShiftTo(n,i),r.compareTo(i)>=0?(r.subTo(i,r),r.rShiftTo(1,r)):(i.subTo(r,i),i.rShiftTo(1,i));return o>0&&i.lShiftTo(o,i),i}function Jt(t){if(t<=0)return 0;var r=this.DV%t,i=this.s<0?t-1:0;if(this.t>0)if(0==r)i=this[0]%t;else for(var e=this.t-1;e>=0;--e)i=(r*i+this[e])%t;return i}function Qt(t){var r=t.isEven();if(this.isEven()&&r||0==t.signum())return i.ZERO;for(var e=t.clone(),n=this.clone(),o=l(1),s=l(0),u=l(0),h=l(1);0!=e.signum();){for(;e.isEven();)e.rShiftTo(1,e),r?(o.isEven()&&s.isEven()||(o.addTo(this,o),s.subTo(t,s)),o.rShiftTo(1,o)):s.isEven()||s.subTo(t,s),s.rShiftTo(1,s);for(;n.isEven();)n.rShiftTo(1,n),r?(u.isEven()&&h.isEven()||(u.addTo(this,u),h.subTo(t,h)),u.rShiftTo(1,u)):h.isEven()||h.subTo(t,h),h.rShiftTo(1,h);e.compareTo(n)>=0?(e.subTo(n,e),r&&o.subTo(u,o),s.subTo(h,s)):(n.subTo(e,n),r&&u.subTo(o,u),h.subTo(s,h))}return 0!=n.compareTo(i.ONE)?i.ZERO:h.compareTo(t)>=0?h.subtract(t):h.signum()<0?(h.addTo(t,h),h.signum()<0?h.add(t):h):h}function Wt(t){var r,i=this.abs();if(1==i.t&&i[0]<=yr[yr.length-1]){for(r=0;r<yr.length;++r)if(i[0]==yr[r])return!0;return!1}if(i.isEven())return!1;for(r=1;r<yr.length;){for(var e=yr[r],n=r+1;n<yr.length&&e<mr;)e*=yr[n++];for(e=i.modInt(e);r<n;)if(e%yr[r++]==0)return!1}return i.millerRabin(t)}function $t(t){var r=this.subtract(i.ONE),n=r.getLowestSetBit();if(n<=0)return!1;var o=r.shiftRight(n);(t=t+1>>1)>yr.length&&(t=yr.length);for(var s=e(),u=0;u<t;++u){s.fromInt(yr[Math.floor(Math.random()*yr.length)]);var h=s.modPow(o,this);if(0!=h.compareTo(i.ONE)&&0!=h.compareTo(r)){for(var a=1;a++<n&&0!=h.compareTo(r);)if(h=h.modPowInt(2,this),0==h.compareTo(i.ONE))return!1;if(0!=h.compareTo(r))return!1}}return!0}function tr(t){gr[Tr++]^=255&t,gr[Tr++]^=t>>8&255,gr[Tr++]^=t>>16&255,gr[Tr++]^=t>>24&255,Tr>=Br&&(Tr-=Br)}function rr(){tr((new Date).getTime())}function ir(){if(null==dr){for(rr(),dr=hr(),dr.init(gr),Tr=0;Tr<gr.length;++Tr)gr[Tr]=0;Tr=0}return dr.next()}function er(t){var r;for(r=0;r<t.length;++r)t[r]=ir()}function nr(){}function or(){this.i=0,this.j=0,this.S=new Array}function sr(t){var r,i,e;for(r=0;r<256;++r)this.S[r]=r;for(i=0,r=0;r<256;++r)i=i+this.S[r]+t[r%t.length]&255,e=this.S[r],this.S[r]=this.S[i],this.S[i]=e;this.i=0,this.j=0}function ur(){var t;return this.i=this.i+1&255,this.j=this.j+this.S[this.i]&255,t=this.S[this.i],this.S[this.i]=this.S[this.j],this.S[this.j]=t,this.S[t+this.S[this.i]&255]}function hr(){return new or}var ar,fr="undefined"!=typeof navigator;fr&&"Microsoft Internet Explorer"==navigator.appName?(i.prototype.am=o,ar=30):fr&&"Netscape"!=navigator.appName?(i.prototype.am=n,ar=26):(i.prototype.am=s,ar=28),i.prototype.DB=ar,i.prototype.DM=(1<<ar)-1,i.prototype.DV=1<<ar;i.prototype.FV=Math.pow(2,52),i.prototype.F1=52-ar,i.prototype.F2=2*ar-52;var lr,cr,pr="0123456789abcdefghijklmnopqrstuvwxyz",vr=new Array;for(lr="0".charCodeAt(0),cr=0;cr<=9;++cr)vr[lr++]=cr;for(lr="a".charCodeAt(0),cr=10;cr<36;++cr)vr[lr++]=cr;for(lr="A".charCodeAt(0),cr=10;cr<36;++cr)vr[lr++]=cr;E.prototype.convert=A,E.prototype.revert=P,E.prototype.reduce=O,E.prototype.mulTo=R,E.prototype.sqrTo=M,H.prototype.convert=k,H.prototype.revert=V,H.prototype.reduce=N,H.prototype.mulTo=L,H.prototype.sqrTo=j,i.prototype.copyTo=a,i.prototype.fromInt=f,i.prototype.fromString=c,i.prototype.clamp=p,i.prototype.dlShiftTo=F,i.prototype.drShiftTo=b,i.prototype.lShiftTo=w,i.prototype.rShiftTo=B,i.prototype.subTo=x,i.prototype.multiplyTo=D,i.prototype.squareTo=I,i.prototype.divRemTo=q,i.prototype.invDigit=C,i.prototype.isEven=z,i.prototype.exp=K,i.prototype.toString=v,i.prototype.negate=y,i.prototype.abs=m,i.prototype.compareTo=d,i.prototype.bitLength=T,i.prototype.mod=S,i.prototype.modPowInt=Z,i.ZERO=l(0),i.ONE=l(1),Ct.prototype.convert=Ht,Ct.prototype.revert=Ht,Ct.prototype.mulTo=kt,Ct.prototype.sqrTo=Vt,zt.prototype.convert=Kt,zt.prototype.revert=Zt,zt.prototype.reduce=Ut,zt.prototype.mulTo=Xt,zt.prototype.sqrTo=_t;var yr=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],mr=(1<<26)/yr[yr.length-1];i.prototype.chunkSize=G,i.prototype.toRadix=Q,i.prototype.fromRadix=W,i.prototype.fromNumber=$,i.prototype.bitwiseTo=nt,i.prototype.changeBit=bt,i.prototype.addTo=Dt,i.prototype.dMultiply=Rt,i.prototype.dAddOffset=Mt,i.prototype.multiplyLowerTo=jt,i.prototype.multiplyUpperTo=Lt,i.prototype.modInt=Jt,i.prototype.millerRabin=$t,i.prototype.clone=U,i.prototype.intValue=_,i.prototype.byteValue=X,i.prototype.shortValue=Y,i.prototype.signum=J,i.prototype.toByteArray=tt,i.prototype.equals=rt,i.prototype.min=it,i.prototype.max=et,i.prototype.and=st,i.prototype.or=ht,i.prototype.xor=ft,i.prototype.andNot=ct,i.prototype.not=pt,i.prototype.shiftLeft=vt,i.prototype.shiftRight=yt,i.prototype.getLowestSetBit=dt,i.prototype.bitCount=Tt,i.prototype.testBit=Ft,i.prototype.setBit=wt,i.prototype.clearBit=Bt,i.prototype.flipBit=xt,i.prototype.add=It,i.prototype.subtract=qt,i.prototype.multiply=St,i.prototype.divide=At,i.prototype.remainder=Pt,i.prototype.divideAndRemainder=Ot,i.prototype.modPow=Yt,i.prototype.modInverse=Qt,i.prototype.pow=Nt,i.prototype.gcd=Gt,i.prototype.isProbablePrime=Wt,i.prototype.square=Et,i.prototype.Barrett=zt;var dr,gr,Tr;if(null==gr){gr=new Array,Tr=0;var Fr;if("undefined"!=typeof window&&window.crypto)if(window.crypto.getRandomValues){var br=new Uint8Array(32);for(window.crypto.getRandomValues(br),Fr=0;Fr<32;++Fr)gr[Tr++]=br[Fr]}else if("Netscape"==navigator.appName&&navigator.appVersion<"5"){var wr=window.crypto.random(32);for(Fr=0;Fr<wr.length;++Fr)gr[Tr++]=255&wr.charCodeAt(Fr)}for(;Tr<Br;)Fr=Math.floor(65536*Math.random()),gr[Tr++]=Fr>>>8,gr[Tr++]=255&Fr;Tr=0,rr()}nr.prototype.nextBytes=er,or.prototype.init=sr,or.prototype.next=ur;var Br=256;r=t.exports={default:i,BigInteger:i,SecureRandom:nr}}).call(this)},function(t,r,i){"use strict";function e(t){if(Array.isArray(t)){for(var r=0,i=Array(t.length);r<t.length;r++)i[r]=t[r];return i}return Array.from(t)}function n(t,r){var i=31&r;return t<<i|t>>>32-i}function o(t,r){for(var i=[],e=t.length-1;e>=0;e--)i[e]=255&(t[e]^r[e]);return i}function s(t){return t^n(t,9)^n(t,17)}function u(t){return t^n(t,15)^n(t,23)}function h(t){var r=8*t.length,i=r%512;i=i>=448?512-i%448-1:448-i-1;for(var o=new Array((i-7)/8),h=new Array(8),a=0,c=o.length;a<c;a++)o[a]=0;for(var p=0,v=h.length;p<v;p++)h[p]=0;r=r.toString(2);for(var y=7;y>=0;y--)if(r.length>8){var m=r.length-8;h[y]=parseInt(r.substr(m),2),r=r.substr(0,m)}else r.length>0&&(h[y]=parseInt(r,2),r="");for(var d=new Uint8Array([].concat(e(t),[128],o,h)),g=new DataView(d.buffer,0),T=d.length/64,F=new Uint32Array([1937774191,1226093241,388252375,3666478592,2842636476,372324522,3817729613,2969243214]),b=0;b<T;b++){f.fill(0),l.fill(0);for(var w=16*b,B=0;B<16;B++)f[B]=g.getUint32(4*(w+B),!1);for(var x=16;x<68;x++)f[x]=u(f[x-16]^f[x-9]^n(f[x-3],15))^n(f[x-13],7)^f[x-6];for(var D=0;D<64;D++)l[D]=f[D]^f[D+4];for(var I=F[0],q=F[1],S=F[2],E=F[3],A=F[4],P=F[5],O=F[6],R=F[7],M=void 0,C=void 0,H=void 0,k=void 0,V=void 0,N=0;N<64;N++)V=N>=0&&N<=15?2043430169:2055708042,M=n(n(I,12)+A+n(V,N),7),C=M^n(I,12),H=(N>=0&&N<=15?I^q^S:I&q|I&S|q&S)+E+C+l[N],k=(N>=0&&N<=15?A^P^O:A&P|~A&O)+R+M+f[N],E=S,S=n(q,9),q=I,I=H,R=O,O=n(P,19),P=A,A=s(k);F[0]^=I,F[1]^=q,F[2]^=S,F[3]^=E,F[4]^=A,F[5]^=P,F[6]^=O,F[7]^=R}for(var j=[],L=0,z=F.length;L<z;L++){var K=F[L];j.push((4278190080&K)>>>24,(16711680&K)>>>16,(65280&K)>>>8,255&K)}return j}function a(t,r){for(r.length>c&&(r=h(r));r.length<c;)r.push(0);var i=o(r,p),n=o(r,v),s=h([].concat(e(i),e(t)));return h([].concat(e(n),e(s)))}for(var f=new Uint32Array(68),l=new Uint32Array(64),c=64,p=new Uint8Array(c),v=new Uint8Array(c),y=0;y<c;y++)p[y]=54,v[y]=92;t.exports={sm3:h,hmac:a}},function(t,r,i){"use strict";function e(t){if(Array.isArray(t)){for(var r=0,i=Array(t.length);r<t.length;r++)i[r]=t[r];return i}return Array.from(t)}function n(t,r){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;t="string"==typeof t?m.hexToArray(m.utf8ToHex(t)):Array.prototype.slice.call(t),r=m.getGlobalCurve().decodePointHex(r);var n=m.generateKeyPairHex(),o=new c(n.privateKey,16),s=n.publicKey;s.length>128&&(s=s.substr(s.length-128));var u=r.multiply(o),h=m.hexToArray(m.leftPad(u.getX().toBigInteger().toRadix(16),64)),a=m.hexToArray(m.leftPad(u.getY().toBigInteger().toRadix(16),64)),f=m.arrayToHex(d([].concat(h,t,a))),l=1,p=0,v=[],y=[].concat(h,a),g=function(){v=d([].concat(e(y),[l>>24&255,l>>16&255,l>>8&255,255&l])),l++,p=0};g();for(var T=0,F=t.length;T<F;T++)p===v.length&&g(),t[T]^=255&v[p++];var b=m.arrayToHex(t);return i===w?s+b+f:s+f+b}function o(t,r){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=n.output,s=void 0===o?"string":o;r=new c(r,16);var u=t.substr(128,64),h=t.substr(192);i===w&&(u=t.substr(t.length-64),h=t.substr(128,t.length-128-64));var a=m.hexToArray(h),f=m.getGlobalCurve().decodePointHex("04"+t.substr(0,128)),l=f.multiply(r),p=m.hexToArray(m.leftPad(l.getX().toBigInteger().toRadix(16),64)),v=m.hexToArray(m.leftPad(l.getY().toBigInteger().toRadix(16),64)),y=1,g=0,T=[],F=[].concat(p,v),b=function(){T=d([].concat(e(F),[y>>24&255,y>>16&255,y>>8&255,255&y])),y++,g=0};b();for(var B=0,x=a.length;B<x;B++)g===T.length&&b(),a[B]^=255&T[g++];return m.arrayToHex(d([].concat(p,a,v)))===u.toLowerCase()?"array"===s?a:m.arrayToUtf8(a):"array"===s?[]:""}function s(t,r){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},e=i.pointPool,n=i.der,o=i.hash,s=i.publicKey,u=i.userId,l="string"==typeof t?m.utf8ToHex(t):m.arrayToHex(t);o&&(s=s||a(r),l=h(l,s,u));var p=new c(r,16),y=new c(l,16),d=null,g=null,T=null;do{do{var F=void 0;F=e&&e.length?e.pop():f(),d=F.k,g=y.add(F.x1).mod(b)}while(g.equals(c.ZERO)||g.add(d).equals(b));T=p.add(c.ONE).modInverse(b).multiply(d.subtract(g.multiply(p))).mod(b)}while(T.equals(c.ZERO));return n?v(g,T):m.leftPad(g.toString(16),64)+m.leftPad(T.toString(16),64)}function u(t,r,i){var e=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},n=e.der,o=e.hash,s=e.userId,u="string"==typeof t?m.utf8ToHex(t):m.arrayToHex(t);o&&(u=h(u,i,s));var a=void 0,f=void 0;if(n){var l=y(r);a=l.r,f=l.s}else a=new c(r.substring(0,64),16),f=new c(r.substring(64),16);var p=F.decodePointHex(i),v=new c(u,16),d=a.add(f).mod(b);if(d.equals(c.ZERO))return!1;var g=T.multiply(f).add(p.multiply(d)),w=v.add(g.getX().toBigInteger()).mod(b);return a.equals(w)}function h(t,r){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"1234567812345678";i=m.utf8ToHex(i);var e=m.leftPad(T.curve.a.toBigInteger().toRadix(16),64),n=m.leftPad(T.curve.b.toBigInteger().toRadix(16),64),o=m.leftPad(T.getX().toBigInteger().toRadix(16),64),s=m.leftPad(T.getY().toBigInteger().toRadix(16),64),u=void 0,h=void 0;if(128===r.length)u=r.substr(0,64),h=r.substr(64,64);else{var a=T.curve.decodePointHex(r);u=m.leftPad(a.getX().toBigInteger().toRadix(16),64),h=m.leftPad(a.getY().toBigInteger().toRadix(16),64)}var f=m.hexToArray(i+e+n+o+s+u+h),l=4*i.length;f.unshift(255&l),f.unshift(l>>8&255);var c=d(f);return m.arrayToHex(d(c.concat(m.hexToArray(t))))}function a(t){var r=T.multiply(new c(t,16));return"04"+m.leftPad(r.getX().toBigInteger().toString(16),64)+m.leftPad(r.getY().toBigInteger().toString(16),64)}function f(){var t=m.generateKeyPairHex(),r=F.decodePointHex(t.publicKey);return t.k=new c(t.privateKey,16),t.x1=r.getX().toBigInteger(),t}var l=i(0),c=l.BigInteger,p=i(3),v=p.encodeDer,y=p.decodeDer,m=i(4),d=i(1).sm3,g=m.generateEcparam(),T=g.G,F=g.curve,b=g.n,w=0;t.exports={generateKeyPairHex:m.generateKeyPairHex,compressPublicKeyHex:m.compressPublicKeyHex,comparePublicKeyHex:m.comparePublicKeyHex,doEncrypt:n,doDecrypt:o,doSignature:s,doVerifySignature:u,getPublicKeyFromPrivateKey:a,getPoint:f,verifyPublicKey:m.verifyPublicKey}},function(t,r,i){"use strict";function e(t,r){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!r||"object"!=typeof r&&"function"!=typeof r?t:r}function n(t,r){if("function"!=typeof r&&null!==r)throw new TypeError("Super expression must either be null or a function, not "+typeof r);t.prototype=Object.create(r&&r.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),r&&(Object.setPrototypeOf?Object.setPrototypeOf(t,r):t.__proto__=r)}function o(t,r){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}function s(t){var r=t.toString(16);if("-"!==r[0])r.length%2==1?r="0"+r:r.match(/^[0-7]/)||(r="00"+r);else{r=r.substr(1);var i=r.length;i%2==1?i+=1:r.match(/^[0-7]/)||(i+=2);for(var e="",n=0;n<i;n++)e+="f";e=new c(e,16),r=e.xor(t).add(c.ONE),r=r.toString(16).replace(/^-/,"")}return r}function u(t,r){return+t[r+2]<8?1:128&+t.substr(r+2,2)}function h(t,r){var i=u(t,r),e=t.substr(r+2,2*i);return e?(+e[0]<8?new c(e,16):new c(e.substr(2),16)).intValue():-1}function a(t,r){return r+2*(u(t,r)+1)}var f=function(){function t(t,r){for(var i=0;i<r.length;i++){var e=r[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(t,e.key,e)}}return function(r,i,e){return i&&t(r.prototype,i),e&&t(r,e),r}}(),l=i(0),c=l.BigInteger,p=function(){function t(){o(this,t),this.tlv=null,this.t="00",this.l="00",this.v=""}return f(t,[{key:"getEncodedHex",value:function(){return this.tlv||(this.v=this.getValue(),this.l=this.getLength(),this.tlv=this.t+this.l+this.v),this.tlv}},{key:"getLength",value:function(){var t=this.v.length/2,r=t.toString(16);return r.length%2==1&&(r="0"+r),t<128?r:(128+r.length/2).toString(16)+r}},{key:"getValue",value:function(){return""}}]),t}(),v=function(t){function r(t){o(this,r);var i=e(this,(r.__proto__||Object.getPrototypeOf(r)).call(this));return i.t="02",t&&(i.v=s(t)),i}return n(r,t),f(r,[{key:"getValue",value:function(){return this.v}}]),r}(p),y=function(t){function r(t){o(this,r);var i=e(this,(r.__proto__||Object.getPrototypeOf(r)).call(this));return i.t="30",i.asn1Array=t,i}return n(r,t),f(r,[{key:"getValue",value:function(){return this.v=this.asn1Array.map(function(t){return t.getEncodedHex()}).join(""),this.v}}]),r}(p);t.exports={encodeDer:function(t,r){var i=new v(t),e=new v(r);return new y([i,e]).getEncodedHex()},decodeDer:function(t){var r=a(t,0),i=a(t,r),e=h(t,r),n=t.substr(i,2*e),o=i+n.length,s=a(t,o),u=h(t,o),f=t.substr(s,2*u);return{r:new c(n,16),s:new c(f,16)}}}},function(t,r,i){"use strict";function e(){return b}function n(){var t=new y("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",16),r=new y("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",16),i=new y("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",16),e=new g(t,r,i);return{curve:e,G:e.decodePointHex("0432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"),n:new y("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",16)}}function o(t,r,i){var e=t?new y(t,r,i):new y(B.bitLength(),T),n=e.mod(B.subtract(y.ONE)).add(y.ONE),o=h(n.toString(16),64),s=w.multiply(n);return{privateKey:o,publicKey:"04"+h(s.getX().toBigInteger().toString(16),64)+h(s.getY().toBigInteger().toString(16),64)}}function s(t){if(130!==t.length)throw new Error("Invalid public key to compress");var r=(t.length-2)/2,i=t.substr(2,r),e=new y(t.substr(r+2,r),16),n="03";return e.mod(new y("2")).equals(y.ZERO)&&(n="02"),n+i}function u(t){t=unescape(encodeURIComponent(t));for(var r=t.length,i=[],e=0;e<r;e++)i[e>>>2]|=(255&t.charCodeAt(e))<<24-e%4*8;for(var n=[],o=0;o<r;o++){var s=i[o>>>2]>>>24-o%4*8&255;n.push((s>>>4).toString(16)),n.push((15&s).toString(16))}return n.join("")}function h(t,r){return t.length>=r?t:new Array(r-t.length+1).join("0")+t}function a(t){return t.map(function(t){return t=t.toString(16),1===t.length?"0"+t:t}).join("")}function f(t){for(var r=[],i=0,e=0;e<2*t.length;e+=2)r[e>>>3]|=parseInt(t[i],10)<<24-e%8*4,i++;try{for(var n=[],o=0;o<t.length;o++){var s=r[o>>>2]>>>24-o%4*8&255;n.push(String.fromCharCode(s))}return decodeURIComponent(escape(n.join("")))}catch(t){throw new Error("Malformed UTF-8 data")}}function l(t){var r=[],i=t.length;i%2!=0&&(t=h(t,i+1)),i=t.length;for(var e=0;e<i;e+=2)r.push(parseInt(t.substr(e,2),16));return r}function c(t){var r=b.decodePointHex(t);if(!r)return!1;var i=r.getX();return r.getY().square().equals(i.multiply(i.square()).add(i.multiply(b.a)).add(b.b))}function p(t,r){var i=b.decodePointHex(t);if(!i)return!1;var e=b.decodePointHex(r);return!!e&&i.equals(e)}var v=i(0),y=v.BigInteger,m=v.SecureRandom,d=i(5),g=d.ECCurveFp,T=new m,F=n(),b=F.curve,w=F.G,B=F.n;t.exports={getGlobalCurve:e,generateEcparam:n,generateKeyPairHex:o,compressPublicKeyHex:s,utf8ToHex:u,leftPad:h,arrayToHex:a,arrayToUtf8:f,hexToArray:l,verifyPublicKey:c,comparePublicKeyHex:p}},function(t,r,i){"use strict";function e(t,r){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}var n=function(){function t(t,r){for(var i=0;i<r.length;i++){var e=r[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(t,e.key,e)}}return function(r,i,e){return i&&t(r.prototype,i),e&&t(r,e),r}}(),o=i(0),s=o.BigInteger,u=new s("2"),h=new s("3"),a=function(){function t(r,i){e(this,t),this.x=i,this.q=r}return n(t,[{key:"equals",value:function(t){return t===this||this.q.equals(t.q)&&this.x.equals(t.x)}},{key:"toBigInteger",value:function(){return this.x}},{key:"negate",value:function(){return new t(this.q,this.x.negate().mod(this.q))}},{key:"add",value:function(r){return new t(this.q,this.x.add(r.toBigInteger()).mod(this.q))}},{key:"subtract",value:function(r){return new t(this.q,this.x.subtract(r.toBigInteger()).mod(this.q))}},{key:"multiply",value:function(r){return new t(this.q,this.x.multiply(r.toBigInteger()).mod(this.q))}},{key:"divide",value:function(r){return new t(this.q,this.x.multiply(r.toBigInteger().modInverse(this.q)).mod(this.q))}},{key:"square",value:function(){return new t(this.q,this.x.square().mod(this.q))}}]),t}(),f=function(){function t(r,i,n,o){e(this,t),this.curve=r,this.x=i,this.y=n,this.z=null==o?s.ONE:o,this.zinv=null}return n(t,[{key:"getX",value:function(){return null===this.zinv&&(this.zinv=this.z.modInverse(this.curve.q)),this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q))}},{key:"getY",value:function(){return null===this.zinv&&(this.zinv=this.z.modInverse(this.curve.q)),this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q))}},{key:"equals",value:function(t){return t===this||(this.isInfinity()?t.isInfinity():t.isInfinity()?this.isInfinity():!!t.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(t.z)).mod(this.curve.q).equals(s.ZERO)&&t.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(t.z)).mod(this.curve.q).equals(s.ZERO))}},{key:"isInfinity",value:function(){return null===this.x&&null===this.y||this.z.equals(s.ZERO)&&!this.y.toBigInteger().equals(s.ZERO)}},{key:"negate",value:function(){return new t(this.curve,this.x,this.y.negate(),this.z)}},{key:"add",value:function(r){if(this.isInfinity())return r;if(r.isInfinity())return this;var i=this.x.toBigInteger(),e=this.y.toBigInteger(),n=this.z,o=r.x.toBigInteger(),u=r.y.toBigInteger(),h=r.z,a=this.curve.q,f=i.multiply(h).mod(a),l=o.multiply(n).mod(a),c=f.subtract(l),p=e.multiply(h).mod(a),v=u.multiply(n).mod(a),y=p.subtract(v);if(s.ZERO.equals(c))return s.ZERO.equals(y)?this.twice():this.curve.infinity;var m=f.add(l),d=n.multiply(h).mod(a),g=c.square().mod(a),T=c.multiply(g).mod(a),F=d.multiply(y.square()).subtract(m.multiply(g)).mod(a),b=c.multiply(F).mod(a),w=y.multiply(g.multiply(f).subtract(F)).subtract(p.multiply(T)).mod(a),B=T.multiply(d).mod(a);return new t(this.curve,this.curve.fromBigInteger(b),this.curve.fromBigInteger(w),B)}},{key:"twice",value:function(){if(this.isInfinity())return this;if(!this.y.toBigInteger().signum())return this.curve.infinity;var r=this.x.toBigInteger(),i=this.y.toBigInteger(),e=this.z,n=this.curve.q,o=this.curve.a.toBigInteger(),s=r.square().multiply(h).add(o.multiply(e.square())).mod(n),u=i.shiftLeft(1).multiply(e).mod(n),a=i.square().mod(n),f=a.multiply(r).multiply(e).mod(n),l=u.square().mod(n),c=s.square().subtract(f.shiftLeft(3)).mod(n),p=u.multiply(c).mod(n),v=s.multiply(f.shiftLeft(2).subtract(c)).subtract(l.shiftLeft(1).multiply(a)).mod(n),y=u.multiply(l).mod(n);return new t(this.curve,this.curve.fromBigInteger(p),this.curve.fromBigInteger(v),y)}},{key:"multiply",value:function(t){if(this.isInfinity())return this;if(!t.signum())return this.curve.infinity;for(var r=t.multiply(h),i=this.negate(),e=this,n=r.bitLength()-2;n>0;n--){e=e.twice();var o=r.testBit(n);o!==t.testBit(n)&&(e=e.add(o?this:i))}return e}}]),t}(),l=function(){function t(r,i,n){e(this,t),this.q=r,this.a=this.fromBigInteger(i),this.b=this.fromBigInteger(n),this.infinity=new f(this,null,null)}return n(t,[{key:"equals",value:function(t){return t===this||this.q.equals(t.q)&&this.a.equals(t.a)&&this.b.equals(t.b)}},{key:"fromBigInteger",value:function(t){return new a(this.q,t)}},{key:"decodePointHex",value:function(t){switch(parseInt(t.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:var r=this.fromBigInteger(new s(t.substr(2),16)),i=this.fromBigInteger(r.multiply(r.square()).add(r.multiply(this.a)).add(this.b).toBigInteger().modPow(this.q.divide(new s("4")).add(s.ONE),this.q));return i.toBigInteger().mod(u).equals(new s(t.substr(0,2),16).subtract(u))||(i=i.negate()),new f(this,r,i);case 4:case 6:case 7:var e=(t.length-2)/2,n=t.substr(2,e),o=t.substr(e+2,e);return new f(this,this.fromBigInteger(new s(n,16)),this.fromBigInteger(new s(o,16)));default:return null}}}]),t}();t.exports={ECPointFp:f,ECCurveFp:l}}])});
+70
View File
@@ -0,0 +1,70 @@
function decompressPublicKey(compressedKey) {
// 获取压缩标志和X坐标
var flag = compressedKey.slice(0, 2);
var x = compressedKey.slice(2);
// 定义常量
var p = BigInt('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16);
var a = BigInt('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16);
var b = BigInt('0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16);
// 计算Y坐标
// y2 = x3 + ax + b
var xBigInt = BigInt('0x' + x);
var alpha = (xBigInt ** 3n) % p;
var beta = (a * xBigInt + b) % p;
var y2 = (alpha + beta) % p;
var y = modularExponentiation(y2, (p + 1n) / 4n, p);
// y2 的值开方有两个值,根据奇偶判定取哪一个
if (flag === "02") {
// 如果压缩标志为“02”,则Y坐标为偶数
if (y % 2n === 0n) {
return "04" + x + y.toString(16).padStart(64, '0');
} else {
y = p - y;
return "04" + x + y.toString(16).padStart(64, '0');
}
} else if (flag === "03") {
// 如果压缩标志为“03”,则Y坐标为奇数
if (y % 2n === 1n) {
return "04" + x + y.toString(16).padStart(64, '0');
} else {
y = p - y;
return "04" + x + y.toString(16).padStart(64, '0');
}
} else {
return null;
}
}
function modularExponentiation(base, exponent, modulus) {
var result = 1n;
base = base % modulus;
while (exponent > 0n) {
if (exponent % 2n === 1n) {
result = (result * base) % modulus;
}
base = (base * base) % modulus;
exponent = exponent / 2n;
}
return result;
}
function hexToBase64(hex) {
var bytes = [];
for (var i = 0; i < hex.length-1; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
var byteArray = new Uint8Array(bytes);
return btoa(String.fromCharCode.apply(null, byteArray));
}
function base64ToHex(base64) {
var binaryString = atob(base64);
var byteArray = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
byteArray[i] = binaryString.charCodeAt(i);
}
var hexArray = Array.prototype.map.call(byteArray, function(byte) {
return ('0' + byte.toString(16)).slice(-2);
});
return hexArray.join('');
}
+1
View File
@@ -0,0 +1 @@
!function(r,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.sm3=n():r.sm3=n()}("undefined"!=typeof self?self:this,function(){return function(r){function n(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return r[e].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var t={};return n.m=r,n.c=t,n.d=function(r,t,e){n.o(r,t)||Object.defineProperty(r,t,{configurable:!1,enumerable:!0,get:e})},n.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(t,"a",t),t},n.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},n.p="",n(n.s=6)}({1:function(r,n,t){"use strict";function e(r){if(Array.isArray(r)){for(var n=0,t=Array(r.length);n<r.length;n++)t[n]=r[n];return t}return Array.from(r)}function o(r,n){var t=31&n;return r<<t|r>>>32-t}function u(r,n){for(var t=[],e=r.length-1;e>=0;e--)t[e]=255&(r[e]^n[e]);return t}function i(r){return r^o(r,9)^o(r,17)}function f(r){return r^o(r,15)^o(r,23)}function a(r){var n=8*r.length,t=n%512;t=t>=448?512-t%448-1:448-t-1;for(var u=new Array((t-7)/8),a=new Array(8),s=0,p=u.length;s<p;s++)u[s]=0;for(var h=0,v=a.length;h<v;h++)a[h]=0;n=n.toString(2);for(var y=7;y>=0;y--)if(n.length>8){var g=n.length-8;a[y]=parseInt(n.substr(g),2),n=n.substr(0,g)}else n.length>0&&(a[y]=parseInt(n,2),n="");for(var d=new Uint8Array([].concat(e(r),[128],u,a)),w=new DataView(d.buffer,0),m=d.length/64,A=new Uint32Array([1937774191,1226093241,388252375,3666478592,2842636476,372324522,3817729613,2969243214]),b=0;b<m;b++){c.fill(0),l.fill(0);for(var x=16*b,j=0;j<16;j++)c[j]=w.getUint32(4*(x+j),!1);for(var U=16;U<68;U++)c[U]=f(c[U-16]^c[U-9]^o(c[U-3],15))^o(c[U-13],7)^c[U-6];for(var E=0;E<64;E++)l[E]=c[E]^c[E+4];for(var I=A[0],O=A[1],P=A[2],k=A[3],S=A[4],_=A[5],D=A[6],M=A[7],V=void 0,q=void 0,z=void 0,B=void 0,C=void 0,F=0;F<64;F++)C=F>=0&&F<=15?2043430169:2055708042,V=o(o(I,12)+S+o(C,F),7),q=V^o(I,12),z=(F>=0&&F<=15?I^O^P:I&O|I&P|O&P)+k+q+l[F],B=(F>=0&&F<=15?S^_^D:S&_|~S&D)+M+V+c[F],k=P,P=o(O,9),O=I,I=z,M=D,D=o(_,19),_=S,S=i(B);A[0]^=I,A[1]^=O,A[2]^=P,A[3]^=k,A[4]^=S,A[5]^=_,A[6]^=D,A[7]^=M}for(var G=[],H=0,J=A.length;H<J;H++){var K=A[H];G.push((4278190080&K)>>>24,(16711680&K)>>>16,(65280&K)>>>8,255&K)}return G}function s(r,n){for(n.length>p&&(n=a(n));n.length<p;)n.push(0);var t=u(n,h),o=u(n,v),i=a([].concat(e(t),e(r)));return a([].concat(e(o),e(i)))}for(var c=new Uint32Array(68),l=new Uint32Array(64),p=64,h=new Uint8Array(p),v=new Uint8Array(p),y=0;y<p;y++)h[y]=54,v[y]=92;r.exports={sm3:a,hmac:s}},6:function(r,n,t){"use strict";function e(r,n){return r.length>=n?r:new Array(n-r.length+1).join("0")+r}function o(r){return r.map(function(r){return r=r.toString(16),1===r.length?"0"+r:r}).join("")}function u(r){var n=[],t=r.length;t%2!=0&&(r=e(r,t+1)),t=r.length;for(var o=0;o<t;o+=2)n.push(parseInt(r.substr(o,2),16));return n}function i(r){for(var n=[],t=0,e=r.length;t<e;t++){var o=r.codePointAt(t);if(o<=127)n.push(o);else if(o<=2047)n.push(192|o>>>6),n.push(128|63&o);else if(o<=55295||o>=57344&&o<=65535)n.push(224|o>>>12),n.push(128|o>>>6&63),n.push(128|63&o);else{if(!(o>=65536&&o<=1114111))throw n.push(o),new Error("input is not supported");t++,n.push(240|o>>>18&28),n.push(128|o>>>12&63),n.push(128|o>>>6&63),n.push(128|63&o)}}return n}var f=t(1),a=f.sm3,s=f.hmac;r.exports=function(r,n){if(r="string"==typeof r?i(r):Array.prototype.slice.call(r),n){if("hmac"!==(n.mode||"hmac"))throw new Error("invalid mode");var t=n.key;if(!t)throw new Error("invalid key");return t="string"==typeof t?u(t):Array.prototype.slice.call(t),o(s(r,t))}return o(a(r))}}})});
+1
View File
@@ -0,0 +1 @@
!function(r,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.sm4=n():r.sm4=n()}("undefined"!=typeof self?self:this,function(){return function(r){function n(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return r[e].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var t={};return n.m=r,n.c=t,n.d=function(r,t,e){n.o(r,t)||Object.defineProperty(r,t,{configurable:!1,enumerable:!0,get:e})},n.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(t,"a",t),t},n.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},n.p="",n(n.s=7)}({7:function(r,n,t){"use strict";function e(r){if(Array.isArray(r)){for(var n=0,t=Array(r.length);n<r.length;n++)t[n]=r[n];return t}return Array.from(r)}function o(r){for(var n=[],t=0,e=r.length;t<e;t+=2)n.push(parseInt(r.substr(t,2),16));return n}function i(r){return r.map(function(r){return r=r.toString(16),1===r.length?"0"+r:r}).join("")}function u(r){for(var n=[],t=0,e=r.length;t<e;t++){var o=r.codePointAt(t);if(o<=127)n.push(o);else if(o<=2047)n.push(192|o>>>6),n.push(128|63&o);else if(o<=55295||o>=57344&&o<=65535)n.push(224|o>>>12),n.push(128|o>>>6&63),n.push(128|63&o);else{if(!(o>=65536&&o<=1114111))throw n.push(o),new Error("input is not supported");t++,n.push(240|o>>>18&28),n.push(128|o>>>12&63),n.push(128|o>>>6&63),n.push(128|63&o)}}return n}function f(r){for(var n=[],t=0,e=r.length;t<e;t++)r[t]>=240&&r[t]<=247?(n.push(String.fromCodePoint(((7&r[t])<<18)+((63&r[t+1])<<12)+((63&r[t+2])<<6)+(63&r[t+3]))),t+=3):r[t]>=224&&r[t]<=239?(n.push(String.fromCodePoint(((15&r[t])<<12)+((63&r[t+1])<<6)+(63&r[t+2]))),t+=2):r[t]>=192&&r[t]<=223?(n.push(String.fromCodePoint(((31&r[t])<<6)+(63&r[t+1]))),t++):n.push(String.fromCodePoint(r[t]));return n.join("")}function s(r,n){var t=31&n;return r<<t|r>>>32-t}function c(r){return(255&w[r>>>24&255])<<24|(255&w[r>>>16&255])<<16|(255&w[r>>>8&255])<<8|255&w[255&r]}function p(r){return r^s(r,2)^s(r,10)^s(r,18)^s(r,24)}function a(r){return r^s(r,13)^s(r,23)}function h(r,n,t){for(var e=new Array(4),o=new Array(4),i=0;i<4;i++)o[0]=255&r[4*i],o[1]=255&r[4*i+1],o[2]=255&r[4*i+2],o[3]=255&r[4*i+3],e[i]=o[0]<<24|o[1]<<16|o[2]<<8|o[3];for(var u,f=0;f<32;f+=4)u=e[1]^e[2]^e[3]^t[f+0],e[0]^=p(c(u)),u=e[2]^e[3]^e[0]^t[f+1],e[1]^=p(c(u)),u=e[3]^e[0]^e[1]^t[f+2],e[2]^=p(c(u)),u=e[0]^e[1]^e[2]^t[f+3],e[3]^=p(c(u));for(var s=0;s<16;s+=4)n[s]=e[3-s/4]>>>24&255,n[s+1]=e[3-s/4]>>>16&255,n[s+2]=e[3-s/4]>>>8&255,n[s+3]=255&e[3-s/4]}function l(r,n,t){for(var e=new Array(4),o=new Array(4),i=0;i<4;i++)o[0]=255&r[0+4*i],o[1]=255&r[1+4*i],o[2]=255&r[2+4*i],o[3]=255&r[3+4*i],e[i]=o[0]<<24|o[1]<<16|o[2]<<8|o[3];e[0]^=2746333894,e[1]^=1453994832,e[2]^=1736282519,e[3]^=2993693404;for(var u,f=0;f<32;f+=4)u=e[1]^e[2]^e[3]^m[f+0],n[f+0]=e[0]^=a(c(u)),u=e[2]^e[3]^e[0]^m[f+1],n[f+1]=e[1]^=a(c(u)),u=e[3]^e[0]^e[1]^m[f+2],n[f+2]=e[2]^=a(c(u)),u=e[0]^e[1]^e[2]^m[f+3],n[f+3]=e[3]^=a(c(u));if(t===d)for(var s,p=0;p<16;p++)s=n[p],n[p]=n[31-p],n[31-p]=s}function v(r,n,t){var s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},c=s.padding,p=void 0===c?"pkcs#7":c,a=s.mode,v=s.iv,w=void 0===v?[]:v,m=s.output,b=void 0===m?"string":m;if("cbc"===a&&("string"==typeof w&&(w=o(w)),16!==w.length))throw new Error("iv is invalid");if("string"==typeof n&&(n=o(n)),16!==n.length)throw new Error("key is invalid");if(r="string"==typeof r?t!==d?u(r):o(r):[].concat(e(r)),("pkcs#5"===p||"pkcs#7"===p)&&t!==d)for(var A=y-r.length%y,x=0;x<A;x++)r.push(A);var j=new Array(g);l(n,j,t);for(var P=[],k=w,S=r.length,C=0;S>=y;){var E=r.slice(C,C+16),O=new Array(16);if("cbc"===a)for(var _=0;_<y;_++)t!==d&&(E[_]^=k[_]);h(E,O,j);for(var I=0;I<y;I++)"cbc"===a&&t===d&&(O[I]^=k[I]),P[C+I]=O[I];"cbc"===a&&(k=t!==d?O:E),S-=y,C+=y}if(("pkcs#5"===p||"pkcs#7"===p)&&t===d){for(var M=P.length,q=P[M-1],z=1;z<=q;z++)if(P[M-z]!==q)throw new Error("padding is invalid");P.splice(M-q,q)}return"array"!==b?t!==d?i(P):f(P):P}var d=0,g=32,y=16,w=[214,144,233,254,204,225,61,183,22,182,20,194,40,251,44,5,43,103,154,118,42,190,4,195,170,68,19,38,73,134,6,153,156,66,80,244,145,239,152,122,51,84,11,67,237,207,172,98,228,179,28,169,201,8,232,149,128,223,148,250,117,143,63,166,71,7,167,252,243,115,23,186,131,89,60,25,230,133,79,168,104,107,129,178,113,100,218,139,248,235,15,75,112,86,157,53,30,36,14,94,99,88,209,162,37,34,124,59,1,33,120,135,212,0,70,87,159,211,39,82,76,54,2,231,160,196,200,158,234,191,138,210,64,199,56,181,163,247,242,206,249,97,21,161,224,174,93,164,155,52,26,85,173,147,50,48,245,140,177,227,29,246,226,46,130,102,202,96,192,41,35,171,13,83,78,111,213,219,55,69,222,253,142,47,3,255,106,114,109,108,91,81,141,27,175,146,187,221,188,127,17,217,92,65,31,16,90,216,10,193,49,136,165,205,123,189,45,116,208,18,184,229,180,176,137,105,151,74,12,150,119,126,101,185,241,9,197,110,198,132,24,240,125,236,58,220,77,32,121,238,95,62,215,203,57,72],m=[462357,472066609,943670861,1415275113,1886879365,2358483617,2830087869,3301692121,3773296373,4228057617,404694573,876298825,1347903077,1819507329,2291111581,2762715833,3234320085,3705924337,4177462797,337322537,808926789,1280531041,1752135293,2223739545,2695343797,3166948049,3638552301,4110090761,269950501,741554753,1213159005,1684763257];r.exports={encrypt:function(r,n,t){return v(r,n,1,t)},decrypt:function(r,n,t){return v(r,n,0,t)}}}})});
+5
View File
@@ -0,0 +1,5 @@
INSERT INTO `toolbox_plugin` (`id`, `title`, `alias`, `class`, `keyword`, `weight`, `enable`, `level`, `login`, `request_count`, `category_id`, `desc`, `create_time`, `update_time`) VALUES
(96, '国密SM2工具', 'guomi', 'dev\\guomi', 'guomism2,gmsm2', 0, 1, 0, 0, 19, 2, '', '2023-10-26 17:50:38', '2023-10-26 21:35:10'),
(97, '证书信息查看', 'cert', 'dev\\cert', 'zhengshuxinxichakan,zsxxck', 0, 1, 0, 0, 18, 2, '', '2023-10-27 16:05:08', '2023-10-27 16:05:08'),
(98, '证书格式转换', 'cert_convert', 'dev\\cert_convert', 'rsazhengshugeshizhuanhuan,rsazsgszh', 0, 1, 0, 0, 8, 2, '', '2023-10-24 21:25:15', '2023-10-27 16:03:56'),
(99, 'CSR生成与查看', 'csr', 'dev\\csr', 'csrshengchengchakan,csrscck', 0, 1, 0, 0, 8, 2, '', '2023-11-09 20:18:19', '2023-11-10 17:14:02');
+5 -5
View File
@@ -5,11 +5,11 @@
<link rel="icon" href="/favicon.ico"/> <link rel="icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link href="https://cdn.staticfile.org/daisyui/2.2.2/full.min.css" rel="stylesheet" type="text/css"/> <link href="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/daisyui/2.2.2/full.min.css" rel="stylesheet" type="text/css"/>
<link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet" type="text/css"/> <link href="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet" type="text/css"/>
<script src="https://cdn.staticfile.org/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js"></script>
<script src="https://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js"></script>
<script src="https://cdn.staticfile.org/axios/0.26.0/axios.min.js"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js"></script>
<style> <style>
body { body {
background-image: url("static/images/install_background.jpg"); background-image: url("static/images/install_background.jpg");