本系列共三篇:
用 DNS Record 驗證 Let's Encrypt 後打包丟給 Web Server 提供下載 Part-1
用 DNS Record 驗證 Let's Encrypt 後打包丟給 Web Server 提供下載 Part-2
用 DNS Record 驗證 Let's Encrypt 後打包丟給 Web Server 提供下載 Part-3
承上一篇 用 DNS Record 驗證 Let's Encrypt 後打包丟給 Web Server 提供下載 Part-2
本篇是以 PowerShell 向 Web Server 自動下載憑證的階段 (雙向加密傳輸)
WebRoot/AAPI/Cert/GetCert.php on Web Server
#--------------------------------------------------------------------------------
<?php
header("HTTP/1.1 200 OK");
$Received_JsonContent = file_get_contents('php://input');
if (!function_exists('CloseConnection')) {
include($_SERVER['DOCUMENT_ROOT'].'/API/Cert/Config.php');
};
if (empty($Received_JsonContent)) {
CloseConnection();
exit;
};
ob_clean();
if (!isset($CertPackagesPath)) {
include($_SERVER['DOCUMENT_ROOT'].'/Cert/Config.php');
};
if ( (!isset($GetCertPrivateKey)) || (!isset($GetCertPublicKey)) ) {
include($_SERVER['DOCUMENT_ROOT'].'/API/Cert/Config.php');
};
$ReceivedData = json_decode($Received_JsonContent,true);
$Request = strtolower(myDecrypt($ReceivedData['Name'],$GetCertPublicKey));
$Auth = strtolower(myDecrypt(Substr(myDecrypt($ReceivedData['Auth'],$GetCertPublicKey),16),$GetCertPrivateKey));
if ($Request != $Auth) {
CloseConnection();
exit;
};
$CertPackages = array_diff(scandir($CertPackagesPath), array('.', '..'));
$LastPackages = array();
foreach ($CertPackages as $CertPackage) {
if (substr($CertPackage, -3) != '.7z') {
Continue;
};
$Cert = explode('_',$CertPackage);
if (strtolower($Cert[0]) != $Auth) {
Continue;
};
$CertExpireDateTime = substr($Cert[1],0,4).'-'.substr($Cert[1],4,2).'-'.substr($Cert[1],6,2).' '.substr($Cert[2],0,2).':'.substr($Cert[2],2,2).':'.substr($Cert[2],4,2);
$CertExpireTimestamp = strtotime(substr($Cert[1],0,4).'-'.substr($Cert[1],4,2).'-'.substr($Cert[1],6,2).' '.substr($Cert[2],0,2).':'.substr($Cert[2],2,2).':'.substr($Cert[2],4,2));
if (isset($LastPackages[$Cert[0]]['ExpireDateTime'])) {
if ($CertExpireTimestamp > $LastPackages[$Cert[0]]['ExpireDateTime']) {
$LastPackages[$Cert[0]]['FileName'] = $CertPackage;
$LastPackages[$Cert[0]]['CertExpireDateTime'] = $CertExpireDateTime;
$LastPackages[$Cert[0]]['ExpireDateTimestamp'] = $CertExpireTimestamp ;
};
} else {
$LastPackages[$Cert[0]]['FileName'] = $CertPackage;
$LastPackages[$Cert[0]]['CertExpireDateTime'] = $CertExpireDateTime;
$LastPackages[$Cert[0]]['ExpireDateTimestamp'] = $CertExpireTimestamp ;
};
};
$LastPackages = array_change_key_case_recursive($LastPackages,CASE_LOWER);
$LastPackages = array_map('nestedLowercase', $LastPackages);
if (Empty($LastPackages)) {
CloseConnection();
exit;
};
if (Count($LastPackages) != 1) {
CloseConnection();
exit;
};
$FP = fopen($CertPackagesPath.$LastPackages[$Auth]['filename'],"rb");
$FileSize = filesize($CertPackagesPath.$LastPackages[$Auth]['filename']);
$Data = base64_encode(fread($FP, $FileSize));
fclose($FP);
$SendData['FileName'] = myEncrypt($LastPackages[$Auth]['filename'], $GetCertPublicKey);
$SendData['Data'] = myEncrypt($Data, $GetCertPublicKey);
echo json_encode($SendData, JSON_UNESCAPED_UNICODE);
exit;
Function myEncrypt($Data, $EncryptKey) {
$AESBlockSize = 128;
$iv = RandomPassword(($AESBlockSize/8));
$Encrypted = openssl_encrypt($Data, 'aes-256-cbc', $EncryptKey, 0, $iv);
return base64_encode($iv.$Encrypted);
};
Function myDecrypt($Data, $EncryptKey) {
$AESBlockSize = 128;
$DataDecode = base64_decode($Data);
$iv = substr($DataDecode,0,($AESBlockSize/8));
$Encrypted_data = substr($DataDecode,($AESBlockSize/8));
return openssl_decrypt($Encrypted_data, 'aes-256-cbc', $EncryptKey, 0, $iv);
};
Function RandomPassword($PasswordLength=16) {
$Alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$Password = Array();
$AlphaLength = strlen($Alphabet) - 1;
for ($i = 0; $i < $PasswordLength; $i++) {
$Password[] = $Alphabet[(rand(0, $AlphaLength))];
};
return implode($Password);
};
?>
#--------------------------------------------------------------------------------
GetCert.ps1 on Client
#--------------------------------------------------------------------------------
#Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force
# input cert path (.pem .pfx)
# ADD '\' at the end of path like: 'C:\Temp\'
$GetCertPath = 'C:\Cert\Cert\';
# input 7zip program fullpath
# https://www.7-zip.org/download.html
$SevenZipProgram = 'C:\Program Files\7-Zip\7z.exe';
#----------------
# Do not change this section
# initial array
$DNSNames = @();
$Auths = @();
# Do not change this section
# when www.contoso.com, input www
$DNSNames += 'www';
# input AuthCode got from https://www.contoso.com/Cert/
$Auths += 'oooxxx==';
#---------------- #Do not change the section below ----------------
$URL = 'https://www.contoso.com/AAPI/Cert/GetCert.php';
$PublicKey = 'wEYlvurP8OCtp4AHUSe9BqiMDVXIo7Fz';
#----------------
$DateTimeString = Get-Date -format yyyyMMdd_HHmmss
$TranscriptLog = (Get-Item -Path ".\" -Verbose).FullName + "\Logs\" + ("GetCert_" + $DateTimeString + ".log")
start-transcript -path $TranscriptLog
Function RandomPassword {
param (
[int]$PasswordLength = 16
);
PROCESS {
return (("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".tochararray() | sort {Get-Random})[0..($PasswordLength - 1)] -join '');
};
};
#----------------
Function myEncrypt {
param (
[String]$Data,
[String]$EncryptKey
);
PROCESS {
$Encode = [System.Text.Encoding]::UTF8;
$DataBytes = $Encode.GetBytes($Data);
$AES = New-Object System.Security.Cryptography.AESManaged;
$AES.BlockSize = 128;
$AES.KeySize = 256;
$AES.Mode = [System.Security.Cryptography.CipherMode]::CBC;
$iv = RandomPassword(($AES.BlockSize / 8));
$AES.IV = $Encode.GetBytes($iv);
$AES.Key = $Encode.GetBytes($EncryptKey);
$Encryptor = $AES.CreateEncryptor();
$EncryptedBytes = $Encryptor.TransformFinalBlock($DataBytes, 0, $DataBytes.length);
return ([Convert]::ToBase64String($Encode.GetBytes($iv + ([Convert]::ToBase64String($EncryptedBytes)))));
};
};
#----------------
Function myDecrypt {
param (
[String]$Data,
[String]$EncryptKey
);
PROCESS {
$Encode = [System.Text.Encoding]::UTF8;
$DataString = $Encode.GetString([Convert]::FromBase64String($Data));
$AES = New-Object System.Security.Cryptography.AESManaged;
$AES.BlockSize = 128;
$AES.KeySize = 256;
$AES.Mode = [System.Security.Cryptography.CipherMode]::CBC;
#$AES.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$iv = $DataString.SubString(0,($AES.BlockSize / 8));
$AES.IV = $Encode.GetBytes($iv);
$AES.Key = $Encode.GetBytes($EncryptKey);
$Decryptor = $AES.CreateDecryptor();
$DataBytes = [Convert]::FromBase64String($DataString.SubString(($AES.BlockSize / 8)));
return $Encode.GetString($Decryptor.TransformFinalBlock($DataBytes, 0, $DataBytes.Length));
};
};
#----------------
$KeySize = 256;
$EncryptKey = RandomPassword(($KeySize / 8))
for ($i=0; $i -lt $DNSNames.Length; $i++) {
$POST = ([PSCustomObject]@{
Name = (myEncrypt $DNSNames[$i] $PublicKey);
Auth = (myEncrypt ((RandomPassword(16)) + [string]$Auths[$i]) $PublicKey);
}) | ConvertTo-Json -Compress;
$ReceivedData = (Invoke-RestMethod -Method 'Post' -Uri $URL -ContentType 'application/json' -Body $POST);
write-host $ReceivedData
$FileName = ($GetCertPath + (myDecrypt ($ReceivedData.FileName) $PublicKey));
[System.IO.File]::WriteAllBytes($FileName, [Convert]::FromBase64String((myDecrypt ($ReceivedData.Data) $PublicKey)))
};
$CommandLine = """" + $SevenZipProgram + """ x """ + $FileName + """ -o""" + $GetCertPath + """ -y"
$CommandLine
cmd /c $CommandLine
Remove-Item -Path $FileName -Force
Stop-Transcript
#--------------------------------------------------------------------------------
註: Apache 路徑設定 Source IP Filter 範例 (httpd-ssl.conf)
<Directory "${SITEROOT}/www.contoso.com/API">
Require all granted
order deny,allow
deny from all
allow from 192.168.1.1/32
allow from 192.168.10.0/24
</Directory>
<Directory "${SITEROOT}/www.contoso.com/AAPI">
Require all granted
order deny,allow
allow from all
allow from 192.168.10.0/24
</Directory>
沒有留言:
張貼留言