This commit is contained in:
2024-08-05 22:57:28 +08:00
commit 0efda6c02a
1779 changed files with 171774 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
<?php
namespace app\command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use AcmePhp\Core\Http\Base64SafeEncoder;
use AcmePhp\Core\Http\SecureHttpClientFactory;
use AcmePhp\Core\Http\ServerErrorHandler;
use AcmePhp\Ssl\KeyPair;
use AcmePhp\Ssl\PrivateKey;
use AcmePhp\Ssl\PublicKey;
use AcmePhp\Ssl\Parser\KeyParser;
use AcmePhp\Ssl\Signer\DataSigner;
use GuzzleHttp\Client as GuzzleHttpClient;
use AcmePhp\Ssl\DistinguishedName;
use AcmePhp\Ssl\CertificateRequest;
use AcmePhp\Ssl\Generator\KeyPairGenerator;
use AcmePhp\Core\Challenge\Dns\DnsDataExtractor;
use AcmePhp\Core\AcmeClient;
class Acme extends Command
{
protected static $defaultName = 'acme';
protected static $defaultDescription = 'acme';
/**
* @return void
*/
protected function configure()
{
$this->addArgument('name', InputArgument::OPTIONAL, 'Name description');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$EMAIL='youremail@yourdomain.com';
$DOMAIN='yourdomain.com';
#$CA='https://acme-v02.api.letsencrypt.org/directory';
$CA='https://acme-staging-v02.api.letsencrypt.org/directory';
$secureHttpClientFactory = new SecureHttpClientFactory(
new GuzzleHttpClient(),
new Base64SafeEncoder(),
new KeyParser(),
new DataSigner(),
new ServerErrorHandler()
);
$publicKeyPath = base_path().'/keys/account.pub.pem';
$privateKeyPath = base_path().'/keys/account.pem';
if (!file_exists($privateKeyPath)) {
$keyPairGenerator = new KeyPairGenerator();
$keyPair = $keyPairGenerator->generateKeyPair();
file_put_contents($publicKeyPath, $keyPair->getPublicKey()->getPEM());
file_put_contents($privateKeyPath, $keyPair->getPrivateKey()->getPEM());
} else {
$publicKey = new PublicKey(file_get_contents($publicKeyPath));
$privateKey = new PrivateKey(file_get_contents($privateKeyPath));
$keyPair = new KeyPair($publicKey, $privateKey);
}
$secureHttpClient = $secureHttpClientFactory->createSecureHttpClient($keyPair);
#$acmeClient = new AcmeClient($secureHttpClient, 'https://acme-v02.api.letsencrypt.org/directory');
$acmeClient = new AcmeClient($secureHttpClient, $CA);
$acmeClient->registerAccount($EMAIL,null);
$authorizationChallenges = $acmeClient->requestAuthorization($DOMAIN);
foreach($authorizationChallenges as $id=>$cha){
$cha->id=$id;
$dde=new DnsDataExtractor;
$cha->value=$dde->getRecordValue($cha);
print_r($cha);
}
#print_r($authorizationChallenges);
#print_r($dde->getRecordValue($authorizationChallenges[1]));
#$output->writeln($authorizationChallenges);
$helper = $this->getHelper('question');
$question = new ChoiceQuestion(
'选择对应的ID',
array('0', '1', '2'),
0
);
$way = $helper->ask($input, $output, $question);
$acmeClient->challengeAuthorization($authorizationChallenges[$way]);
$dn = new DistinguishedName($DOMAIN);
$keyPairGenerator = new KeyPairGenerator();
// Make a new key pair. We'll keep the private key as our cert key
$domainKeyPair = $keyPairGenerator->generateKeyPair();
// This is the private key
var_dump($domainKeyPair->getPrivateKey()->getPem());
// Generate CSR
$csr = new CertificateRequest($dn, $domainKeyPair);
$certificateResponse = $acmeClient->requestCertificate($DOMAIN, $csr);
// This is the certificate (public key)
var_dump($certificateResponse->getCertificate()->getPem());
// For Let's Encrypt, you will need the intermediate too
var_dump($certificateResponse->getCertificate()->getIssuerCertificate()->getPEM());
return self::SUCCESS;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace app\controller;
use support\Request;
use Respect\Validation\Validator as v;
class Apply
{
public function check(Request $request)
{
$email=$request->input('email',null);
$domain=$request->input('domain',null);
$cert=$request->input('cert',null);
if(!$email||!$domain||!$cert){
return json(['code'=>404,'msg'=>'缺少参数,请刷新重试']);
}
if(!in_array($cert,array('R3','Laysense','FWNET'))){
return json(['code'=>404,'msg'=>'证书类型错误或不存在']);
}
if(in_array($cert,array('Laysense','FWNET'))){
return json(['code'=>403,'msg'=>'当前证书类型'.$cert.'暂时无法颁发']);
}
if(!v::domain($innerDomain)->validate($domain))
return view('index');
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace app\controller;
use support\Request;
class IndexController
{
public function index(Request $request)
{
return view('index');
}
}
+4
View File
@@ -0,0 +1,4 @@
<?php
/**
* Here is your custom functions.
*/
+42
View File
@@ -0,0 +1,42 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
/**
* Class StaticFile
* @package app\middleware
*/
class StaticFile implements MiddlewareInterface
{
public function process(Request $request, callable $next): Response
{
// Access to files beginning with. Is prohibited
if (strpos($request->path(), '/.') !== false) {
return response('<h1>403 forbidden</h1>', 403);
}
/** @var Response $response */
$response = $next($request);
// Add cross domain HTTP header
/*$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
]);*/
return $response;
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace app\model;
use support\Model;
class Test extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'test';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}
+183
View File
@@ -0,0 +1,183 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta content="width=device-width, initial-scale=1" name="viewport">
<meta name="generator" content="pandoc" />
<title>Center ACME Auto SSL</title>
<style type="text/css">code{white-space: pre;}</style>
<link rel="stylesheet" href="normal.css" type="text/css" />
<link rel="stylesheet" href="sakura.css" type="text/css" />
<style>
body {
height: 100%;
}
.wrapper{
position:relative;
box-sizing:border-box;
min-height:100%;
padding-bottom:35px;
}
.footer{
position:absolute;
bottom:0;
left:0;
right:0;
height:35px;
}
</style>
</head>
<body>
<div class="wrapper">
<h1 id="sakura-a-minimal-classless-css-framework-theme.">
Center ACME Auto SSL(CAAS) by Laysense
</h1>
<p>[由 <a href="https://lab.laysense.cn/">来笙实验室</a> 出品]</p>
<blockquote>
<p>CAAS将打破你对ssl和acme的认识,现在只需要设置一次302重定向就可以直接申请到证书辣!</p>
</blockquote>
<button id="back" onclick="skipto('step1')">🔃重设</button>
<div id="step1" style="display: none;">
<center>
<h3>Step1:输入你需要申请SSL的域名</h3>
<input id="domain" type="url" /><br />
<h3>以及你的邮箱</h3>
<input id="email" type="email" />
<button onclick="skipto('step2')">👉下一步</button>
</div>
<div id="step2" style="display: none;">
<center>
<h3>Step2:选择一种证书类型</h3>
<ul>
<li>
<h5>正式使用、公共网站:</h5>
<button onclick="choose('R3')" style="padding:8px;">📃R3(3个月)</button><br>
(即Let's Encrypt)
</li><hr />
<li>
<h5>实验用途、私有网络:</h5>
<button onclick="skipto('step99')" style="background: linear-gradient(135deg, #c850c0, #4158d0);padding:15px;">💎来笙豪华娱乐证书(10年)</button>
</li>
<li>
<h5>5050net、fwnet内部使用</h5>
<button onclick="skipto('step999')">🌐FWNET证书</button>
</li>
</ul>
</center>
</div>
<div id="step3" style="display: none;">
<div width="50%" style="background-color: beige;">
当前证书品牌:<span class="showtype"></span> 域名:<span class="showdomain"></span>
</div>
<center>
<h3>最后一步!</h3>
<p>请将<pre>http://<span class="showdomain"></span>/.well-known/acme-challenge/</pre>重定向到 <pre>http://httpacme.lab.laysense.cn/acme/<span class="showdomain"></span></pre></p>
<button id="checkbutton" onclick="check()">😀我搞定了!</button><br />
<pre id="checknotice" style="background-color: antiquewhite;display: none;"></pre>
<hr><a>🧐不会操作? 查看文档!</a>
</center>
</div>
<div id="step4" style="display: none;">
<div width="50%" style="background-color: beige;">
当前证书品牌:<span class="showtype"></span> 域名:<span class="showdomain"></span>
</div>
<center>
<h3>😍激动人心的时刻</h3>
<button style="background: linear-gradient(135deg, #ff9a9e, #fad0c4);padding:12px;" onclick="skipto('step999')">获取证书</button>
</center>
</div>
<div id="step99" style="display: none;">
<center>
<h3>😳诶呀,所选证书品牌暂时不可用</h3>
<button id="back" onclick="skipto('step2')">🔃重选证书</button>
</center>
</div>
<div id="step999" style="display: none;">
<center>
<h3>😳诶呀,该证书品牌拒绝新的申请</h3>
<button id="back" onclick="skipto('step2')">🔃重选证书</button>
</center>
</div>
<hr />
<div class="footer">
<a>📥下载来笙根证书</a> <a href="https://5050net.cn/fwnet-ecdsa-root-ca-1.crt" target="_blank">📥下载FWNET根证书</a>
<a style="float: right;">📄文档</a>
<a style="float: right;">⏰自动续期</a>
</div>
</div>
<script src="jquery-3.7.1.js"></script>
<script>
changestep();
window.onhashchange=function(){
changestep();
}
function skipto(whichstep){
location.hash=whichstep;
changestep();
}
function choose(type){
window.certtype=type;
location.hash='step3';
changestep();
}
function changestep(){
var domain=$('#domain').val();
var step=location.hash;
if(typeof(step) == "undefined" || !step || !domain || typeof(domain) == "undefined"){
step='#step1';
}else if(step!='#step2'&& !window.certtype || typeof(window.certtype) == "undefined"){
step='#step2';
}else{
$('.showdomain').html(domain);
$('.showtype').html(window.certtype);
}
if(step=='#step4'&&(window.check!='pass'||!window.check)){
step='#step3';
}
//console.log(step);
$(window.laststep).hide();
$(step).show();
window.laststep=step;
}
function check(){
var domain=$('#domain').val();
var email=$('#email').val();
$.ajax({
type: "POST",
url: '/apply/check',
data: {'domain':domain,'email':email,'cert':window.certtype},
async: true,
dataType: 'json',
cache: false,
beforeSend: function () {
$('#checkbutton').attr("disabled","disabled");
$('#checkbutton').hide();
$('#checknotice').show();
$('#checknotice').html('📑检查中,请稍等');
},
success: function (data) {
if (data.code == 200) {
$('#checknotice').html('已通过校验');
changestep('step4');
window.check='pass';
} else {
$('#checkbutton').show();
$('#checknotice').html('校验未通过:'+data.msg);
$('#checkbutton').removeAttr("disabled");
}
},
clearForm: true,
resetForm: false
});
}
</script>
</body>
</html>