save
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use support\Request;
|
||||
|
||||
class IndexController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return view('index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user