2017-09-21 13:32:15 +02:00
|
|
|
function New-CredentialStore {
|
|
|
|
<#
|
|
|
|
.SYNOPSIS
|
|
|
|
Creates a new credential store File
|
|
|
|
|
|
|
|
.DESCRIPTION
|
|
|
|
You need to run this script first to create a new credential store before you try to
|
|
|
|
save new credentials with New-CredentialStoreItem.
|
|
|
|
|
|
|
|
.PARAMETER Path
|
|
|
|
Define a location for the new shared CredentialStore. The default store will be created in
|
|
|
|
$Env:ProgramData\PSCredentialStore dir.
|
|
|
|
|
|
|
|
.PARAMETER Shared
|
|
|
|
Creates a CredentialStore in the Shared mode. This enables you to read the CredentialStore Items on
|
|
|
|
different systems or profiles. In addition you can optionally provide a custom path wit the -Path parameter.
|
|
|
|
|
|
|
|
.PARAMETER Force
|
|
|
|
Use this switch to reset an existing store. The complete content will be wiped.
|
|
|
|
|
2019-04-29 16:05:43 +02:00
|
|
|
.PARAMETER SkipPFXCertCreation
|
2022-07-15 10:59:56 +02:00
|
|
|
You can skip the pfx certificate creation process. This makes sense if you have a previously created cert
|
|
|
|
or want to import a cert in cross-platform environments.
|
2019-04-29 16:05:43 +02:00
|
|
|
|
|
|
|
.Parameter UseCertStore
|
2022-07-15 10:59:56 +02:00
|
|
|
Instead of using a plain pfx file beside your CredentialStore file you can import it into the user or
|
|
|
|
machine certificate store. In this case the system itself secures the cert and you don't hat to set custom
|
|
|
|
NTFS permissions so secure your shared certificate.
|
2019-04-29 16:05:43 +02:00
|
|
|
|
2017-09-21 13:32:15 +02:00
|
|
|
.INPUTS
|
|
|
|
[None]
|
|
|
|
|
|
|
|
.OUTPUTS
|
2019-01-16 12:55:29 +01:00
|
|
|
['PSCredentialStore.Store'] Returns the recently created CredentialStore object if the -PassThru parameter
|
|
|
|
was given.
|
2017-09-21 13:32:15 +02:00
|
|
|
|
|
|
|
.EXAMPLE
|
|
|
|
New-CredentialStore
|
|
|
|
# Creates a new private CredentialStore
|
|
|
|
|
|
|
|
.EXAMPLE
|
|
|
|
New-CredentialStore -Force
|
|
|
|
# Resets an existing private CredentialStore
|
|
|
|
|
|
|
|
.EXAMPLE
|
|
|
|
New-CredentialStore -Shared
|
|
|
|
# Creates a new shared CredentialStore
|
|
|
|
|
|
|
|
.EXAMPLE
|
|
|
|
New-CredentialStore -Shared -Path "C:\TMP\CredentialStore.json"
|
|
|
|
# Creates a new shared CredentialStore in the given location.
|
|
|
|
#>
|
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Private')]
|
|
|
|
[OutputType('PSCredentialStore.Store')]
|
|
|
|
param (
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
|
2017-09-21 13:32:15 +02:00
|
|
|
[switch]$Shared,
|
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
|
2017-09-21 13:32:15 +02:00
|
|
|
[ValidateNotNullOrEmpty()]
|
2019-04-04 17:02:17 +02:00
|
|
|
[System.IO.FileInfo]$Path,
|
2017-09-21 13:32:15 +02:00
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
|
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
|
|
|
|
[switch]$Force,
|
2017-09-21 13:32:15 +02:00
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
|
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
|
|
|
|
[switch]$PassThru,
|
2017-09-21 13:32:15 +02:00
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
|
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
|
|
|
|
[switch]$SkipPFXCertCreation,
|
2019-04-04 17:02:17 +02:00
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
|
|
|
|
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
|
|
|
|
[switch]$UseCertStore
|
2019-01-16 12:55:29 +01:00
|
|
|
)
|
2017-09-21 13:32:15 +02:00
|
|
|
|
2019-01-16 12:55:29 +01:00
|
|
|
begin {
|
|
|
|
# Lets get the current Date in a human readable format.
|
2022-06-28 08:56:33 +02:00
|
|
|
$CurrentDate = Get-Date -Format 'u'
|
2019-04-04 17:02:17 +02:00
|
|
|
|
|
|
|
# test if the path input is a valid file path
|
|
|
|
if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Path')) {
|
|
|
|
if ($Path.Attributes -contains 'Directory') {
|
|
|
|
$ErrorParams = @{
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
Exception = [System.IO.InvalidDataException]::new(
|
2022-07-15 10:59:56 +02:00
|
|
|
'Please provide a full path containing the ' +
|
|
|
|
'credential store file name with the .json extension!'
|
2019-04-04 17:02:17 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
Write-Error @ErrorParams
|
|
|
|
}
|
|
|
|
elseif ( ($null -eq $Path.Extension) -or ($Path.Extension -ne '.json')) {
|
|
|
|
$ErrorParams = @{
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
Exception = [System.IO.InvalidDataException]::new(
|
2022-06-28 08:56:33 +02:00
|
|
|
'Your provided path does not contain the required file extension .json!'
|
2019-04-04 17:02:17 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
Write-Error @ErrorParams
|
|
|
|
}
|
|
|
|
}
|
2017-09-21 13:32:15 +02:00
|
|
|
}
|
|
|
|
|
2019-01-16 12:55:29 +01:00
|
|
|
process {
|
|
|
|
# Set the CredentialStore for private, shared or custom mode.
|
|
|
|
Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
|
2022-06-28 08:56:33 +02:00
|
|
|
if ($PSCmdlet.ParameterSetName -eq 'Private') {
|
2019-01-16 12:55:29 +01:00
|
|
|
$Path = Get-DefaultCredentialStorePath
|
2017-09-21 13:32:15 +02:00
|
|
|
}
|
2022-06-28 08:56:33 +02:00
|
|
|
elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
|
2019-01-16 12:55:29 +01:00
|
|
|
if (!($PSBoundParameters.ContainsKey('Path'))) {
|
|
|
|
$Path = Get-DefaultCredentialStorePath -Shared
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Test if in the CredentialStore already exists.
|
2022-06-28 08:56:33 +02:00
|
|
|
Write-Verbose 'Test if there is already a credential store.'
|
2019-01-16 12:55:29 +01:00
|
|
|
if ((Test-Path -Path $Path) -and ($Force -ne $true)) {
|
|
|
|
$ErrorParams = @{
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
Exception = [System.InvalidOperationException]::new(
|
|
|
|
'The given file already exists. Use the -Force switch to override the existing store.'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Write-Error @ErrorParams
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! $SkipPFXCertCreation.IsPresent) {
|
|
|
|
$CRTParams = @{
|
|
|
|
Country = 'DE'
|
|
|
|
State = 'PSCredentialStore'
|
|
|
|
City = 'PSCredentialStore'
|
|
|
|
Organization = 'PSCredentialStore'
|
2019-04-04 17:02:17 +02:00
|
|
|
OrganizationalUnitName = $PSCmdlet.ParameterSetName
|
|
|
|
CommonName = 'PSCredentialStore'
|
2019-01-16 12:55:29 +01:00
|
|
|
}
|
2019-04-29 16:05:43 +02:00
|
|
|
$CRTAttribute = New-CSCertAttribute @CRTParams
|
2019-01-16 12:55:29 +01:00
|
|
|
|
|
|
|
# If we are working with a ne shared store we have to create the location first.
|
|
|
|
# Otherwise openssl fails with unknown path
|
|
|
|
|
|
|
|
$StoreHome = Split-Path -Path $Path -Parent
|
|
|
|
if (! (Test-Path -Path $StoreHome)) {
|
|
|
|
New-Item -ItemType Directory -Path $StoreHome -ErrorAction Stop
|
|
|
|
}
|
|
|
|
|
|
|
|
$PfxParams = @{
|
|
|
|
CRTAttribute = $CRTAttribute
|
|
|
|
KeyName = Join-Path -Path $StoreHome -ChildPath 'private.key'
|
|
|
|
CertName = Join-Path -Path $StoreHome -ChildPath 'PSCredentialStore.pfx'
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
Confirm = $false
|
|
|
|
}
|
|
|
|
|
2019-04-04 17:02:17 +02:00
|
|
|
# test if there is already a cert
|
2019-01-16 12:55:29 +01:00
|
|
|
if ((Test-Path $PfxParams.CertName) -and (! $Force.IsPresent)) {
|
|
|
|
$ErrorParams = @{
|
|
|
|
Exception = [System.IO.InvalidDataException]::new(
|
|
|
|
'There is already a PfxCertificate for a private CredentialStore!'
|
|
|
|
)
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
}
|
|
|
|
Write-Error @ErrorParams
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2019-04-29 16:05:43 +02:00
|
|
|
New-CSCertificate @PfxParams
|
2019-01-16 12:55:29 +01:00
|
|
|
}
|
|
|
|
catch {
|
|
|
|
$_.Exception.Message | Write-Error
|
|
|
|
$ErrorParams = @{
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
Exception = [System.Exception]::new(
|
|
|
|
'Could not create the private PfXCertificate!'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Write-Error @ErrorParams
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$FreshCert = Get-PfxCertificate -FilePath $PfxParams.CertName -ErrorAction Stop
|
|
|
|
}
|
|
|
|
catch [System.Management.Automation.ItemNotFoundException] {
|
|
|
|
$_.Exception.Message | Write-Error
|
|
|
|
Write-Error -Message 'Could not read the new PfxCertificate.' -ErrorAction Stop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# We need to use the IDictionary to keep the property sorting in the object.
|
|
|
|
$ObjProperties = [ordered]@{
|
|
|
|
PSTypeName = 'PSCredentialStore.Store'
|
|
|
|
Version = $CSVersion
|
|
|
|
Created = $CurrentDate
|
|
|
|
PfxCertificate = $null
|
|
|
|
Thumbprint = $null
|
|
|
|
Type = $null
|
|
|
|
}
|
2019-04-29 16:05:43 +02:00
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
if ($PSCmdlet.ParameterSetName -eq 'Shared') {
|
|
|
|
$ObjProperties.Type = 'Shared'
|
2019-04-29 16:05:43 +02:00
|
|
|
}
|
|
|
|
else {
|
2022-06-28 08:56:33 +02:00
|
|
|
$ObjProperties.Type = 'Private'
|
2019-04-29 16:05:43 +02:00
|
|
|
}
|
|
|
|
|
2019-01-16 12:55:29 +01:00
|
|
|
if (! $SkipPFXCertCreation.IsPresent) {
|
|
|
|
$ObjProperties.Thumbprint = $FreshCert.Thumbprint
|
2019-04-04 17:02:17 +02:00
|
|
|
|
2019-04-29 16:05:43 +02:00
|
|
|
if ($UseCertStore.IsPresent) {
|
|
|
|
Write-Verbose 'Importing new PFX certificate file...'
|
|
|
|
Import-CSCertificate -Type $ObjProperties.Type -Path $PfxParams.CertName
|
2019-04-04 17:02:17 +02:00
|
|
|
}
|
|
|
|
else {
|
2019-04-29 16:05:43 +02:00
|
|
|
$ObjProperties.PfxCertificate = $PfxParams.CertName
|
|
|
|
|
2019-04-04 17:02:17 +02:00
|
|
|
}
|
2019-01-16 12:55:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$CredentialStoreObj = [PSCustomObject]$ObjProperties
|
|
|
|
try {
|
|
|
|
$JSON = ConvertTo-Json -InputObject $CredentialStoreObj -ErrorAction Stop
|
|
|
|
$JSON | Out-File -FilePath $Path -ErrorAction Stop -Force
|
|
|
|
}
|
|
|
|
catch {
|
|
|
|
$_.Exception.Message | Write-Error
|
|
|
|
$ErrorParams = @{
|
|
|
|
ErrorAction = 'Stop'
|
|
|
|
Exception = [System.IO.IOException]::new(
|
|
|
|
'Unable to convert or write the CredentialStore'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Write-Error @ErrorParams
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($PassThru.IsPresent) {
|
|
|
|
return $CredentialStoreObj
|
2017-09-21 13:32:15 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-16 12:55:29 +01:00
|
|
|
|
2022-06-28 08:56:33 +02:00
|
|
|
end {}
|
2017-09-21 13:32:15 +02:00
|
|
|
}
|