rmbolger / posh-acme Goto Github PK
View Code? Open in Web Editor NEWPowerShell module and ACME client to create certificates from Let's Encrypt (or other ACME CA)
Home Page: https://poshac.me/docs/latest/
License: MIT License
PowerShell module and ACME client to create certificates from Let's Encrypt (or other ACME CA)
Home Page: https://poshac.me/docs/latest/
License: MIT License
Working on an Azure Automation Runbook using Posh-ACME 2.2.0. I obtained Posh-ACME from the powershell gallery in Azure Automation. I'm using the GoDaddy DnsPlugin. I'm using the LE_STAGE environment.
$PluginArgs = @{}
$PluginArgs.Add("GDKey", $Key)
$PluginArgs.Add("GDSecret", $Secret)
Write-Output $PluginArgs
New-PACertificate <sitename> -AcceptTOS -Contact <myemail> -DnsPlugin GoDaddy -PluginArgs $PluginArgs -Verbose
I always receive this exception:
Unable to find type [Security.Cryptography.ECCurve+NamedCurves].
At C:\Modules\User\Posh-ACME\Private\New-PAKey.ps1:31 char:23
+ ... 256 { [Security.Cryptography.ECCurve+NamedCurves]::nistP2 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Security.Crypto...rve+NamedCurves:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
I've tried to add-type system.security.cryptography and system.core but no joy.
Currently if a DNS challenge validation fails. You get a fairly generic error message that only indicates the validation failed but not why it failed. The server should be sending the error reason in the challenge object. So it would be nice to show that specific error message instead of the generic one.
Where the Powershell script is already running in the context of an authenticated Azure user - there is no need to provide Azure access credentials. Suggesting AZAppCred parameter is removed from being mandatory.
Edit: So, I missed the part in the docs which showed how to use an existing credential and gave a CLI v2 example.
Here's a powershell example of the same code
Function Get-AccessToken() {
$tenantId = (Get-AzureRmContext).Tenant.Id
$cache = (Get-AzureRmContext).tokencache
$cacheItem = $cache.ReadItems() | Where-Object { $_.TenantId -eq $tenantId } | Select-Object -First 1
return $cacheItem.AccessToken
}
However, I get this error in version 2.4.0 of the module
Cannot process command because of one or more missing mandatory parameters: AZAppCred
When trying to get a certificate.
$azureParams = @{AZSubscriptionId=$subId; AZTenantId=$tenantId;AZAccessToken=$token;}
New-PACertificate -Domain $aliases -Contact $RegistrationEmail -AcceptTOS -DnsPlugin Azure -PluginArgs $azureParams
This will be a continuation of the side conversation about specifying PFX passwords in issue #13.
Currently, PFX files generated by Posh-ACME do not require a password to extract the contents (or import into a Windows cert store). This is not ideal from a security standpoint when you need to copy it to a less secure location. It can also break some tools that naively assume all PFX files will have a non-empty password.
I think my current plan is to have a default password of something like 'poshacme'. I'll also add an optional parameter like -PfxPassword
to New-PACertificate
that will let clients override the default value.
Does this sound good @rian-hout?
The GoDaddy plugin fails to remove DNS records from my zone (hope.mx), failing as follows:
Invoke-RestMethod : {"code":"INACTIVE_DOMAIN","message":"The given domain is not eligible to have its nameservers changed"}
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\DnsPlugins\GoDaddy.ps1:125 char:17
+ $response = Invoke-RestMethod -Uri "$apiRoot/$zone/records" `
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
This looks to be because the GoDaddy API doesn't support the removal of individual records. The plugin currently implements this by overwriting the entire zone.
My untested assumption is that this fails for my domain since it is locked with GoDaddy, meaning the NS records cannot be updated.
If you create a certificate using a particular set of parameters, the parameters are saved such that Submit-Renewal
is able to do a renewal on the cert later without needing to re-specify those parameters. In the future, you may want to do a renewal with slightly tweaked parameters (changing a friendly name, etc) but currently that means re-specifying all of the original parameters.
I would be nice if New-PACertificate
worked more like Submit-Renewal
such that you only need to specify the domain list and any changed parameters from the original order.
I realize the DNS plugin list is pretty limited right now. But I wanted to get version 1.0 out the door so I can get some feedback before spending too much time on additional plugins.
In any case, here are the list of DNS providers I'm hoping to add plugins for ASAP.
Feel free to comment with suggested additions or +1 other people's suggestions. Also feel free to try implementing a plugin yourself. Pull Requests are welcome and appreciated. There's a guide in the DnsPlugins README.
It would be helpful to know that the default output path is %LOCALAPPDATA%\Posh-ACME\acme-v02.api.letsencrypt.org\
Hi,
When running this command
New-PACertificate '*.test.co.za' -AcceptTOS -Contact [email protected] -DnsPlugin Windows -PluginArgs @{WinServer='localhost'}
I get the following output error. Any suggestions?
Publish-DnsChallenge : The script 'Windows.ps1' cannot be run because the following modules that are specified by the "#requires" statements of the script are missing:
DnsServer.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Public\Submit-ChallengeValidation.ps1:129 char:25
+ ... Publish-DnsChallenge $auth.DNSId $Account $auth.DNS01Toke ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (Windows.ps1:String) [Publish-DnsChallenge], ScriptRequiresException
+ FullyQualifiedErrorId : ScriptRequiresMissingModules,Publish-DnsChallenge```
I updated all of my modules and I am now presented with this error (where it previously worked fine):
WARNING: Fewer DnsPlugin values than Domain values supplied. Using Azure for the rest.
VERBOSE: Publishing DNS challenge for xxx.xxx.xxx.xx
PS>TerminatingError(Add-DnsTxtAzure): "Cannot bind argument to parameter 'AZAccessToken' because it is an empty string."
>> TerminatingError(Publish-DnsChallenge): "Cannot bind argument to parameter 'AZAccessToken' because it is an empty string."
Publish-DnsChallenge : Cannot bind argument to parameter 'AZAccessToken' because it is an empty string.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME.net46\2.5.0\Public\Submit-ChallengeValidation.ps1:122 char:25
+ ... Publish-DnsChallenge $auth.DNSId $Account $auth.DNS01Toke ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Publish-DnsChallenge], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Publish-DnsChallenge
Publish-DnsChallenge : Cannot bind argument to parameter 'AZAccessToken' because it is an empty string.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME.net46\2.5.0\Public\Submit-ChallengeValidation.ps1:122 char:25
+ ... Publish-DnsChallenge $auth.DNSId $Account $auth.DNS01Toke ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Publish-DnsChallenge], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Publish-DnsChallenge
I'm using credentials instead of token or MIS. Following the information here: https://github.com/rmbolger/Posh-ACME/blob/master/Posh-ACME/DnsPlugins/Azure-Readme.md
while running this
$azParams = @{
AZSubscriptionId='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
AZTenantId='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
AZAppCred=(Get-Credential)
}
# issue a cert
New-PACertificate test.example.com -DnsPlugin Azure -PluginArgs $azParams
I'm getting the following error/output:
VERBOSE: Using directory https://acme-staging-v02.api.letsencrypt.org/directory
VERBOSE: POST https://acme-staging-v02.api.letsencrypt.org/acme/acct/6294604 with -1-byte payload
VERBOSE: received 330-byte response of content type application/json
VERBOSE: Using account 6294604
VERBOSE: Using existing order for *.xxxx.xxxx.com with status pending
VERBOSE: Publishing DNS challenge for *.xxxx.xxxx.com
Add-DnsTxtAzure : A parameter cannot be found that matches parameter name 'AZSubscriptionId'.
At C:\Program Files\WindowsPowerShell\Modules\posh-acme\2.7.1\Public\Publish-DNSChallenge.ps1:44 char:5
+ &$addCommand $recordName $txtValue @PluginArgs
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-DnsTxtAzure], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Add-DnsTxtAzure
I don't understand why I'm getting this error
Add-DnsTxtAzure : A parameter cannot be found that matches parameter name 'AZSubscriptionId'.
-- following the guide explicitly, running this from windows 10 (1803) -- latest version of Posh-ACME(2.7.1)
When running Set-PAServer LE_STAGE
the powershell process is consuming an inordinate amount of memory (until the process/ explorer crashes).
Attach is a screenshot showing at what point powershell.exe start to consume memory. I've added the "-debug" switch to `Set-PAServer LE_STAGE' in order to track when the issue occurs.
PSVersion: 5.1.17134.112
PSEdition: Desktop
BuildVersion: 10.0.17134.112
CLRVersion: 4.0.30319.42000
OS BUild: 17134.112
In support of Managed Service Identities and generally when you'd rather not create a service principal, I'd like to see support for sending in an access token to the provider instead of tenant and credentials.
I will be sending in a PR for this based on my fork at https://github.com/bokio/Posh-ACME when I've verified that it works and we can have discussions in the PR about the implementation.
I am attempting to issue a certificate for acmetest.int.hope.mx , using the GoDaddy plugin. I'm doing this as follows:
New-PACertificate 'acmetest.int.hope.mx' -AcceptTOS -Contact [email protected] -DnsPlugin GoDaddy -PluginArgs @{GDKey='xxx'; GDSecret='xxx'} -Debug
This results in an error:
.. trimmed before here ..
DEBUG: Calling GoDaddy plugin to add _acme-challenge.acmetest.int.hope.mx TXT with value NH-jufRQ57OPCLCgTTSHPpu8P1s80NswgATY8ozKt7U
DEBUG: Checking acmetest.int.hope.mx
DEBUG: Checking int.hope.mx
DEBUG: Checking hope.mx
DEBUG: ""
Invoke-RestMethod : {"code":"INACTIVE_DOMAIN","message":"The given domain is not eligible to have its nameservers changed"}
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\DnsPlugins\GoDaddy.ps1:125 char:17
+ $response = Invoke-RestMethod -Uri "$apiRoot/$zone/records" `
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
PR incoming.
After importing all modules and dependency modules I get an error:
New-Object : Cannot find type [Org.BouncyCastle.Security.SecureRandom]: verify that the assembly containing this type
is loaded.
At C:\Modules\User\Posh-ACME\Private\New-Csr.ps1:38 char:20
$sRandom = New-Object Org.BouncyCastle.Security.SecureRandom
~~~~~~~~~~~~~~~
Hi, i'm trying to use a DNS Plugin for OVH but I don't manage to find one.
Is it a plugin already existing for OVH but with an other name ?
Thank you !
I wrote a DNSPlugin for Cloudflare. I'm having trouble make a pull request to upload the file. I'm receiving a permissions error. Can you help with this?
Any advice on how I can troubleshoot this? Tried both stable and dev versions.
PS C:\Users\Administrator> New-PACertificate darren.gdn -AcceptTOS -Contact [email protected] -DnsPlugin GCloud -PluginA
rgs @{GCKeyFile='C:\Certs\xxxxxxxxxxxxx.json'}
Connect-GCloudDns : Unable to find type [Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair].
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.3.0\DnsPlugins\GCloud.ps1:18 char:5
+ Connect-GCloudDns $GCKeyFile $GCKeyObj
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl...icCipherKeyPair:TypeName) [Connect-GCloudDns], Runt
imeException
+ FullyQualifiedErrorId : TypeNotFound,Connect-GCloudDns
PS C:\Users\Administrator>
Currently, the tooling stores all of its state including certificates in a folder in your home directory.
If you want several people or services being enabled to manage the certificates, the contents of that folder needs to be shared somehow.
Do you have any pointers to solutions around this, e.g. by storing/restoring the state in S3/Azure Blob or some other way.
Hi,
Despite the example in Readme.md it's not possible to make certificate with and without wildcard.
They want me to create two TXT record with the same name but different values:
[DBG]: PS C:\WINDOWS\system32>> New-PACertificate '*.mysite.org','mysite.org' -AcceptTOS -Contact [email protected]
ะะ ะะะฃะะ ะะะะะะะ: DnsPlugin not specified. Defaulting to Manual.
Press any key to continue once the record has been created:
Create TXT record for: _acme-challenge.mysite.org
TXT Value: WnhgRh3FrxeL401FydEpZzpbLsfZss_DcTk0hTnQq6Y
Create TXT record for: _acme-challenge.mysite.org
Press any key to continue once the record has been created:
TXT Value: OShUlweS_h6BWnoqbPyA6rLTaladTVFzqYInfTnDOEo
Delete TXT record for: _acme-challenge.mysite.org
Press any key to continue once the record has been deleted:
TXT Value: WnhgRh3FrxeL401FydEpZzpbLsfZss_DcTk0hTnQq6Y
Delete TXT record for: _acme-challenge.mysite.org
TXT Value: OShUlweS_h6BWnoqbPyA6rLTaladTVFzqYInfTnDOEo
Press any key to continue once the record has been deleted:
Authorization for *.mysite.org returned status 'invalid'.
C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\1.1\Private\Wait-AuthValidation.ps1:33 ะทะฝะฐะบ:17
+ ... throw "Authorization for $($auth.fqdn) returned status '$ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Authorization f...atus 'invalid'.:String) [], RuntimeException
+ FullyQualifiedErrorId : Authorization for *.mysite.org returned status 'invalid'.
(I replaced real domain with 'mysite' in log)
The plugin parameters are saved to disk, but the access token for Azure is short-lived and needs to be sent in again. Adding a pluginArgs parameter like in other places should be enough.
Workaround: Using New-PACertificate after copying some code from Submit-Renewal.ps1
Hi there,
Firstly, thanks for your work on this project.
I'm not sure if this is a bug, but i first followed the simple tutorial and got to the manual txt entry. Then again i have tried and now each time i am faced with the following output from powershell and wanted to see if there is something missing regarding bouncycastle library ?
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.
PS D:\Users\admin> Install-Module -Name Posh-ACME
Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its
InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
'PSGallery'?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): y
PS D:\Users\admin> New-PACertificate *.mydomainname.net -AcceptTOS
New-Object : Cannot find type [Org.BouncyCastle.Security.SecureRandom]: verify that the assembly containing this type
is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:38 char:20
+ $sRandom = New-Object Org.BouncyCastle.Security.SecureRandom
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
New-Object : Cannot find type [Org.BouncyCastle.Crypto.Generators.RsaKeyPairGenerator]: verify that the assembly
containing this type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:65 char:23
+ ... $rsaGen = New-Object Org.BouncyCastle.Crypto.Generators.RsaKeyPairG ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
New-Object : Cannot find type [Org.BouncyCastle.Crypto.KeyGenerationParameters]: verify that the assembly containing
this type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:66 char:25
+ ... $genParam = New-Object Org.BouncyCastle.Crypto.KeyGenerationParameter ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
You cannot call a method on a null-valued expression.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:67 char:13
+ $rsaGen.Init($genParam)
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:68 char:13
+ $keyPair = $rsaGen.GenerateKeyPair()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Export-Pem : Cannot bind argument to parameter 'InputObject' because it is null.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:73 char:20
+ Export-Pem $keyPair $keyFile
+ ~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Pem], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Export-Pem
New-Object : Cannot find type [Org.BouncyCastle.Asn1.X509.X509Name]: verify that the assembly containing this type is
loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:80 char:16
+ ... $subject = New-Object Org.BouncyCastle.Asn1.X509.X509Name("CN=$($Ord ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
New-Object : Cannot find type
[Collections.Generic.Dictionary[Org.BouncyCastle.Asn1.DerObjectIdentifier,Org.BouncyCastle.Asn1.X509.X509Extension]]:
verify that the assembly containing this type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:83 char:16
+ ... $extDict = New-Object 'Collections.Generic.Dictionary[Org.BouncyCast ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
New-Object : Cannot find type [Org.BouncyCastle.Asn1.X509.BasicConstraints]: verify that the assembly containing this
type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:86 char:134
+ ... OctetString(New-Object Org.BouncyCastle.Asn1.X509.BasicConstraints($f ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
Unable to find type [Org.BouncyCastle.Asn1.X509.KeyUsage].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:87 char:172
+ ... stle.Asn1.X509.KeyUsage([Org.BouncyCastle.Asn1.X509.KeyUsage]::Digita ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastle.Asn1.X509.KeyUsage:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
Unable to find type [Org.BouncyCastle.Asn1.X509.KeyPurposeID].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:88 char:184
+ ... 09.ExtendedKeyUsage([Org.BouncyCastle.Asn1.X509.KeyPurposeID]::IdKPSe ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastle.Asn1.X509.KeyPurposeID:TypeName) [], RuntimeExceptio
n
+ FullyQualifiedErrorId : TypeNotFound
Unable to find type [Org.BouncyCastle.Asn1.X509.GeneralName].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:91 char:98
+ ... sn1.X509.GeneralName([Org.BouncyCastle.Asn1.X509.GeneralName]::DnsNam ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastle.Asn1.X509.GeneralName:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
New-Object : Cannot find type [Org.BouncyCastle.Asn1.X509.GeneralNames]: verify that the assembly containing this type
is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:92 char:122
+ ... OctetString(New-Object Org.BouncyCastle.Asn1.X509.GeneralNames(@(,$ge ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
New-Object : Cannot find type [Org.BouncyCastle.X509.Extension.SubjectKeyIdentifierStructure]: verify that the
assembly containing this type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:93 char:121
+ ... OctetString(New-Object Org.BouncyCastle.X509.Extension.SubjectKeyIden ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
Unable to find type [Org.BouncyCastle.Asn1.X509.X509Extensions].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:96 char:18
+ $extDict.Add([Org.BouncyCastle.Asn1.X509.X509Extensions]::BasicCo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl....X509Extensions:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
Unable to find type [Org.BouncyCastle.Asn1.X509.X509Extensions].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:97 char:18
+ $extDict.Add([Org.BouncyCastle.Asn1.X509.X509Extensions]::KeyUsag ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl....X509Extensions:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
Unable to find type [Org.BouncyCastle.Asn1.X509.X509Extensions].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:98 char:18
+ $extDict.Add([Org.BouncyCastle.Asn1.X509.X509Extensions]::Extende ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl....X509Extensions:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
Unable to find type [Org.BouncyCastle.Asn1.X509.X509Extensions].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:99 char:18
+ $extDict.Add([Org.BouncyCastle.Asn1.X509.X509Extensions]::Subject ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl....X509Extensions:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
Unable to find type [Org.BouncyCastle.Asn1.X509.X509Extensions].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:100 char:18
+ $extDict.Add([Org.BouncyCastle.Asn1.X509.X509Extensions]::Subject ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl....X509Extensions:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
New-Object : Cannot find type [Org.BouncyCastle.Asn1.X509.X509Extensions]: verify that the assembly containing this
type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:110 char:19
+ ... xtensions = New-Object Org.BouncyCastle.Asn1.X509.X509Extensions($ext ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
Unable to find type [Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers].
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:111 char:110
+ ... tributePkcs([Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers]::Pkcs9 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Org.BouncyCastl...jectIdentifiers:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
New-Object : Cannot find type [Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest]: verify that the assembly containing
this type is loaded.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:114 char:12
+ $req = New-Object Org.BouncyCastle.Pkcs.Pkcs10CertificationReques ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
Export-Pem : Cannot bind argument to parameter 'InputObject' because it is null.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:117 char:16
+ Export-Pem $req $reqFile
+ ~~~~
+ CategoryInfo : InvalidData: (:) [Export-Pem], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Export-Pem
You cannot call a method on a null-valued expression.
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:120 char:12
+ return (ConvertTo-Base64Url $req.GetEncoded())
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Error parsing certificate request: asn1: syntax error: sequence truncated
At D:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\Invoke-ACME.ps1:125 char:9
+ throw [AcmeException]::new($acmeError.detail,$acmeError)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], AcmeException
+ FullyQualifiedErrorId : Error parsing certificate request: asn1: syntax error: sequence truncated
This is similar to my previous enhancement (asking for wiki page on doing DNS validation by hand) -- but it would be really nice if the "Manual" dns plugin -- instead of prompting the user.. would just output the txtvalue needed and then somehow allow the user to notify "yup, its done, continue your process". Perhaps the "manual" dns plugin to accept a scriptblock and the user could put the code in there to create the DNS record?
Anyways, some thoughts and thanks for developing this incredibly useful PowerShell module! -- I've been excited about LE for 2 years now, but wasn't able to start using it until I came across this module! Also, thanks for being very responsive on issues!
In Get-PAOrder there's a place (lines 52-58) where cached PAOrder is checked. But if there's no cached PAOrder module throws an exception.
} else {
throw "Unable to find cached PAOrder info for $MainDomain."
}
As I understood right, cached PAOrder isn't necessary for new-pacertificate due to:
try { $order = Get-PAOrder $Domain[0] -Refresh } catch {}
So it's not actually an error but one expected behavior of code. So maybe it's better to change to something like "return $null"?
Because throwing an Exception will add Error description to $Error variable (list that contains all errors during script execution) and it make scripting and automation harder because since that moment you cannot just check if $Error is null and have to analyze what errors are in there.
Approvement:
PS C:\Users\Kell> function invoke-error { throw "error" }
PS C:\Users\Kell> $Error.clear()
PS C:\Users\Kell> $Error
PS C:\Users\Kell> try {invoke-error} catch {}
PS C:\Users\Kell> $Error
error
At line:1 char:25
+ function invoke-error { throw "error" }
+ ~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (error:String) [], RuntimeException
+ FullyQualifiedErrorId : error
PS C:\Users\Kell>
Another way is to use "-ErrorAction Ignore" since it does not add error to $Error but due to PowerShell/PowerShell#1759 there are some problems with it that would be solved in PS6.2
$azParams = @{AZSubscriptionId = 'xxxx-xxxxxx-xxx-xx'; AZUseIMDS=$true}
New-PACertificate somedomain -DnsPlugin Azure -PluginArgs $azParams -verbose
gives me this output
VERBOSE: Using directory https://acme-v02.api.letsencrypt.org/directory
VERBOSE: POST https://acme-v02.api.letsencrypt.org/acme/acct/36862242 with -1-byte payload
VERBOSE: received 333-byte response of content type application/json
VERBOSE: Using account 36862242
VERBOSE: Creating a new order for somedomain
VERBOSE: POST https://acme-v02.api.letsencrypt.org/acme/new-order with -1-byte payload
VERBOSE: received 369-byte response of content type application/json
VERBOSE: Publishing DNS challenge for somedomain
cmdlet Add-DnsTxtAzure at command pipeline position 1
Supply values for the following parameters:
AZTenantId:
Why is it prompting me for AZTenantId -- when I passed in "AZUseIMDS"?
Hello,
First off, please do not ever abandon this project. It has literally saved my life and made production deployment seamless and painless.
Second: I have an entire windows domain back-end with windows DNS servers in my DMZ facing the internet.
How exactly does the "Windows" DNS plugin work. I enter in the IP address of the DNS server and it will then give me my "TXT" record ? I then go add that to my domain Registrar ?
Unfortunately at the moment I cannot do a test but my curiosity is getting the best of me.
Thanks
There is a useful page for doing manual HTTP validation -- but nothing for doing manual DNS validation. There is a "Manual" dns plugin -- but it doesn't allow for programmatic engagement. If you are wanting to leverage a DNS infrastructure that there isn't a module for, there is no easy way to do this. Please provide an advanced wiki page for DNS validation that matches the http validation. -- below is what I was able to cobble together by stepping through POSH-Acme with the "Manual" DNS plugin.
# Setup your Account
if(-not (Get-PAAccount)) {
Write-Host "Building account..."
New-PAAccount -Contact "youremailaddress" -AcceptTOS
}
#Setup the Order
New-PAOrder -Domain "somedomain" -Force -FriendlyName "Friendly Description"
#Gather the authorization information
$authNeeded = (Get-PAOrder | Get-PAAuthorizations)[0]
#Generate the TXT Value
$keyAuth = Get-KeyAuthorization $authNeeded.challenges[0].token
$keyAuthBytes = [Text.Encoding]::UTF8.GetBytes($keyAuth)
$sha256 = [Security.Cryptography.SHA256]::Create()
$keyAuthHash = $sha256.ComputeHash($keyAuthBytes)
$txtValue = [Convert]::ToBase64String($keyAuthHash).Split("=")[0].Replace('+','-').Replace('/','_')
#Do your own coding for DNS record creation
# Send DNS Challenge Acknowledge (basically, tell LE to check dns now)
Send-ChallengeAck $authNeeded.DNS01Url
#Verify that the challenge happened
$start = (Get-Date)
while((Get-Date) -lt $start.AddSeconds(15)) {
Start-Sleep -Milliseconds 500
$authStatus = Get-PAAuthorizations $authNeeded.DNS01Url | Select-Object -ExpandProperty status
if($authStatus -ne "pending") { break }
}
if($authStatus -ne "valid") { throw "not validated!" }
# Do your own coding to remove DNS record
# Lets get the Certificate (by invoke "New-PACertificate" with the same domain, posh-ACME is smart enough to complete the order for us!
Get-PAOrder -Refresh
$certInfo = New-PACertificate "somedomain"
When testing with both Digital Ocean and CloudFlare DNS plugin, I get prompted for the arguments in PluginArgs , even though I passed the args like shown in the examples.
Running Posh-ACME v2 from PowerShell Gallery
Now that I got my first certificate set up for Azure (this tooling is awesome - it just worked) I want to build something automated around it, e.g. by creating a web service or a VM or something else that can run periodically and refresh certificates and then upload them to Keyvault, app services, wherever needed.
Are there any recipes to follow or advise to be had?
Reading the letsencrypt documentation, it seems that once you have a valid authorized key pair for a domain you can use it also for renewal of ssl certificates without needing to re-validate the domain.
That would be nice to not have to use any DNSPlugin functionality for certificate renewals.
Everyone loves tab completion. It would be great to have tab completion for the DnsPlugin
parameter in New-PACertificate
and Submit-ChallengeValidation
.
The code is already dynamically validating the parameter via ValidateScript
. We should convert it to a dynamic parameter instead and get the benefits of tab completion as well.
Desktop works fine, however after installing on server, I am receiving...
Cannot find type [Org.BouncyCastle.Security.SecureRandom]: verify that the assembly containing this type is loaded.
Hi Guys,
Thanks for all your work on this module.
Just wondering if I'm missing a step... after running New-PACertificate ...
it generates a file called order.json which contains a bunch of JSON, but the DNSPlugin name is set to null. It works though, and saves the certificates.
But then calls to Submit-Renewal fail with the error: New-PACertificate : Cannot validate argument on parameter 'DnsPlugin'
To get it working I had to manually edit Submit-Renewal.ps1
line 74 and set $certParams.DnsPlugin = "Route53"
Note that updating order.json worked (one time) and then got changed back to null again!
I've traced though the code, but I can't see why it isn't saving the DnsPlugin value in order.json, any ideas?
Cheers,
Simon
Hello,
How can I output to a PEM format ?
Thanks
This part of the ACME spec allows you to replace the current account key with a new one in the case of key compromise or suspicion of key compromise.
Hello,
Not sure if it is an issue or just how it works now.
In the previous versions when I will submit a new Cert it will give the "TXT" record value.
I will then submit it to my domain registrar and complete the process.
But now in version 2.4.0 I request a Cert it does not give me the "TXT" record value and just makes the cert.
Is that suppose to be happening ?
Thanks
While the module works pretty well today for getting certs using the DNS challenge, support for other challenge types like HTTP is still missing. And while implementing an HTTP challenge plugin system is still a ways off, it should be possible to expose more of the ACME protocol via public functions that would allow someone to handle other challenge types manually. This should ultimately help the effort to develop an official HTTP challenge solution.
When using the Azure plugin the expiration for the authentication token is evaluated incorrectly by simply calling Get-Date
and comparing the localized TimeDate object against the UTC value attached to the token rather than converting the resulting TimeDate object to it's UTC value.
Posh-ACME/Posh-ACME/DnsPlugins/Azure.ps1
Line 314 in 55abb2d
DnsPluginHelp no aliyun cloud api
Hello,
Not really an issue but wanted to ask.
Ive read the docs and which parameter can I use to name the Cert that is created.
Currently when the cert gets outputted to the folder it is named "Cert" and when I import it into the certificate store its name is like a string of numbers.
IS their a way to name the certificate ?
Thanks
Can the PFX creation be updated to include the intermediate and root certs?
After running 'Install-Module -Name Posh-ACME' and successfully installing the module, the following error occurs on all servers.
Client Version: v2.5.0
Windows Version: Windows Server 2016
Command: New-PACertificate XXXXXX -AcceptTOS (dns entry masked)
Error:
New-PACertificate : The 'New-PACertificate' command was found in the module 'Posh-ACME', but the module could not be
loaded. For more information, run 'Import-Module Posh-ACME'.
At line:1 char:1
New-PACertificate XXXXXX -AcceptTO ...
CategoryInfo : ObjectNotFound: (New-PACertificate:String) [], CommandNotFoundException
FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
acme-dns is a project designed explicitly to make it easier for people to deal with DNS challenges associated with the ACME protocol. From the project readme:
Many DNS servers do not provide an API to enable automation for the ACME DNS challenges. Those which do, give the keys way too much power. Leaving the keys laying around your random boxes is too often a requirement to have a meaningful process automation.
Acme-dns provides a simple API exclusively for TXT record updates and should be used with ACME magic "_acme-challenge" - subdomain CNAME records. This way, in the unfortunate exposure of API keys, the effetcs are limited to the subdomain TXT record in question.
This would be a great way to indirectly support more DNS providers until the list of supported plugins grows.
Here's a great article about the security merits of using something like acme-dns as well.
https://www.eff.org/deeplinks/2018/02/technical-deep-dive-securing-automation-acme-dns-challenge-validation
New-PACertificate
is written in such a way that if the cert request process has an error or is otherwise interrupted prior to completing the request, you can run the same command again and it will attempt to pick up where it left off.
Currently if request makes it all the way to completion, running the same command again will re-download and overwrite the cert files each time it's run until the renewal window.
It should probably just do nothing instead. Maybe throw a warning that the order is already complete and to use -Force if you want to re-issue.
When Running as a service account that has never had IE launched Invoke-WebRequest and Invoke-RestMethod will fail with the following error:
Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.
This has been seen by many others and the fix (described in this Stack Overflow question) is to add -UseBasicParsing to the commands.
I'm attempting to run this from OctopusDeploy so it can generate the certificate and then distribute it to our web servers. The account that OctopusDeploy runs as will never have IE launched so this is the only option. Does this seem feasible?
Thanks in advance!
First off thanks for the great Module. In other issues you've described that as of now the "profile" directory cannot be customized and copy/pasting the directory will not work as DPAPI is used to encrypt sensitive data.
You also mention that one can just request a new certificate if the profile of the initial requester cannot be used.
My understanding of LE was always that it is "account-based". Meaning you register an account with a contact address and so on and then add domains to it. If I use New-PACertificate
each time I don't have the originial profile available wouldn't that create new accounts with LE each time?
Is there a more elegant solution to this?
PS C:\Users\chris> New-PACertificate '*.REMOVED.com' -AcceptTOS -Contact [email protected] -Verbose
VERBOSE: Using directory https://acme-staging-v02.api.letsencrypt.org/directory
VERBOSE: POST https://acme-staging-v02.api.letsencrypt.org/acme/acct/6159866 with -1-byte payload
VERBOSE: received 349-byte response of content type application/json
VERBOSE: Using account 6159866
VERBOSE: Creating a new order for *.REMOVED.com
VERBOSE: POST https://acme-staging-v02.api.letsencrypt.org/acme/new-order with -1-byte payload
VERBOSE: received 386-byte response of content type application/json
VERBOSE: Publishing DNS challenge for *.REMOVED.com
VERBOSE: Saving TXT record to display when Save-DnsTxtManual is called.
VERBOSE: Saving changes for Manual plugin
Please create the following TXT records:
------------------------------------------
_acme-challenge.REMOVED.com -> abPX3eaRYqLyN9nPIXApJGd7h1x_-3lWCYu73Iskr2k
------------------------------------------
Press any key to continue.:
VERBOSE: Sleeping for 120 seconds while DNS change(s) propagate
VERBOSE: Requesting challenge validations
VERBOSE: POST
https://acme-staging-v02.api.letsencrypt.org/acme/challenge/pv-ga2Vz_4iW9kIjjXPdnW_U68isoBUjvvuhj06ZSHI/130779510
with -1-byte payload
VERBOSE: received 229-byte response of content type application/json
VERBOSE: Saving TXT record to display when Save-DnsTxtManual is called.
VERBOSE: Saving changes for Manual plugin
Please remove the following TXT records:
------------------------------------------
_acme-challenge.REMOVED.com -> abPX3eaRYqLyN9nPIXApJGd7h1x_-3lWCYu73Iskr2k
------------------------------------------
Authorization for *.REMOVED.com returned status 'invalid'.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.2.0\Private\Wait-AuthValidation.ps1:33 char:17
+ ... throw "Authorization for $($auth.fqdn) returned status '$ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Authorization f...atus 'invalid'.:String) [], RuntimeException
+ FullyQualifiedErrorId : Authorization for *.REMOVED.com returned status 'invalid'.`
Doing an nslookup using Google, CloudFlare, OpenDNS and even my local ISP DNS comes back perfect. The TXT record value matches exactly.
PS C:\Users\chris> nslookup -q=TXT _acme-challenge.REMOVED.com 1.1.1.1
Server: 1dot1dot1dot1.cloudflare-dns.com
Address: 1.1.1.1
Non-authoritative answer:
_acme-challenge.REMOVED.com text =
"abPX3eaRYqLyN9nPIXApJGd7h1x_-3lWCYu73Iskr2k"
I assume this is not a library issue, but something going on on the LE side of things.
Hello,
I'm am trying out your code and did a install-module -name posh-acme to install the latest release (I haven't tried the development release yet). Then ran set-paserver le_stage and New-PACertificate '*.mydomainhere','mydomainhere' -accepttos -contact myemailhere -verbose Using my actual domain info for mydomainhere. it got to line "Creating new private key for the certificate request" and then started getting lots of errors. the first was:
New-Object : Cannot find type [Org.BouncyCastle.Security.SecureRandom]: verify that the assembly containing this type
is loaded.
At C:\Program Files\WindowsPowerShell\Modules\Posh-ACME\2.4.0\Private\New-Csr.ps1:38 char:20
$sRandom = New-Object Org.BouncyCastle.Security.SecureRandom
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
They continue on in the same vein from there. I went to that site and it apprears to be a helper class your using? I checked and couldn't find any dependencies listed in your documentation or code to load from there site. Any recommendations?
Thanks so much for doing this and any help you can provide!
Hello,
I installed the module and it worked perfectly. I got my cert
How can I revoke the cert on the letsEncrypt servers ?
Is it possible ?
If so what commands do I type ?
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.