Feeds:
Записи
Комментарии

Archive for the ‘PowerShell’ Category

Оператор -replace в PowerShell — является упрощенной реализацией метода Replace класса System.Text.RegularExpressions.Regex –> альяс в [regex]::Replace.

Реализация оператора –replace в PowerShell Core методом internal static object ReplaceOperator.

Синтаксис:

PowerShell Desktop

          input –[ci]replace pattern, [replacement]

PowerShell Core

          input –[ci]replace pattern, [replacement]

          input –replace pattern, [evaluator]

  [] – необязательные аргументы

   input – входная строка или массив строк

   pattern – шаблон регулярного выражения

   replacement – строка замены

   evaluator — пользовательский метод, анализирующий каждое совпадение и возвращающий либо исходную строку с совпадениями, либо строку замены.

 

Большинство –op операторов, представлены в двух версиях:

-creplace : с учетом регистра (case sensitive (-cop)) , указывается опция RegexOptions.None

-ireplace = replace : без учета регистра (case-insensitive (-iop)) , указывается опция RegexOptions.IgnoreCase

Пару условных правил:

  1. -replace не изменяет первоначальную строку, а возвращает новый объект строки в измененном варианте
  2. при несовпадении шаблона, строка возвращается как есть
  3. при отсутствии шаблона используется ""

Примеры:

# input -[ci]replace pattern

PS > "Hello" -replace "ll"
Heo

PS > "Hello" -ireplace "h"
ello

PS > "Hello" -creplace "h"
Hello

# input –[ci]replace pattern, [replacement]

PS > "aabc" -replace "a","b"
bbbc

PS > "Abcd" -ireplace "a","B"
Bbcd

PS > "Abcd" -creplace "[a-z]","B"
ABBB
  

В PowerShell Core добавили  параметр evaluator, который может принимать следующие значения:

  1. ScriptBlock
  2. Объект класса [System.Text.RegularExpressions.MatchEvaluator]
  3. Пользовательский класс с статическим методом

ScriptBlock, каждое совпадение по шаблону сохраняется в переменной $_ или $psitem.

$_.GetType().FullName — System.Text.RegularExpressions.Match

PS > "A1B2C3" -replace "\d", {[int]$_[0].Value*2}
A2B4C6

PS > "A1B2C3" -replace "\d", {$psitem[0].Value*3}
A111B222C333


PS > $_[0]


Groups   : {0}
Success  : True
Name     : 0
Captures : {0}
Index    : 5
Length   : 1
Value    : 3

PS > $_[0].Value
3
  



MatchEvaluator, каждое совпадение по шаблону сохраняется в переменной $args или param($var).



PS > $matchEvaluator = {[int]$args[0].Value*2} -as [System.Text.RegularExpressions.MatchEvaluator]
PS > "A1B2C3" -replace "\d", $matchEvaluator
A2B4C6

PS > $matchEvaluator = [System.Text.RegularExpressions.MatchEvaluator]{param($m) [int]$m[0].Value*3}
PS > "A1B2C3" -replace "\d", $matchEvaluator
A3B6C9
  



Пользовательский класс с статическим методом, каждое совпадение по шаблону сохраняется в переменной MethodName([ObjectClass]$var).



PS >
class matchEvaluator {
    static [string] Replace([System.Text.RegularExpressions.Match]$m) {
	return [int]$m[0].Value*3
    }
}

"A1B2C3" -replace "\d", [matchEvaluator]::Replace

A3B6C9
  

В PowerShell Desktop evaluator можно использовать через [regex]::Replace.

PS >  [regex]::Replace("A1B2C3","\d",{[int]$args[0].Value*2})
A2B4C6
PS >  [regex]::Replace("A1B2C3","\d",{param($m) [int]$m[0].Value*3})
A3B6C9
  

Для массива строк мы можем использовать аналогичные действия:

PS > "1abc","2qwe","3rty" -replace "\d"
abc
qwe
rty

  

PS > "1abc","2qwe","3rty" -replace "\d","X"

Xabc

Xqwe

Xrty

PS > "1abc","2qwe","3rty" -replace "\d",{[int]$_[0].Value*2}
2abc
4qwe
6rty
  

Подстановки в регулярных выражениях подробно описано на docs.microsoft.com,я лишь приведу примеры использования.

* Для экранирования символа подстановки $ используйте ` (backtick) – "`$" если используются двойные кавычки

   ‘$’ – не требуется экранирования

   "`$" – экранирование

   ("Abc`$1" + ‘$1’ + "Xyz") – комбинирование



# $number The text matched by group number number.
# $0 - весь шаблон
# $1 - первая группа
# $2 - вторая группа 

PS > "111-2222" -replace "(\d{3})-(\d{4})",'($1) : $2'
(111) : 2222

PS > "111-2222" -replace "(\d{3})-(\d{4})","(`$1) : `$2"
(111) : 2222

PS > "111-2222" -replace "(\d{3})-(\d{4})","'`$`$1 - `$1' : `$`$2 - '`$2'"
'$1 - 111' : $2'2222'

PS > "String" -replace "(.+)ing",("Abc" + '$1' + "Xyz")
AbcStrXyz

# ${name} The text matched by group named name.

PS > "Dog name" -replace "^(?<dog>.{3}).+",'${dog} pop'
Dog pop

# $$ A literal $.
  

PS > "1234" -replace "\d",'$$'
$$$$

#$& A copy of the entire match.
  

PS > "Moscow" -replace "^.*$",'Capital: $&'
Capital: Moscow

#$` The text of the input string that precedes the match.
  

PS > "PreText" -replace "Text$",'$`'
PrePre

#$' The text of the input string that follows the match.
  

PS > "PreText" -replace "Pre",'$'''
TextText

#$+ The last group captured.
  

PS > "PreText" -replace "(.+)Text",'$+Now'
PreNow

#$_ The entire input string.
  

PS > "String" -replace "(.*)ing",'Entire String: $_'
Entire String: String
  
Реклама

Read Full Post »

Тема была навеяна вопросом с форума, где автор хотел немного странного. С получением подключений в текущей сессии пользователя никаких сложностей не возникает. Для это можно воспользоваться классом Win32_MappedLogicalDisk:

ld26042018

Основная сложность – это получить из удаленной сессии пользователя. При указании параметра –ComputerName , данные вернутся из контекста пользователя, который выполняет команду, а не вошедшего в систему через консольный сеанс.

Поэтому предварительно :

  • Права эквивалентные локальному администратору на удаленном ПК
  • Настроен PowerShell Remoting
  • Пользователь в консольном сеансе

Часть информации можно получить из реестра при определенных условиях:

HKCU:\Network\ — Содержит диски подключенные с указанием флага PERSISTEN

HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\##* — Может содержать и актуальную,и неактуальную информацию

Т.е. актуальную информацию из реестра получить будет непросто,но возможно, кому-то помогут данные ключи.

Для удаленного доступа к ключам реестра потребуется запросить:

Registry::HKEY_USERS\SID\

Основная идея скрипта будет заключаться в создании задачи в планировщике задач от имени пользователя.

  • Invoke-Command для выполнения скрипта на удаленном ПК
  • Schedule.Service для отслеживания результата выполнения задачи
  • schtasks.exe простой способ создать задачу

Function Get-MappedLogicalDisk {
	[CmdletBinding()]
	Param (
		[Parameter(ParameterSetName='ComputerName', Position=0)]
		[ValidateNotNullOrEmpty()]
		[String[]]$ComputerName,
		
		[Parameter(ParameterSetName='Session', Position=0)]
		[ValidateNotNullOrEmpty()]
		[System.Management.Automation.Runspaces.PSSession[]]
		$Session,
		
		[Parameter(ParameterSetName='ComputerName')]
		[System.Management.Automation.PSCredential]
		[System.Management.Automation.CredentialAttribute()]
		$Credential,
		
		[Parameter(ParameterSetName='ComputerName')]
		[ValidateRange(1, 65535)]
		[Int]$Port,
		
		[Parameter(ParameterSetName='ComputerName')]
		[Switch]$UseSSL,
		
		[Parameter(ParameterSetName='ComputerName')]
		[String]$ConfigurationName,
		
		[Parameter(ParameterSetName='ComputerName')]
		[System.Management.Automation.Runspaces.AuthenticationMechanism]
		$Authentication,
		
		[Parameter(ParameterSetName='ComputerName')]
		[String]$CertificateThumbprint
	)

	$script = {
		# Текущий пользователь в системе выполнивший вход через консольный сеанс
		$user = (Get-WmiObject -Class Win32_ComputerSystem).UserName
		# Название задачи
		$tn = "GetMappedLogicalDisk"
		# Папка для временного файла
		$logdir = "C:\LogMAP"
			
		# Команда для получения сетевых дисков в контексте пользователя
		# Возможно ограничение в 260 символов 
		$arg = '&{{New-Item -Path {0} -Type Directory -Force;Get-WmiObject Win32_MappedLogicalDisk | Export-Clixml {0}\$env:UserName.xml}}' -f $logdir
		$cmd = 'powershell.exe -WindowStyle Hidden -NoProfile -NoLogo -Command {0}' -f $arg
					
		if($user) {
			# Создаем ComObject для работы с Task Scheduler
			$sch = New-Object -ComObject Schedule.Service
			$sch.Connect()
			# Корневая директория для заданий
			$taskdir = $sch.GetFolder("\")
			
			# Удаляем прежние данные
			Remove-Item -Path $logdir -Force -Recurse -ErrorAction SilentlyContinue
				
			# Создаем задачу в контексте пользователя
			$null = schtasks.exe /create /TN $tn /ST 00:00 /RU $user /SC ONCE /TR $cmd /f 2>&1
			# Запускаем задачу
			$null = $taskdir.GetTask($tn).Run($null)
				
			# Проверим завершение задачи независимо от локализации ОС
			$maxcount = 0
			While($taskdir.GetTask($tn).State -ne 3 -and $maxcount -lt 60) {
				$maxcount++
				Start-Sleep -Sec 1
			}
				
			# Получаем результат
			Import-CliXML $logdir\*.xml | Add-Member -Type NoteProperty -Name User -Value $user -PassThru
				
			# Удаляем временную задачу и файлы
			Remove-Item -Path $logdir -Force -Recurse -ErrorAction SilentlyContinue
			$null = $taskdir.DeleteTask($tn,0)
		} else {
			"В системе нет пользователей выполнивших вход!"
		}
	}
		
	Invoke-Command @PSBoundParameters -ScriptBlock $script
}
  

Примерный вывод:

PS > Get-MappedLogicalDisk -ComputerName con-desk-01 -Credential "contoso\admin" | Format-Table Name,ProviderName,FileSystem,FreeSpace,Size -AutoSize

  User        Name ProviderName      FileSystem   FreeSpace     Size
  ----        ---- ------------      ----------   ---------     ----
  CON\ivanov  Y:   \\fs01\Документы  NTFS         775772479488  1000198893568
  CON\ivanov  Z:   \\fs02\Фото       NTFS         775772479488  1000198893568
  

Скрипт — GetMappedLogicalDiskPowerShellRemoting.txt

Read Full Post »

Сегодня анонсировали возможность получения бесплатного wildcard-сертификата от Let’s Encrypt сроком на 3 месяца — ACME v2 and Wildcard Certificate Support is Live . Технические подробности можно узнать – ACME v2 Production Environment & Wildcards.

  • Требуется повторная проверка домена для ACME v2
  • Учетные записи для V1 или V2 staging environment(тестовых сред) не будут работать в рабочей среде
  • ACME v2  поддерживает не более 300 запрос в течение 3 часов
  • Проверка осуществляется только с помощью DNS-01 challenge

В прошлый раз использовали модуль ACMESharp, на данный момент он не поддерживает ACME v2, но я думаю в скором времени, все будет поддерживаться. В библиотеке certes реализована поддержка  ACME v2, ее и  будем использовать.

Для тренировки получения сертификатов указывайте [Certes.Acme.WellKnownServers]::LetsEncryptStagingV2 , в этом случае издатель будет   — Fake LE Intermediate and Root X1. Для рабочей среды – [Certes.Acme.WellKnownServers]::LetsEncryptV2.

!! Создания записи _acme-challenge.contoso.com для DNS ,в нашем примере — Windows DNS Server !!

 

 

# Path - где храним библиотеки            
# Mail - почта администратора            
# Domain - домен для которого получаем сертификат            
# PfxPassword - пароль для Pfx            
$Path = "C:\certes"            
$Mail = "mailto:admin@contoso.ru"            
$Domain = "*.contoso.ru"            
$PfxFriendlyName = "CONTOSO_{0}" -f (Get-Date).AddMonths(3).ToString("MMyy")            
$PfxPath = Join-Path $Path "wildcard_cert.pfx"            
$PfxPassword = "12345678"            
            
# Ссылки для скачивания nupkg            
$Urls = (            
 "https://www.nuget.org/api/v2/package/Certes",            
 "https://www.nuget.org/api/v2/package/BouncyCastle.Crypto.dll",            
 "https://www.nuget.org/api/v2/package/Newtonsoft.Json"            
)            
            
# Указываем библиотеки для проверки            
$Dlls = (            
 "$Path\lib\net45\Certes.dll",            
 "$Path\lib\BouncyCastle.Crypto.dll",            
 "$Path\lib\net45\Newtonsoft.Json.dll"            
)            
            
            
# Создаем папку            
if( -not (Test-Path $path) ) {            
 New-Item -Type Directory -Path $path | Out-Null            
}            
            
Test-Path $Dlls | Where-Object {-not $_} | Foreach-Object {             
 $Urls | Foreach-Object {            
  # Скачиваем nupkg            
  $r = Invoke-WebRequest $_ -UseBasicParsing            
  $filename = Join-Path $path $r.BaseResponse.ResponseUri.Segments[-1]            
  [System.IO.File]::WriteAllBytes($filename,$r.Content)            
            
  # Распаковываем nupkg            
  Expand-Archive $filename  -OutputPath $path -Force            
 }            
}            
            
# Удалим лишнее            
Get-ChildItem $path | Where {$_.Name -ne "lib"} | Remove-Item -Force -Recurse            
            
# Подгрузи библиотеки            
$Dlls | Foreach-Object {            
 [System.Reflection.Assembly]::LoadFile($_) | Out-Null            
}            
            
            
# Создадим учетную запись            
# Для тестового использования LetsEncryptStagingV2            
$context = [Certes.AcmeContext]::new([Certes.Acme.WellKnownServers]::LetsEncryptV2)            
$context           
$account = $context.NewAccount([System.Collections.Generic.List[string]]$Mail, $true)            
$account.Result            
            
# Формируем запрос на получение wildcard            
$order = $context.NewOrder([Collections.Generic.List[string]]$Domain)            
$authz = $order.Result.Authorizations()            
$dnsChallenge = $authz.Result.Resource().Result.Challenges            
            
# Содержит token для DNS записи _acme-challenge.contoso.com            
$dnsTxt = [Certes.ISignatureKeyExtensions]::DnsTxt($context.AccountKey,$dnsChallenge.Token)            
            
# Создаем ресурсные записи             
# Windows DNS Server            
$DomainName = $Domain.Replace("*.","")            
$Name = "_acme-challenge"            
$record = Get-DnsServerResourceRecord -ZoneName $DomainName -Name $Name -RRType Txt            
            
if($record)            
{            
 $newrecord = $record.Clone()            
 $newrecord.RecordData.DescriptiveText = $_.RecordValue            
 Set-DnsServerResourceRecord -ZoneName $DomainName -NewInputObject $newrecord -OldInputObject $record            
}            
else            
{            
 Add-DnsServerResourceRecord -ZoneName $DomainName -Txt -Name $Name -DescriptiveText $dnsTxt            
}            
            
            
# Получаем сертификат            
if($dnsTxt) {            
 # Отправляем запрос на проверку DNS записи            
 $validatetatus = $authz.Result.Challenges().Result.Validate()            
 $validatetatus.Result            
             
 # Finalize            
 $csr = [Certes.Pkcs.CertificationRequestBuilder]::new()            
 $csr.AddName("CN=$Domain")            
 $order.Result.Finalize($csr.Generate())            
             
 # Скачиваем цепочку сертификатов            
 $certChain = $order.Result.Download()            
 $cert = $certChain.Result            
              
 # Экспорт в Pfx                        
 $pfx = [Certes.CertificateChainExtensions]::ToPfx($cert,$csr.Key)            
 $pfxcert = $pfx.Build($PfxFriendlyName, $PfxPassword)            
 [System.IO.File]::WriteAllBytes($PfxPath,$pfxcert)            
}            
            
# Отзыв сертификата            
# $context.RevokeCertificate($cert.Certificate.ToDer(), [Certes.Acme.Resource.RevocationReason]::KeyCompromise,$null)

context - 140318

account - 140318

 

order - 140318

order_answ - 140318

dnschall - 140318

dnschall_ans -140318

cert - 140318

 

PS. letsencrypt_wildcard_certes.txt

Read Full Post »

Для перемещения формы, у которой свойство FormBorderStyle=None, воспользуемся советом из топика – "Перемещение формы без границ — C#" за авторством mak326428.

#Requires -PSEdition Desktop            
            
using assembly System.Windows.Forms            
using namespace System.Windows.Forms            
            
$form = [Form]@{            
 FormBorderStyle = "None"            
}            
            
$form.Add_MouseDown({            
 $form.Capture = $false            
 $m = [Message]::Create($form.Handle, 0xa1, (New-Object IntPtr(2)), [IntPtr]::Zero)            
 $form.WindowTarget.DefWndProc([ref]$m)            
})            
            
$form.ShowDialog()
moveform

Скрипт — MoveFormBorderStyleNone.ps1

Read Full Post »

В наборе  Windows Server 2003 Resource Kit Tools , есть утилита tsctst.exe — Terminal Server Client License Dump , для просмотра клиентских лицензий сервера терминалов. Основное ограничение на системах x64, что tsctst.exe –  читает раздел реестра — HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\MSLicensing\Store , где отсутствует данная информация.

Если хочется воспользоваться данной утилитой, то можно скопировать/импортировать данные из HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\MSLicensing\Store и перенести в раздел :  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSLicensing\Store и утилита вновь будет работать.

Нас будет интересовать ключ реестра LicenseXXX\ClientLicense(REG_BINARY), где содержится информация о сертификатах.

Для получения данных о сертификате , воспользуемся(для примера, приведу  два способа):

1) System.Security.Cryptography.Pkcs.SignedCms

Add-Type -AssemblyName System.Security            
$license = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\MSLicensing\Store\LICENSE000" -Name "ClientLicense"            
$cms = New-Object System.Security.Cryptography.Pkcs.SignedCms            
$cms.Decode($license.ClientLicense)            
$cms.Certificates             
            
PS > $cms.Certificates

Thumbprint                                Subject
----------                                -------
785F491C579B6421FE155EFB6E6482F1E44FC266  L=CON + CN=SBTR
B50C54A586ADC8FA249639B7A06E3CE4B2C24070  L=SYSTEM + CN=CL-01 + SERIALNUMBER="1BcKee1dW/BhjacbI9tTm0hPCQ

2) System.Security.Cryptography.X509Certificates.X509Certificate2Collection

$certs = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new()            
$certs.Import($license.ClientLicense)            
$certs            
            
PS > $cms.Certificates

Thumbprint                                Subject
----------                                -------
785F491C579B6421FE155EFB6E6482F1E44FC266  L=CON + CN=SBTR
B50C54A586ADC8FA249639B7A06E3CE4B2C24070  L=SYSTEM + CN=CL-01 + SERIALNUMBER="1BcKee1dW/BhjacbI9tTm0hPCQ

Можно сохранить данные в файл и посмотреть, через оснастку — certmgr.msc:

$license.ClientLicense | Set-Content certs.p7b -Encoding Byte

Утилита tsctst.exe читает следующие OID:

Microsoft Hydra……………………………1.3.6.1.4.1.311.18

     License Info root

        szOID_PKIX_LICENSE_INFO                 1.3.6.1.4.1.311.18.1

     Manufacturer value

        szOID_PKIX_MANUFACTURER                 1.3.6.1.4.1.311.18.2

     Manufacturer Specfic Data

        szOID_PKIX_MANUFACTURER_MS_SPECIFIC     1.3.6.1.4.1.311.18.3

     OID for Certificate Version Stamp

        szOID_PKIX_HYDRA_CERT_VERSION           1.3.6.1.4.1.311.18.4

     OID for License Server to identify licensed product.

        szOID_PKIX_LICENSED_PRODUCT_INFO        1.3.6.1.4.1.311.18.5

     OID for License Server specific info.

        szOID_PKIX_MS_LICENSE_SERVER_INFO       1.3.6.1.4.1.311.18.6

     Extension OID reserved for product policy module — only one is allowed.

        szOID_PKIS_PRODUCT_SPECIFIC_OID         1.3.6.1.4.1.311.18.7

        szOID_PKIS_TLSERVER_SPK_OID             1.3.6.1.4.1.311.18.8

Вывод утилиты:

PS > tsctst.exe /A

*** License # 1 ***

TS Certificate Version — 0x00050001

Licensed Product

        HWID — 0x00000002, 0x7d4eb2c0, 0x37ffa910, 0x75eb542b, 0x87707af6

        Client Platform ID — 0x000000ff

        Company Name — Microsoft Corporation

Issuer — SBTR

Scope — COM

Issued to machine — CL-01

Issued to user — SYSTEM

        TS Locale ID — 0x00000419

        License ID — A02-5.02-S

Licensed Product Version 0005.0002, Flag 0x80d48000

Temporary       RTM

Valid from — 1d30f66 8ff2a100 Mon Aug 07 13:18:50 2017

Expires on — 1d3561f 79202100 Sun Nov 05 13:18:50 2017

Поля:

Issuer,Scope,Issued to machine,Issued to user -  Subject,Issuer

TS Certificate Version — 1.3.6.1.4.1.311.18.4(szOID_PKIX_HYDRA_CERT_VERSION)

Licensed Product — 1.3.6.1.4.1.311.18.2(szOID_PKIX_MANUFACTURER),

                 1.3.6.1.4.1.311.18.6 (szOID_PKIX_MS_LICENSE_SERVER_INFO)

TS Locale ID  -   1.3.6.1.4.1.311.18.5( szOID_PKIX_LICENSED_PRODUCT_INFO)

License ID — 1.3.6.1.4.1.311.18.5( szOID_PKIX_LICENSED_PRODUCT_INFO)

Licensed Product Version — 1.3.6.1.4.1.311.18.5( szOID_PKIX_LICENSED_PRODUCT_INFO)

Valid from – NotBefore

Expires on – NotAfter

Function Get-TSCtst {            
 Param(            
  $Path = "HKLM:\SOFTWARE\Microsoft\MSLicensing\Store"            
 )            
             
 $Certs = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection            
 $License = Get-ChildItem $Path | Get-ItemProperty -Name ClientLicense             
             
 $LicenseType = @{            
  "A02-5.00-EX" = 'Windows 2000 TS CAL from the "Built-in" pool.'             
  "A02-5.00-S"  = "Windows 2000 TS temporary or permanent CAL."             
  "A02-5.02-S"  = "Windows server 2003 TS temporary or permanent CAL."             
  "A02-6.00-S"  = "Windows server 2008 TS/2008 R2 RDS temporary or permanent CAL."             
 }            
             
 if(-not $License)             
 {            
  "Can't decode license"            
  break            
 }            
             
 foreach($l in $License)             
 {            
  $Certs.Import($l.ClientLicense)            
 }            
             
            
            
 $Certs = $Certs.Where{$_.Subject -match "SERIALNUMBER"}            
 $Number = 1            
             
 foreach($Cert in $Certs)            
 {            
  #Info            
  [byte[]]$info = $Cert.Extensions | Where{$_.Oid.Value -eq '1.3.6.1.4.1.311.18.5'} | Foreach {$_.RawData}             
  #TSVer            
  [byte[]]$btsver = $Cert.Extensions | Where{$_.Oid.Value -eq '1.3.6.1.4.1.311.18.4'} | Foreach {$_.RawData}            
  [Array]::Reverse($btsver)            
  $TSVer = "0x$([BitConverter]::ToString($btsver).replace('-',''))"            
  #Compnay            
  $bcompany = $Cert.Extensions | Where {$_.Oid.Value -eq '1.3.6.1.4.1.311.18.2'} | Foreach {$_.RawData}            
  $Company = [Text.Encoding]::Unicode.GetString($bcompany)            
  #Product            
  $CId = "0x$([BitConverter]::ToString($info[5..8]).Replace('-','').ToLower())"            
  #Issuer,Scope            
  $Scope,$Issuer = $Cert.Issuer -split "\s\+\s" | Foreach {$_.split("=")[1]}            
  #User,Machine Issuer            
  $UIssuer,$MIssuer = $Cert.Subject -split "\s\+\s" -match "^(L|CN)" | Foreach {$_.split("=")[1]}            
  #Locale            
  $bid = $info[12..13]            
  [Array]::Reverse($bid)            
  $TSId = "0x$([BitConverter]::ToString($bid).Replace('-','').PadLeft(8,'0'))"            
  #License            
  [string]$LicStr = [Text.Encoding]::Unicode.GetString($info) -split "\0" -match "A02-"            
  #License Type            
  $LicType = $LicenseType[$LicStr]            
  #Licensed Product Version            
  $LPV = "{0}.{1}, Flag 0x{2}" -f [BitConverter]::ToString($info,58,1).PadLeft(4,'0'),            
   [BitConverter]::ToString($info,60,1).PadLeft(4,'0').ToLower(),            
   [BitConverter]::ToString($info,63,4).Replace('-','').ToLower()            
  #HWID            
  $RegHWID    = "HKLM:\SOFTWARE\Microsoft\MSLicensing\HardwareID"            
  $ClientHWID = (Get-ItemProperty $RegHWID).ClientHWID            
  $HexClientHWID = for($i=0;$i -le $ClientHWID.Count -1 ; $i+=4) {            
   $temp = $ClientHWID[$i..($i+3)]            
   [Array]::Reverse($temp)            
   "0x$([BitConverter]::ToString($temp).Replace('-','').ToLower())"            
  }            
  $HexClientHWID = $HexClientHWID -join ', '            
              
  [PSCustomObject]@{            
   "ID"        = $Number            
   "TS Certificate Version"   = $TSVer            
   "HWID"         = $HexClientHWID            
   "Client Platform ID"       = $CId            
   "Company Name"             = $Company            
   "Issuer"        = $Issuer            
   "Scope"         = $Scope            
   "Issued to machine"     = $MIssuer            
   "Issued to user"      = $UIssuer            
   "TS Locale ID"       = $TSId            
   "License ID"       = $LicStr            
   "License Type"      = $LicType            
   "Licensed Product Version" = $LPV            
   "Valid from"       = $Cert.NotBefore            
   "Expires on"       = $Cert.NotAfter            
  }            
              
  $Number++            
 }            
}            
            

ID                       : 1
TS Certificate Version   : 0x00050001
HWID                     : 0x00000002, 0x7d4eb2c0, 0x37ffa910, 0x75eb542b, 0x87707af6
Client Platform ID       : 0x000000ff
Company Name             : Microsoft Corporation
Issuer                   : SBTR
Scope                    : CON
Issued to machine        : CL-01
Issued to user           : SYSTEM
TS Locale ID             : 0x00000419
License ID               : A02-5.02-S
License Type             : Windows server 2003 TS temporary or permanent CAL.
Licensed Product Version : 0005.0002, Flag 0x80d48000
Valid from               : 8/7/2017 1:18:50 PM
Expires on               : 11/5/2017 1:18:50 PM

PS. Скрипт Get-TSCtst.ps1 .

Read Full Post »

Для симуляции различных ошибок  проверки сертификатов, существует прекрасный сайт — https://badssl.com/ , который предоставляет отличный набор тестов. В PowerShell 6.0 у командлетов Invoke-WebRequest/Invoke-RestMethod появился параметр –SkipCertificateCheck . Версия PowerShell v2.0  — Windows PowerShell 2.0 Deprecation. В PowerShell v3-5.1 — отсутствуют параметры для игнорирования ошибок связанных с сертификатами, поэтому воспользуемся сторонними методами.

Быстрый поиск в Google по теме игнорирование ошибок связанный с сертификатами дает большое количество различный способов.

Все примеры для PowerShell v5.1 и https://expired.badssl.com/

1 – 2 – не работают

3 – работает частично

4 – 5 – работают

 

Первоначальная ошибка:

er_06092017

 

 

1. [Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }            
Invoke-RestMethod https://expired.badssl.com/            
       
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At line:1 char:1
+ Invoke-RestMethod https://expired.badssl.com/
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
            
2. https://blogs.technet.microsoft.com/bshukla/2010/04/12/ignoring-ssl-trust-in-powershell-system-net-webclient/

$netAssembly = [Reflection.Assembly]::GetAssembly([System.Net.Configuration.SettingsSection])            
            
if($netAssembly)            
{            
    $bindingFlags = [Reflection.BindingFlags] "Static,GetProperty,NonPublic"            
    $settingsType = $netAssembly.GetType("System.Net.Configuration.SettingsSectionInternal")            
            
    $instance = $settingsType.InvokeMember("Section", $bindingFlags, $null, $null, @())            
            
 if($instance)            
    {            
        $bindingFlags = "NonPublic","Instance"            
        $useUnsafeHeaderParsingField = $settingsType.GetField("useUnsafeHeaderParsing", $bindingFlags)            
            
  if($useUnsafeHeaderParsingField)            
        {            
          $useUnsafeHeaderParsingField.SetValue($instance, $true)            
        }            
    }            
}            
            
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At line:1 char:1
+ Invoke-RestMethod https://expired.badssl.com/
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
            

3. [Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

Тут странности у меня – в 90% случаев отрабатывает, 10% – после перезапуска сессии, уже не отрабатывало. Проверяйте.

add-type @"
     using System.Net;
     using System.Security.Cryptography.X509Certificates;
     public class TrustAllCertsPolicy : ICertificatePolicy {
         public bool CheckValidationResult(
             ServicePoint srvPoint, X509Certificate certificate,
             WebRequest request, int certificateProblem) {
             return true;
         }
     }
"@            
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy            
Invoke-RestMethod https://expired.badssl.com/

 

С ошибкой:

witherr_06092017

 

Без ошибки:

withouterr_06092017

 

4. https://stackoverflow.com/questions/36456104/invoke-restmethod-ignore-self-signed-certs

if (-not("dummy" -as [type])) {            
    add-type -TypeDefinition @"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public static class Dummy {
    public static bool ReturnTrue(object sender,
        X509Certificate certificate,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors) { return true; }

    public static RemoteCertificateValidationCallback GetDelegate() {
        return new RemoteCertificateValidationCallback(Dummy.ReturnTrue);
    }
}
"@            
}            
            
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [dummy]::GetDelegate()            
            
Invoke-RestMethod https://expired.badssl.com/

 

При использовании Add-Type создается dll с случайным именем, а после удаляется. У тех, кто использует SRP/Aplocker — потребуются дополнительные действия для администратора. Утилита ProcMon – покажет, какие действия с файловой системой производит Add-Type. Сохраним вывод ProcMon в csv формате.


PS > Import-Csv AddType.CSV | Where Path -match "Temp" | Format-Table -Auto Operation,Path,Detail

Operation                     Path                                              Detail
---------                     ----                                              ------
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Desired Access: Generic Write, Read Attri
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Desired Access: Generic Write, Read Attri
WriteFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Offset: 0, Length: 465, Priority: Normal
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Desired Access: Generic Read/Write, Dispo
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Desired Access: Generic Write, Read Attri
WriteFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Offset: 0, Length: 357, Priority: Normal
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Desired Access: Generic Write, Read Attri
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Desired Access: Generic Write, Read Attri
WriteFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Offset: 0, Length: 440, Priority: Normal
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Desired Access: Generic Read, Disposition
QueryStandardInformationFile  C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     AllocationSize: 4 096, EndOfFile: 3 584,
ReadFile                      C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Offset: 0, Length: 3 584, Priority: Norma
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.pdb     Desired Access: Read Attributes, Disposit
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.out
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.pdb     Desired Access: Read Attributes, Delete,
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.err
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll

 

Попробуем сгенерировать динамический код в памяти не используя промежуточный файл. Для этого сначала создадим dll и получим IL код.

Add-Type $code -OutputAssembly cert.dll

Для получение IL-кода — воспользуемся бесплатным продуктом dotPeek (существует большого количество декомпиляторов на любой вкус – ildasm, .Net Reflector, ILSpy и т.д.)

dotPeek – Load — Windows  — IL Viewer

 

dp_06092017

 

Получим код:

il_06092017



Создадим динамический модуль:

https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/25/use-powershell-to-interact-with-the-windows-api-part-1/
https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/26/use-powershell-to-interact-with-the-windows-api-part-2/
https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/27/use-powershell-to-interact-with-the-windows-api-part-3/


# Module Builder            
$Domain = [AppDomain]::CurrentDomain            
$DynAssembly = New-Object System.Reflection.AssemblyName('Win32IC')            
            
# Запускать в памяти [System.Reflection.Emit.AssemblyBuilderAccess]::Run            
$AssemblyBuilder = $Domain.DefineDynamicAssembly(            
    $DynAssembly,             
    [System.Reflection.Emit.AssemblyBuilderAccess]::Run            
)             
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Win32IC', $False)            
            
# [Dummy]::GetDelegate()            
$TypeBuilder = $ModuleBuilder.DefineType('Dummy', 'Public, Class')            
            
# Определим ReturnTrue метод            
$ReturnTrue = $TypeBuilder.DefineMethod(            
 # Название метода            
 'ReturnTrue',            
 # Атрибуты класса            
 [Reflection.MethodAttributes] 'Public, Static',            
 # Тип возврата            
 [bool],            
 # Тип параметров             
 [Type[]] @(            
  [object],            
  [System.Security.Cryptography.X509Certificates.X509Certificate],            
  [System.Security.Cryptography.X509Certificates.X509Chain],            
  [System.Net.Security.SslPolicyErrors]            
 )            
)             
            
$ilReturnTrue = $ReturnTrue.GetILGenerator()            
$ilReturnTrue.Emit([Reflection.Emit.OpCodes]::Ldc_I4_1)            
$ilReturnTrue.Emit([Reflection.Emit.OpCodes]::Ret)            
            
# Определим GetDelegate метод            
$GetDelegate = $TypeBuilder.DefineMethod(            
 'GetDelegate',            
 [Reflection.MethodAttributes] 'Public, Static',            
 [System.Net.Security.RemoteCertificateValidationCallback],            
 $null            
)            
            
$ctor = [System.Net.Security.RemoteCertificateValidationCallback].GetConstructor(            
 [type[]]@([object],[intptr])            
)            
            
$ilGetDelegate = $GetDelegate.GetILGenerator()            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Ldnull)            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Ldftn,$ReturnTrue)            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Newobj,$ctor)            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Ret)            
            
# Создание типа и вызов метода            
$Dummy = $TypeBuilder.CreateType()            
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $Dummy::GetDelegate()            
            
#  Выполнияем запрос к сайту         
Invoke-RestMethod https://expired.badssl.com/

 

g4_06092017

 

5. Использование модуля Tunable-SSL-Validator

Описание проблемы — http://huddledmasses.org/blog/validating-self-signed-certificates-properly-from-powershell/

Скачать модуль — https://github.com/Jaykul/Tunable-SSL-Validator/archive/master.zip

В данной модуле Invoke-WebRequest/Invoke-RestMethod – переопределены в виде proxy function и добавляют дополнительную диагностическую информацию.


Get-Command -Module TunableSSLValidator -Verb Invoke            
            

CommandType     Name               Version    Source
-----------     ----               -------    ------
Function        Invoke-RestMethod  0.0        TunableSSLValidator
Function        Invoke-WebRequest  0.0        TunableSSLValidator

Дополнительная диагностическая информация:
wir_06092017

 

Добавим сертификат в исключение:

Add-SessionTrustedCertificate -LastFailed

e5_06092017


# Удалим сертификат            
Get-SessionTrustedCertificate | Remove-SessionTrustedCertificate            
            
# Отключим проверку            
Disable-SSLChainValidation            
            
Invoke-RestMethod https://expired.badssl.com/

 

l5_06092017

 

PS. Пример динамического кода из 4 пункта –RemoteCertificateValidationCallback .

Read Full Post »

Цель нашей лабораторной получить сертификат от доверенного центра сертификации для Skype For Business 2015:
    1. Выбор доверенного центра сертификации
        . Let’s Encrypt
    2. Выбрать способ проверки для получения сертификата
        . Windows DNS Server / Yandex PDD
    3. Определить список доменов в сертификате
        . sfb.contoso.ru,lyncdiscoverinternal.contoso.ru,web.contoso.ru,contoso.ru
    4. Получить сертификат
        . ACMESharp
        . DNSServer module
        . Yandex API
    5. Установить сертификат на сервер Skype For Business 2015
        . Import-CsCertificate
        . Set-CsCertificate
        . Get-CsCertificate

* В сертификате от Let’s Encrypt отсутствует CRL , поэтому может возникнуть проблема с запуском сервиса Fabric и требуется исправление ключа — <Parameter Name=”CrlCheckingFlag” Value=”0”

  https://itbasedtelco.wordpress.com/2016/06/14/s4b-front-end-servers-event-4097-flooding/

 

1. Выбор доверенного центра сертификации

Центры сертификации WoSign и StartCom, которые ранее выдавали сертификаты с большим сроком действия от 1 до 3 лет, перестали быть доверенными компанией Microsoft (https://blogs.technet.microsoft.com/mmpc/2017/08/08/microsoft-to-remove-wosign-and-startcom-certificates-in-windows-10/) и многими другими, хотя возможность получения и покупка сертификатов возможна.
К счастью, есть доверенный центр сертификации
Let’s Encrypt. Его функционала будет достаточно, для создания федерации с другими партнерами и взаимодействия с сервисом Skype. Для взаимодействия с облачными сервисами, требуется дополнительное тестирование.

Let’s Encrypt:
    Стоимость                            — бесплатно
    Сертификат действителен  — 90 дней
    SAN                                  — 100 доменов
    CRL                                   — не поддерживается
    OCSP                                — поддерживается
    IDN(Internationalized Domain Names) — поддерживается
    OV,EV                               — не поддерживается
    Способ проверки             — ftp,http,dns
    RSA размер ключа            — по умолчанию 2048 (2048, 3072, 4096)
    WildCard                            —
https://letsencrypt.org/2017/07/06/wildcard-certificates-coming-jan-2018.html
    Список ограничений       — https://letsencrypt.org/docs/rate-limits/

Список совместимости:
   
https://community.letsencrypt.org/t/which-browsers-and-operating-systems-support-lets-encrypt/4394
    https://letsencrypt.org/docs/certificate-compatibility/

Сроки реализации новых функций:
   
https://letsencrypt.org/upcoming-features/

# IDN (Internationalized Domain Names)             
[System.Globalization.IdnMapping]::new().GetAscii("президент.рф")            
xn--d1abbgf6aiiy.xn--p1ai                
            
[System.Globalization.IdnMapping]::new().GetUnicode("xn--d1abbgf6aiiy.xn--p1ai")            
президент.рф            

2. Выбрать способ проверки для получения сертификата

В данной лабораторной работе, для простоты был выбран способ проверки через записи DNS.

Рассмотрим вариант создания записей в бесплатном сервисе Яндекс.Почта(предоставляет функционал DNS сервера) и Windows DNS Server(должен быть опубликован в интернет).

Кому лень использовать скрипты, может с легкостью получить сертификаты через онлайн сервис — https://www.sslforfree.com/

Windows DnsServer Module — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/dnsserver

    Get-DnsServerResourceRecord — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/get-dnsserverresourcerecord

    Add-DnsServerResourceRecord — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/add-dnsserverresourcerecord

    Set-DnsServerResourceRecord — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/set-dnsserverresourcerecord

Yandex API Управление DNS — https://tech.yandex.ru/pdd/doc/concepts/api-dns-docpage/

    GET  /api2/admin/dns/list? — Получить DNS-записи домена —
https://tech.yandex.ru/pdd/doc/reference/dns-list-docpage/

    POST /api2/admin/dns/add   — Добавить DNS-запись        — https://tech.yandex.ru/pdd/doc/reference/dns-add-docpage/

    POST /api2/admin/dns/edit  — Редактировать DNS-запись   — https://tech.yandex.ru/pdd/doc/reference/dns-edit-docpage/

Как подключить себе сервис Яндекса:

   
https://blog.it-kb.ru/2016/02/03/delegating-dns-domain-to-yandex-ns-servers-and-connect-to-free-services-yandex-mail-for-domain-with-1000-unlimited-mailboxes-and-yandex-disk/

Все DNS записи будут типа TXT, формата:

name                       class  rr    text

_acme-challenge.domain   IN     TXT   "123drNmQL5vX0bu4YZlgy5wKNBlCny4yrjF1lSaUndc"

 

3. Определить список доменов в сертификате

Требования к сертифакату для Skype For Business 2015 —
https://technet.microsoft.com/en-us/library/dn933910.aspx

Воспользуемся мастером и скопируем список из вкладки Subject Alternative Name — https://blogs.technet.microsoft.com/uclobby/2015/05/15/renewing-skype-for-business-server-2015-certificates/

4. Получить сертификат

К этому пункту у нас должна быть собрана информация:

    + $DomainName    = "contoso.ru"

    + $SAN           = "sfb.contoso.ru","lyncdiscoverinternal.contoso.ru","web.contoso.ru","contoso.ru"

    + $email         = "pki@contoso.ru"

    * Yandex API — учетные данные, зарегистрированный и настроенный домен

+ — Обязательные параметры

* — Необязательные параметры

! — Все действия выполняем под учетной записью с правами Администратора

Для работы с Let’s Encrypt с помощью PowerShell , есть замечательный модуль ACMESharp.

Документация по модулю — https://pkisharp.github.io/ACMESharp-docs/

User Guide: ACMESharp PowerShell Client  — https://pkisharp.github.io/ACMESharp-docs/User-Guide.html

Quick Start: ACMESharp PowerShell Client — https://pkisharp.github.io/ACMESharp-docs/Quick-Start.html

ACMESharp создает Vault , которое содержит очень чувствительные данные.

* https://github.com/ebekker/ACMESharp/wiki/Vaults,-Vault-Providers-and-Vault-Profiles

:sys — the default system-wide Vault if you are running as an elevated user (admin)

    %ALLUSERSPROFILE%\ACMESharp\sysVault

:user — the default user-specific Vault if you are running as a non-elevated user

    %LOCALAPPDATA%\ACMESharp\userVault

Папки:

VAULT  /**/ = "00-VAULT"; // Vault Info

MTADT  /**/ = "01-MTADT"; // Asset Meta Data

PRVDR  /**/ = "10-PRVDR"; // Challenge Handler Provider Profile

INSTP  /**/ = "18-INSTP"; // Installer Provider Profile

CSRDT  /**/ = "30-CSRDT"; // CSR Generation Details

KEYGN  /**/ = "40-KEYGN"; // Private Key Generation Details

KEYPM  /**/ = "45-KEYPM"; // Private Key PEM Export

CSRGN  /**/ = "50-CSRGN"; // CSR Export

CSRPM  /**/ = "55-CSRPM"; // CSR PEM Export

CSRDR  /**/ = "56-CSRDR"; // CSR DER Export

CRTPM  /**/ = "65-CRTPM"; // Certificate PEM Export

CRTDR  /**/ = "66-CRTDR"; // Certificate DER Export

ISUPM  /**/ = "75-ISUPM"; // Issuer Certificate PEM Export

ISUDR  /**/ = "76-ISUDR"; // Issuer Certificate DER Export

ASSET  /**/ = "99-ASSET"; // Generic Asset

vault

Т.к. по умолчанию используется  EFS для защиты Vault ,а у тех где данный функционал отключен , надо будет явно отключить в файле конфигурации EFS — https://pkisharp.github.io/ACMESharp-docs/Local-Vault-EFS.html .

public string RootPath

        { get; set; }

public bool CreatePath

        { get; set; }

public bool BypassEFS

        { get; set; }

 

$param = (Get-ACMEVaultProfile).VaultParameters            
$param.Add("BypassEFS",$true)            
Set-ACMEVaultProfile -ProfileName ":user" -Provider local -VaultParameters $param  -Force

 

{

  "$type": "ACMESharp.Vault.Profile.VaultProfile, ACMESharp.Vault",

  "Name": "user",

  "ProviderName": "local",

  "ProviderParameters": null,

  "VaultParameters": {

    "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib",

    "BypassEFS": true,

    "CreatePath": true,

    "RootPath": "C:\\Users\\Administrator\\AppData\\Local\\ACMESharp\\userVault"

  }

}

 

Шаг 1: Установка модуля ACMESharp

Install-Module -Name ACMESharp -AllowClobber            
            

Данный модуль располагается в двух репозитариях PSGallery и Nuget. Версия в PSGallery новее и на момент написания 0.9.0.321:

Install-Module -Name ACMESharp -AllowClobber -Repository PSGallery -Force

Шаг 2: Инициализация хранилища

Initialize-ACMEVault
iv_25082017_1

Шаг 3: Регистрация аккаунта

* Let’s Encrypt CA поддерживает только тип email contact, URI формата mailto:<email-address>

New-ACMERegistration -Contacts mailto:$emailAcceptTos

Шаг 4: Подтверждение владением записей домена

В ACMESharp реализовано два типа ChallengeType: dns-01 , http-01 .

https-01 — описан https://pkisharp.github.io/ACMESharp-docs/Quick-Start.html

 

PS > Get-ACMEChallengeHandlerProfile -ListChallengeTypes

dns-01

http-01

PS > Get-ACMEChallengeHandlerProfile -ListChallengeHandlers

manual

Длинная запись:

 

New-ACMEIdentifier -Dns lyncdiscoverinternal.contoso.ru -Alias sfb1            
New-ACMEIdentifier -Dns sfb.contoso.ru -Alias sfb2            
New-ACMEIdentifier -Dns web.contoso.ru -Alias sfb3            
New-ACMEIdentifier -Dns contoso.ru -Alias sfb4            
            
Complete-ACMEChallenge -IdentifierRef sfb1 -ChallengeType dns-01 -Handler manual            
Complete-ACMEChallenge -IdentifierRef sfb2 -ChallengeType dns-01 -Handler manual            
Complete-ACMEChallenge -IdentifierRef sfb3 -ChallengeType dns-01 -Handler manual            
Complete-ACMEChallenge -IdentifierRef sfb4 -ChallengeType dns-01 -Handler manual             
            
Submit-ACMEChallenge -IdentifierRef sfb1 -ChallengeType dns-01            
Submit-ACMEChallenge -IdentifierRef sfb2 -ChallengeType dns-01            
Submit-ACMEChallenge -IdentifierRef sfb3 -ChallengeType dns-01            
Submit-ACMEChallenge -IdentifierRef sfb4 -ChallengeType dns-01            
            
## Give it a minute, just in case -- or go get more coffee            
 sleep -s 60            
            
Update-ACMEIdentifier -IdentifierRef sfb1            
Update-ACMEIdentifier -IdentifierRef sfb2            
Update-ACMEIdentifier -IdentifierRef sfb3            
Update-ACMEIdentifier -IdentifierRef sfb4

 

Короткая запись:

$SAN | Foreach-Object {$i=1} {            
 $id = "sfb$i"            
 New-ACMEIdentifier -Dns $_ -Alias $id | Out-Null            
 Complete-ACMEChallenge -IdentifierRef $id -ChallengeType dns-01 -Handler manual            
 $i++            
}
# Содержит ресурсные записи, которые потребуется создать на DNS сервере            
# Требуются для подтверждения владением доменом            
$DNS = (Get-ACMEVault).Identifiers.Where{$_.Alias -like "sfb*"}.Authorization.Challenges.Where{$_.Type -eq "dns-01"}.Challenge            
chdns_25082017_1            
# Создаем ресурсные записи             
# Windows DNS Server            
$AllRecords = (Get-DnsServerResourceRecord -ZoneName $DomainName -RRType Txt).Where{$_.Name -match "_acme-challenge"}            
            
$DNS.Foreach{            
 $rdns = $_            
 $name = $_.RecordName.TrimEnd($DomainName)            
 $record = $AllRecords.Where{$_.HostName -eq $name}            
 if($record)            
 {            
  $newrecord = $record.Clone()            
  $newrecord.RecordData.DescriptiveText = $_.RecordValue            
  Set-DnsServerResourceRecord -ZoneName $DomainName -NewInputObject $newrecord -OldInputObject $record            
 }            
 else            
 {            
  ADD-DnsServerResourceRecord -ZoneName $DomainName -Txt -Name $name -DescriptiveText $_.RecordValue            
 }            
}
# Yandex API            
Function Input-Captcha            
{            
 Param($src)            
             
 Add-Type -AssemblyName System.Windows.Forms,System.Drawing            
            
 $bimg  = [Net.WebClient]::new().DownloadData($src)            
 $ms    = [IO.MemoryStream]::new($bimg)            
 $img   = [Drawing.Image]::FromStream($ms)            
            
 [Windows.Forms.Application]::EnableVisualStyles();            
             
 $form = New-Object Windows.Forms.Form -Property @{            
  Text            = "Captcha"            
  Width           = 300            
  Height          = 227            
  FormBorderStyle = "FixedDialog"            
  Icon            = [Drawing.Icon]::ExtractAssociatedIcon(            
   "$env:ProgramFiles\Internet Explorer\iexplore.exe"            
  )            
 }            
             
 $pictureBox = New-Object Windows.Forms.PictureBox -Property @{            
  Width    = $img.Size.Width            
  Height   = $img.Size.Height            
  Location = New-Object Drawing.Size(60,30)            
  Image    = $img            
 }            
             
 $button = New-Object Windows.Forms.Button -Property @{            
  Location = New-Object Drawing.Size(12,153)            
  Size     = New-Object Drawing.Size(260,23)            
  Text     = "Set"            
 }            
             
 $button.Add_Click({            
  $script:rep = $textbox.Text.Trim()            
  $form.Close()            
 })            
            
 $textbox = New-Object Windows.Forms.TextBox -Property @{            
  Location = New-Object Drawing.Size(12,117)             
  Size     = New-Object Drawing.Size(260,20)             
 }            
             
 $form.Controls.Add($pictureBox)            
 $form.Controls.Add($button)            
 $form.Controls.Add($textbox)             
 $form.Add_Shown( { $form.Activate() } )            
 $form.ShowDialog() | Out-Null            
}            
            
Function Get-Token            
{            
 [CmdLetBinding()]            
 Param(            
  [String]$Url    = "https://pddimp.yandex.ru",            
  [String]$TUrl   = "https://pddimp.yandex.ru/api2/admin/get_token",            
  [Parameter(Mandatory)]            
   [PSCredential]$Credential,            
  [Parameter(Mandatory)]            
   [String]$DomainName            
 )            
             
 try {            
  # Логинемся в Yandex Passport            
  $wr = Invoke-WebRequest $TUrl -SessionVariable ya            
              
  # В форме заполняем login&passwd            
  $Form = $wr.Forms[0]            
  $Form.Fields["login"]  = $Credential.UserName            
  $Form.Fields["passwd"] = $Credential.GetNetworkCredential().Password            
              
  if($Form)            
  {            
   # Отправляем Post запрос            
   $wr = Invoke-WebRequest -Uri $Form.Action -WebSession $ya -Method POST -Body $Form            
               
   # Если успешно, то переходим к странице получения токена            
   $wr = Invoke-WebRequest $TUrl -WebSession $ya            
   if($wr.ParsedHtml.Title -eq "Admin's token management")            
   {            
    $Form = $wr.Forms[0]            
                
    # Удаляем из post запроса token_del             
    $Form.Fields.Remove("token_del") | Out-Null            
                
    # Вводим капчу            
    Input-Captcha $wr.Images.src            
                
    # Заполняем форму rep - Captcha            
    $Form.Fields["rep"]         = $rep             
    $Form.Fields["domain_name"] = $DomainName            
                
    # Отправляем Post запрос с методом token_get            
    $wr=Invoke-WebRequest -Uri "$url$($Form.Action)" -WebSession $ya -Method POST -Body $Form            
                
    # Возвращаем полученный токен            
    $wr.ParsedHtml.getElementsByTagName("Strong").Item(0).outerText            
   }            
  }            
 }            
 catch {            
  $_            
 }            
}            
            
$LUrl = 'https://pddimp.yandex.ru/api2/admin/dns/list?domain={0}' -f $DomainName            
$EUrl = 'https://pddimp.yandex.ru/api2/admin/dns/edit'            
$AUrl = 'https://pddimp.yandex.ru/api2/admin/dns/add'            
            
$PddToken = Get-Token -Credential "" -DomainName $DomainName            
            
if($PddToken)            
{            
 # Получим все записи для дальнейшего сравнения            
 $AllRecords = Invoke-WebRequest $LUrl -Headers @{            
  "accept" = "application/json"            
  PddToken = $PddToken            
 } | Foreach Content | ConvertFrom-Json | Foreach {$_.Records}            
             
 $DNS | Foreach {            
  $rdns = $_            
  $record = $AllRecords.Where{$_.FQDN -eq $rdns.RecordName}             
  if($record) {            
   # Обновляем данные            
   Invoke-WebRequest $EUrl -Headers @{"accept"="application/json" ; PddToken = $PddToken} -Method POST -Body @{            
    'domain'=$DomainName            
    'record_id'=$record.record_id            
    'content'=$rdns.RecordValue            
   }            
  }            
  else {            
   # Создаем записи            
   Invoke-WebRequest $AUrl -Headers @{"accept"="application/json" ; PddToken = $PddToken} -Method POST -Body @{            
    'domain'=$DomainName            
    'type'='TXT'            
    'subdomain' = $rdns.RecordName.TrimEnd($DomainName)            
    'content'=$rdns.RecordValue            
   }            
  }            
 }            
} 
 
Т.к. для подтверждения требуется вводить captcha вручную.
captcha_25082017_2           
 
# Отправить запрос на проверку            
(Get-ACMEIdentifier).Where{$_.Alias -match "sfb"}.Foreach{Submit-ACMEChallenge -IdentifierRef $_.Alias -ChallengeType dns-01}            
sleep -s 60            
(Get-ACMEIdentifier).Where{$_.Alias -match "sfb"}.Foreach{Update-ACMEIdentifier -IdentifierRef $_.Alias}

uacme_25082017_1

 

Шаг 5. Генерация сертификата

$idref = (Get-ACMEIdentifier).Where{$_.Alias -match "sfb"} | Select -First 1 | Foreach {$_.Alias}            
$aref =  (Get-ACMEIdentifier).Where{$_.Alias -match "sfb"} | Select -Skip 1 | Foreach {$_.Alias}            
New-ACMECertificate -Generate -IdentifierRef $idref -AlternativeIdentifierRefs $aref -Alias SfB2015FECert

new_25082017_1

# Отправить запрос            
Submit-ACMECertificate -CertificateRef SfB2015FECert

submit_25082017_1

Update-ACMECertificate -CertificateRef SfB2015FECert

update_25082017_1

 

Шаг 6. Экспорт в PFX

Get-ACMECertificate SfB2015FECert -ExportPkcs12 "SfB2015FECert.pfx" -CertificatePassword '12345678'
 
5. Установить сертификат на сервер Skype For Business 2015
 
Пример можно посмотреть - https://www.tbs-certificates.co.uk/FAQ/en/install-skype-business-server.html
 
Import-CsCertificate -Path "c:\SfB2015FECert.pfx" -PrivateKeyExportable $True -Password '12345678'            
Set-CsCertificate -Type Default, WebServicesInternal, WebServicesExternal,OAuthTokenIssuer -Thumbprint "439637470DF2D0E9DF933EADEA1F0610B3BA841C"            
            
Get-CsCertificate | Ft Thumbprint,Use            

Thumbprint                               Use
----------                               ---
439637470DF2D0E9DF933EADEA1F0610B3BA841C Default
439637470DF2D0E9DF933EADEA1F0610B3BA841C WebServicesInternal
439637470DF2D0E9DF933EADEA1F0610B3BA841C WebServicesExternal
439637470DF2D0E9DF933EADEA1F0610B3BA841C OAuthTokenIssuer

PS. Скрипты к статье:

le_windows_dns.txt -  Пример для Windows DNS Server

le_yandex_dns.txt    — Пример использования Yandex API PDD

Read Full Post »

Older Posts »