[PowerShell] Add useOneOfDiscriminatorLookup option (#6516)

* add useOneOfDiscriminatorLookup option

* update doc
This commit is contained in:
William Cheng
2020-06-02 14:07:05 +08:00
committed by GitHub
parent 0fbbbe8a95
commit c1cf63e81c
7 changed files with 631 additions and 296 deletions

View File

@@ -388,3 +388,140 @@ function Get-PSUrlFromHostSetting {
}
}
<#
.SYNOPSIS
Sets the configuration for http signing.
.DESCRIPTION
Sets the configuration for the HTTP signature security scheme.
The HTTP signature security scheme is used to sign HTTP requests with a key
which is in possession of the API client.
An 'Authorization' header is calculated by creating a hash of select headers,
and optionally the body of the HTTP request, then signing the hash value using
a key. The 'Authorization' header is added to outbound HTTP requests.
Ref: https://openapi-generator.tech
.PARAMETER KeyId
KeyId for HTTP signing
.PARAMETER KeyFilePath
KeyFilePath for HTTP signing
.PARAMETER KeyPassPhrase
KeyPassPhrase, if the HTTP signing key is protected
.PARAMETER HttpSigningHeader
HttpSigningHeader list of HTTP headers used to calculate the signature. The two special signature headers '(request-target)' and '(created)'
SHOULD be included.
The '(created)' header expresses when the signature was created.
The '(request-target)' header is a concatenation of the lowercased :method, an
ASCII space, and the :path pseudo-headers.
If no headers are specified then '(created)' sets as default.
.PARAMETER HashAlgorithm
HashAlgrithm to calculate the hash, Supported values are "sha256" and "sha512"
.PARAMETER SigningAlgorithm
SigningAlgorithm specifies the signature algorithm, supported values are "RSASSA-PKCS1-v1_5" and "RSASSA-PSS"
RSA key : Supported values "RSASSA-PKCS1-v1_5" and "RSASSA-PSS", for ECDSA key this parameter is not applicable
.PARAMETER SignatureValidityPeriod
SignatureValidityPeriod specifies the signature maximum validity time in seconds. It accepts integer value
.OUTPUTS
System.Collections.Hashtable
#>
function Set-PSConfigurationHttpSigning {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$KeyId,
[Parameter(Mandatory = $true)]
[string]$KeyFilePath,
[Parameter(Mandatory = $false)]
[securestring]$KeyPassPhrase,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string[]] $HttpSigningHeader = @("(created)"),
[Parameter(Mandatory = $false)]
[ValidateSet("sha256", "sha512")]
[string] $HashAlgorithm = "sha256",
[Parameter(Mandatory = $false)]
[ValidateSet("RSASSA-PKCS1-v1_5", "RSASSA-PSS")]
[string]$SigningAlgorithm ,
[Parameter(Mandatory = $false)]
[int]$SignatureValidityPeriod
)
Process {
$httpSignatureConfiguration = @{ }
if (Test-Path -Path $KeyFilePath) {
$httpSignatureConfiguration["KeyId"] = $KeyId
$httpSignatureConfiguration["KeyFilePath"] = $KeyFilePath
}
else {
throw "Private key file path does not exist"
}
$keyType = Get-PSKeyTypeFromFile -KeyFilePath $KeyFilePath
if ([String]::IsNullOrEmpty($SigningAlgorithm)) {
if ($keyType -eq "RSA") {
$SigningAlgorithm = "RSASSA-PKCS1-v1_5"
}
}
if ($keyType -eq "RSA" -and
($SigningAlgorithm -ne "RSASSA-PKCS1-v1_5" -and $SigningAlgorithm -ne "RSASSA-PSS" )) {
throw "Provided Key and SigningAlgorithm : $SigningAlgorithm is not compatible."
}
if ($HttpSigningHeader -contains "(expires)" -and $SignatureValidityPeriod -le 0) {
throw "SignatureValidityPeriod must be greater than 0 seconds."
}
if ($HttpSigningHeader -contains "(expires)") {
$httpSignatureConfiguration["SignatureValidityPeriod"] = $SignatureValidityPeriod
}
if ($null -ne $HttpSigningHeader -and $HttpSigningHeader.Length -gt 0) {
$httpSignatureConfiguration["HttpSigningHeader"] = $HttpSigningHeader
}
if ($null -ne $HashAlgorithm ) {
$httpSignatureConfiguration["HashAlgorithm"] = $HashAlgorithm
}
if ($null -ne $SigningAlgorithm) {
$httpSignatureConfiguration["SigningAlgorithm"] = $SigningAlgorithm
}
if ($null -ne $KeyPassPhrase) {
$httpSignatureConfiguration["KeyPassPhrase"] = $KeyPassPhrase
}
$Script:Configuration["HttpSigning"] = New-Object -TypeName PSCustomObject -Property $httpSignatureConfiguration
}
}
<#
.SYNOPSIS
Get the configuration object 'PSConfigurationHttpSigning'.
.DESCRIPTION
Get the configuration object 'PSConfigurationHttpSigning'.
.OUTPUTS
[PSCustomObject]
#>
function Get-PSConfigurationHttpSigning{
$httpSignatureConfiguration = $Script:Configuration["HttpSigning"]
return $httpSignatureConfiguration
}

View File

@@ -7,64 +7,11 @@
<#
.SYNOPSIS
Get the API key Id and API key file path.
Gets the headers for HTTP signature.
.DESCRIPTION
Get the API key Id and API key file path. If no api prefix is provided then it use default api key prefix 'Signature'
.OUTPUTS
PSCustomObject : This contains APIKeyId, APIKeyFilePath, APIKeyPrefix
#>
function Get-PSAPIKeyInfo {
$ApiKeysList = $Script:Configuration['ApiKey']
$ApiKeyPrefixList = $Script:Configuration['ApiKeyPrefix']
$apiPrefix = "Signature"
if ($null -eq $ApiKeysList -or $ApiKeysList.Count -eq 0) {
throw "Unable to reterieve the api key details"
}
if ($null -eq $ApiKeyPrefixList -or $ApiKeyPrefixList.Count -eq 0) {
Write-Verbose "Unable to reterieve the api key prefix details,setting it to default ""Signature"""
}
foreach ($item in $ApiKeysList.GetEnumerator()) {
if (![string]::IsNullOrEmpty($item.Name)) {
if (Test-Path -Path $item.Value) {
$apiKey = $item.Value
$apikeyId = $item.Name
break;
}
else {
throw "API key file path does not exist."
}
}
}
if ($ApiKeyPrefixList.ContainsKey($apikeyId)) {
$apiPrefix = ApiKeyPrefixList[$apikeyId]
}
if ($apikeyId -and $apiKey -and $apiPrefix) {
$result = New-Object -Type PSCustomObject -Property @{
ApiKeyId = $apikeyId;
ApiKeyFilePath = $apiKey
ApiKeyPrefix = $apiPrefix
}
}
else {
return $null
}
return $result
}
<#
.SYNOPSIS
Gets the headers for http signed auth.
.DESCRIPTION
Gets the headers for the http signed auth. It use (targetpath), date, host and body digest to create authorization header.
Gets the headers for the http sigature.
.PARAMETER Method
Http method
HTTP method
.PARAMETER UriBuilder
UriBuilder for url and query parameter
.PARAMETER Body
@@ -76,93 +23,393 @@ function Get-PSHttpSignedHeader {
param(
[string]$Method,
[System.UriBuilder]$UriBuilder,
[string]$Body
[string]$Body,
[hashtable]$RequestHeader
)
$HEADER_REQUEST_TARGET = '(request-target)'
# The time when the HTTP signature was generated.
$HEADER_CREATED = '(created)'
# The time when the HTTP signature expires. The API server should reject HTTP requests
# that have expired.
$HEADER_EXPIRES = '(expires)'
# The 'Host' header.
$HEADER_HOST = 'Host'
# The 'Date' header.
$HEADER_DATE = 'Date'
# When the 'Digest' header is included in the HTTP signature, the client automatically
# computes the digest of the HTTP request body, per RFC 3230.
$HEADER_DIGEST = 'Digest'
# The 'Authorization' header is automatically generated by the client. It includes
# the list of signed headers and a base64-encoded signature.
$HEADER_AUTHORIZATION = 'Authorization'
#Hash table to store singed headers
$HttpSignedHeader = @{}
$HttpSignedRequestHeader = @{ }
$HttpSignatureHeader = @{ }
$TargetHost = $UriBuilder.Host
#Check for Authentication type
$apiKeyInfo = Get-PSAPIKeyInfo
if ($null -eq $apiKeyInfo) {
throw "Unable to reterieve the api key info "
}
$httpSigningConfiguration = Get-PSConfigurationHttpSigning
$Digest = $null
#get the body digest
$bodyHash = Get-PSStringHash -String $Body
$Digest = [String]::Format("SHA-256={0}", [Convert]::ToBase64String($bodyHash))
#get the date in UTC
$bodyHash = Get-PSStringHash -String $Body -HashName $httpSigningConfiguration.HashAlgorithm
if ($httpSigningConfiguration.HashAlgorithm -eq "SHA256") {
$Digest = [String]::Format("SHA-256={0}", [Convert]::ToBase64String($bodyHash))
}
elseif ($httpSigningConfiguration.HashAlgorithm -eq "SHA512") {
$Digest = [String]::Format("SHA-512={0}", [Convert]::ToBase64String($bodyHash))
}
$dateTime = Get-Date
#get the date in UTC
$currentDate = $dateTime.ToUniversalTime().ToString("r")
$requestTargetPath = [string]::Format("{0} {1}{2}",$Method.ToLower(),$UriBuilder.Path.ToLower(),$UriBuilder.Query)
$h_requestTarget = [string]::Format("(request-target): {0}",$requestTargetPath)
$h_cdate = [string]::Format("date: {0}",$currentDate)
$h_digest = [string]::Format("digest: {0}",$Digest)
$h_targetHost = [string]::Format("host: {0}",$TargetHost)
foreach ($headerItem in $httpSigningConfiguration.HttpSigningHeader) {
if ($headerItem -eq $HEADER_REQUEST_TARGET) {
$requestTargetPath = [string]::Format("{0} {1}{2}", $Method.ToLower(), $UriBuilder.Path, $UriBuilder.Query)
$HttpSignatureHeader.Add($HEADER_REQUEST_TARGET, $requestTargetPath)
}
elseif ($headerItem -eq $HEADER_CREATED) {
$created = Get-PSUnixTime -Date $dateTime -TotalTime TotalSeconds
$HttpSignatureHeader.Add($HEADER_CREATED, $created)
}
elseif ($headerItem -eq $HEADER_EXPIRES) {
$expire = $dateTime.AddSeconds($httpSigningConfiguration.SignatureValidityPeriod)
$expireEpocTime = Get-PSUnixTime -Date $expire -TotalTime TotalSeconds
$HttpSignatureHeader.Add($HEADER_EXPIRES, $expireEpocTime)
}
elseif ($headerItem -eq $HEADER_HOST) {
$HttpSignedRequestHeader[$HEADER_HOST] = $TargetHost
$HttpSignatureHeader.Add($HEADER_HOST.ToLower(), $TargetHost)
}
elseif ($headerItem -eq $HEADER_DATE) {
$HttpSignedRequestHeader[$HEADER_DATE] = $currentDate
$HttpSignatureHeader.Add($HEADER_DATE.ToLower(), $currentDate)
}
elseif ($headerItem -eq $HEADER_DIGEST) {
$HttpSignedRequestHeader[$HEADER_DIGEST] = $Digest
$HttpSignatureHeader.Add($HEADER_DIGEST.ToLower(), $Digest)
}elseif($RequestHeader.ContainsKey($headerItem)){
$HttpSignatureHeader.Add($headerItem.ToLower(), $RequestHeader[$headerItem])
}else{
throw "Cannot sign HTTP request. Request does not contain the $headerItem header."
}
}
$stringToSign = [String]::Format("{0}`n{1}`n{2}`n{3}",
$h_requestTarget,$h_cdate,
$h_targetHost,$h_digest)
# header's name separated by space
$headersKeysString = $HttpSignatureHeader.Keys -join " "
$headerValuesList = @()
foreach ($item in $HttpSignatureHeader.GetEnumerator()) {
$headerValuesList += [string]::Format("{0}: {1}", $item.Name, $item.Value)
}
#Concatinate headers value separated by new line
$headerValuesString = $headerValuesList -join "`n"
$hashedString = Get-PSStringHash -String $stringToSign
$signedHeader = Get-PSRSASHA256SignedString -APIKeyFilePath $apiKeyInfo.ApiKeyFilePath -DataToSign $hashedString
$authorizationHeader = [string]::Format("{0} keyId=""{1}"",algorithm=""rsa-sha256"",headers=""(request-target) date host digest"",signature=""{2}""",
$apiKeyInfo.ApiKeyPrefix, $apiKeyInfo.ApiKeyId, $signedHeader)
#Gets the hash of the headers value
$signatureHashString = Get-PSStringHash -String $headerValuesString -HashName $httpSigningConfiguration.HashAlgorithm
#Gets the Key type to select the correct signing alogorithm
$KeyType = Get-PSKeyTypeFromFile -KeyFilePath $httpSigningConfiguration.KeyFilePath
if ($keyType -eq "RSA") {
$headerSignatureStr = Get-PSRSASignature -PrivateKeyFilePath $httpSigningConfiguration.KeyFilePath `
-DataToSign $signatureHashString `
-HashAlgorithmName $httpSigningConfiguration.HashAlgorithm `
-KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase `
-SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm
}
elseif ($KeyType -eq "EC") {
$headerSignatureStr = Get-PSECDSASignature -ECKeyFilePath $httpSigningConfiguration.KeyFilePath `
-DataToSign $signatureHashString `
-HashAlgorithmName $httpSigningConfiguration.HashAlgorithm `
-KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase
}
#Depricated
<#$cryptographicScheme = Get-PSCryptographicScheme -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm `
-HashAlgorithm $httpSigningConfiguration.HashAlgorithm
#>
$cryptographicScheme = "hs2019"
$authorizationHeaderValue = [string]::Format("Signature keyId=""{0}"",algorithm=""{1}""",
$httpSigningConfiguration.KeyId, $cryptographicScheme)
if ($HttpSignatureHeader.ContainsKey($HEADER_CREATED)) {
$authorizationHeaderValue += [string]::Format(",created={0}", $HttpSignatureHeader[$HEADER_CREATED])
}
if ($HttpSignatureHeader.ContainsKey($HEADER_EXPIRES)) {
$authorizationHeaderValue += [string]::Format(",expires={0}", $HttpSignatureHeader[$HEADER_EXPIRES])
}
$HttpSignedHeader["Date"] = $currentDate
$HttpSignedHeader["Host"] = $TargetHost
$HttpSignedHeader["Content-Type"] = "application/json"
$HttpSignedHeader["Digest"] = $Digest
$HttpSignedHeader["Authorization"] = $authorizationHeader
return $HttpSignedHeader
$authorizationHeaderValue += [string]::Format(",headers=""{0}"",signature=""{1}""",
$headersKeysString , $headerSignatureStr)
$HttpSignedRequestHeader[$HEADER_AUTHORIZATION] = $authorizationHeaderValue
return $HttpSignedRequestHeader
}
<#
.SYNOPSIS
Gets the headers for http signed auth.
Gets the RSA signature
.DESCRIPTION
Gets the headers for the http signed auth. It use (targetpath), date, host and body digest to create authorization header.
.PARAMETER APIKeyFilePath
Gets the RSA signature for the http signing
.PARAMETER PrivateKeyFilePath
Specify the API key file path
.PARAMETER DataToSign
Specify the data to sign
.PARAMETER HashAlgorithmName
HashAlgorithm to calculate the hash
.PARAMETER KeyPassPhrase
KeyPassPhrase for the encrypted key
.OUTPUTS
String
Base64String
#>
function Get-PSRSASHA256SignedString {
function Get-PSRSASignature {
Param(
[string]$APIKeyFilePath,
[byte[]]$DataToSign
[string]$PrivateKeyFilePath,
[byte[]]$DataToSign,
[string]$HashAlgorithmName,
[string]$SigningAlgorithm,
[securestring]$KeyPassPhrase
)
try {
$rsa_provider_path = Join-Path -Path $PSScriptRoot -ChildPath "PSRSAEncryptionProvider.cs"
$rsa_provider_sourceCode = Get-Content -Path $rsa_provider_path -Raw
Add-Type -TypeDefinition $rsa_provider_sourceCode
$signed_string = [RSAEncryption.RSAEncryptionProvider]::GetRSASignb64encode($APIKeyFilePath, $DataToSign)
if ($null -eq $signed_string) {
throw "Unable to sign the header using the API key"
if ($hashAlgorithmName -eq "sha256") {
$hashAlgo = [System.Security.Cryptography.HashAlgorithmName]::SHA256
}
return $signed_string
elseif ($hashAlgorithmName -eq "sha512") {
$hashAlgo = [System.Security.Cryptography.HashAlgorithmName]::SHA512
}
if ($PSVersionTable.PSVersion.Major -ge 7) {
$ecKeyHeader = "-----BEGIN RSA PRIVATE KEY-----"
$ecKeyFooter = "-----END RSA PRIVATE KEY-----"
$keyStr = Get-Content -Path $PrivateKeyFilePath -Raw
$ecKeyBase64String = $keyStr.Replace($ecKeyHeader, "").Replace($ecKeyFooter, "").Trim()
$keyBytes = [System.Convert]::FromBase64String($ecKeyBase64String)
$rsa = [System.Security.Cryptography.RSACng]::new()
[int]$bytCount = 0
$rsa.ImportRSAPrivateKey($keyBytes, [ref] $bytCount)
if ($SigningAlgorithm -eq "RSASSA-PSS") {
$signedBytes = $rsa.SignHash($DataToSign, $hashAlgo, [System.Security.Cryptography.RSASignaturePadding]::Pss)
}
else {
$signedBytes = $rsa.SignHash($DataToSign, $hashAlgo, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
}
}
else {
$rsa_provider_path = Join-Path -Path $PSScriptRoot -ChildPath "PSRSAEncryptionProvider.cs"
$rsa_provider_sourceCode = Get-Content -Path $rsa_provider_path -Raw
Add-Type -TypeDefinition $rsa_provider_sourceCode
[System.Security.Cryptography.RSA]$rsa = [RSAEncryption.RSAEncryptionProvider]::GetRSAProviderFromPemFile($PrivateKeyFilePath, $KeyPassPhrase)
if ($SigningAlgorithm -eq "RSASSA-PSS") {
throw "$SigningAlgorithm is not supported on $($PSVersionTable.PSVersion)"
}
else {
$signedBytes = $rsa.SignHash($DataToSign, $hashAlgo, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
}
}
$signedString = [Convert]::ToBase64String($signedBytes)
return $signedString
}
catch {
throw $_
}
}
<#
.SYNOPSIS
Gets the ECDSA signature
.DESCRIPTION
Gets the ECDSA signature for the http signing
.PARAMETER PrivateKeyFilePath
Specify the API key file path
.PARAMETER DataToSign
Specify the data to sign
.PARAMETER HashAlgorithmName
HashAlgorithm to calculate the hash
.PARAMETER KeyPassPhrase
KeyPassPhrase for the encrypted key
.OUTPUTS
Base64String
#>
function Get-PSECDSASignature {
param(
[Parameter(Mandatory = $true)]
[string]$ECKeyFilePath,
[Parameter(Mandatory = $true)]
[byte[]]$DataToSign,
[Parameter(Mandatory = $false)]
[String]$HashAlgorithmName,
[Parameter(Mandatory = $false)]
[securestring]$KeyPassPhrase
)
if (!(Test-Path -Path $ECKeyFilePath)) {
throw "key file path does not exist."
}
if($PSVersionTable.PSVersion.Major -lt 7){
throw "ECDSA key is not supported on $($PSVersionTable.PSVersion), Use PSVersion 7.0 and above"
}
$ecKeyHeader = "-----BEGIN EC PRIVATE KEY-----"
$ecKeyFooter = "-----END EC PRIVATE KEY-----"
$keyStr = Get-Content -Path $ECKeyFilePath -Raw
$ecKeyBase64String = $keyStr.Replace($ecKeyHeader, "").Replace($ecKeyFooter, "").Trim()
$keyBytes = [System.Convert]::FromBase64String($ecKeyBase64String)
#$cngKey = [System.Security.Cryptography.CngKey]::Import($keyBytes,[System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
#$ecdsa = [System.Security.Cryptography.ECDsaCng]::New($cngKey)
$ecdsa = [System.Security.Cryptography.ECDsaCng]::New()
[int]$bytCount =0
if(![string]::IsNullOrEmpty($KeyPassPhrase)){
$ecdsa.ImportEncryptedPkcs8PrivateKey($KeyPassPhrase,$keyBytes,[ref]$bytCount)
}
else{
$ecdsa.ImportPkcs8PrivateKey($keyBytes,[ref]$bytCount)
}
if ($HashAlgorithmName -eq "sha512") {
$ecdsa.HashAlgorithm = [System.Security.Cryptography.CngAlgorithm]::Sha512
}
else {
$ecdsa.HashAlgorithm = [System.Security.Cryptography.CngAlgorithm]::Sha256
}
$signedBytes = $ecdsa.SignHash($DataToSign)
$signedString = [System.Convert]::ToBase64String($signedBytes)
return $signedString
}
<#
.Synopsis
Gets the hash of string.
.Description
Gets the hash of string
.Parameter String
Specifies the string to calculate the hash
.Parameter HashName
Specifies the hash name to calculate the hash, Accepted values are "SHA1", "SHA256" and "SHA512"
It is recommneded not to use "SHA1" to calculate the Hash
.Outputs
String
#>
Function Get-PSStringHash([String] $String, $HashName = "SHA256") {
Function Get-PSStringHash {
param(
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[string]$String,
[Parameter(Mandatory = $true)]
[ValidateSet("SHA1", "SHA256", "SHA512")]
$HashName
)
$hashAlogrithm = [System.Security.Cryptography.HashAlgorithm]::Create($HashName)
$hashAlogrithm.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))
}
<#
.Synopsis
Gets the Unix time.
.Description
Gets the Unix time
.Parameter Date
Specifies the date to calculate the unix time
.Parameter ToTalTime
Specifies the total time , Accepted values are "TotalDays", "TotalHours", "TotalMinutes", "TotalSeconds" and "TotalMilliseconds"
.Outputs
Integer
#>
function Get-PSUnixTime {
param(
[Parameter(Mandatory = $true)]
[DateTime]$Date,
[Parameter(Mandatory = $false)]
[ValidateSet("TotalDays", "TotalHours", "TotalMinutes", "TotalSeconds", "TotalMilliseconds")]
[string]$TotalTime = "TotalSeconds"
)
$date1 = Get-Date -Date "01/01/1970"
$timespan = New-TimeSpan -Start $date1 -End $Date
switch ($TotalTime) {
"TotalDays" { [int]$timespan.TotalDays }
"TotalHours" { [int]$timespan.TotalHours }
"TotalMinutes" { [int]$timespan.TotalMinutes }
"TotalSeconds" { [int]$timespan.TotalSeconds }
"TotalMilliseconds" { [int]$timespan.TotalMilliseconds }
}
}
function Get-PSCryptographicScheme {
param(
[Parameter(Mandatory = $true)]
[string]$SigningAlgorithm,
[Parameter(Mandatory = $true)]
[string]$HashAlgorithm
)
$rsaSigntureType = @("RSASSA-PKCS1-v1_5", "RSASSA-PSS")
$SigningAlgorithm = $null
if ($rsaSigntureType -contains $SigningAlgorithm) {
switch ($HashAlgorithm) {
"sha256" { $SigningAlgorithm = "rsa-sha256" }
"sha512" { $SigningAlgorithm = "rsa-sha512" }
}
}
return $SigningAlgorithm
}
<#
.Synopsis
Gets the key type from the pem file.
.Description
Gets the key type from the pem file.
.Parameter KeyFilePath
Specifies the key file path (pem file)
.Outputs
String
#>
function Get-PSKeyTypeFromFile {
param(
[Parameter(Mandatory = $true)]
[string]$KeyFilePath
)
if (-not(Test-Path -Path $KeyFilePath)) {
throw "Key file path does not exist."
}
$ecPrivateKeyHeader = "BEGIN EC PRIVATE KEY"
$ecPrivateKeyFooter = "END EC PRIVATE KEY"
$rsaPrivateKeyHeader = "BEGIN RSA PRIVATE KEY"
$rsaPrivateFooter = "END RSA PRIVATE KEY"
$pkcs8Header = "BEGIN PRIVATE KEY"
$pkcs8Footer = "END PRIVATE KEY"
$keyType = $null
$key = Get-Content -Path $KeyFilePath
if ($key[0] -match $rsaPrivateKeyHeader -and $key[$key.Length - 1] -match $rsaPrivateFooter) {
$KeyType = "RSA"
}
elseif ($key[0] -match $ecPrivateKeyHeader -and $key[$key.Length - 1] -match $ecPrivateKeyFooter) {
$keyType = "EC"
}
elseif ($key[0] -match $ecPrivateKeyHeader -and $key[$key.Length - 1] -match $ecPrivateKeyFooter) {
<#this type of key can hold many type different types of private key, but here due lack of pem header
Considering this as EC key
#>
#TODO :- update the key based on oid
$keyType = "EC"
}
else {
throw "Either the key is invalid or key is not supported"
}
return $keyType
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
@@ -10,15 +11,97 @@ namespace RSAEncryption
{
public class RSAEncryptionProvider
{
public static RSACryptoServiceProvider GetRSAProviderFromPemFile(String pemfile,SecureString keyPassPharse = null)
{
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
bool isPrivateKeyFile = true;
byte[] pemkey = null;
if (!File.Exists(pemfile))
{
throw new Exception("private key file does not exist.");
}
string pemstr = File.ReadAllText(pemfile).Trim();
if (pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter))
{
isPrivateKeyFile = false;
}
if (isPrivateKeyFile)
{
pemkey = ConvertPrivateKeyToBytes(pemstr,keyPassPharse);
if (pemkey == null)
{
return null;
}
return DecodeRSAPrivateKey(pemkey);
}
return null ;
}
static byte[] ConvertPrivateKeyToBytes(String instr, SecureString keyPassPharse = null)
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter))
{
return null;
}
StringBuilder sb = new StringBuilder(pemstr);
sb.Replace(pemprivheader, "");
sb.Replace(pemprivfooter, "");
String pvkstr = sb.ToString().Trim();
try
{ // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
binkey = Convert.FromBase64String(pvkstr);
return binkey;
}
catch (System.FormatException)
{
StringReader str = new StringReader(pvkstr);
//-------- read PEM encryption info. lines and extract salt -----
if (!str.ReadLine().StartsWith("Proc-Type: 4,ENCRYPTED"))
return null;
String saltline = str.ReadLine();
if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,"))
return null;
String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim();
byte[] salt = new byte[saltstr.Length / 2];
for (int i = 0; i < salt.Length; i++)
salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16);
if (!(str.ReadLine() == ""))
return null;
//------ remaining b64 data is encrypted RSA key ----
String encryptedstr = str.ReadToEnd();
try
{ //should have b64 encrypted RSA key now
binkey = Convert.FromBase64String(encryptedstr);
}
catch (System.FormatException)
{ //data is not in base64 fromat
return null;
}
byte[] deskey = GetEncryptedKey(salt, keyPassPharse, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
if (deskey == null)
return null;
//------ Decrypt the encrypted 3des-encrypted RSA private key ------
byte[] rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
return rsakey;
}
}
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
const String pemp8header = "-----BEGIN PRIVATE KEY-----";
const String pemp8footer = "-----END PRIVATE KEY-----";
const String pemp8encheader = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
const String pemp8encfooter = "-----END ENCRYPTED PRIVATE KEY-----";
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
@@ -46,7 +129,6 @@ namespace RSAEncryption
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
@@ -72,19 +154,6 @@ namespace RSAEncryption
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
/*Console.WriteLine("showing components ..");
if (true)
{
showBytes("\nModulus", MODULUS);
showBytes("\nExponent", E);
showBytes("\nD", D);
showBytes("\nP", P);
showBytes("\nQ", Q);
showBytes("\nDP", DP);
showBytes("\nDQ", DQ);
showBytes("\nIQ", IQ);
}*/
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
@@ -140,94 +209,17 @@ namespace RSAEncryption
return count;
}
static byte[] DecodeOpenSSLPrivateKey(String instr)
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
String pemstr = instr.Trim();
byte[] binkey;
if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter))
return null;
StringBuilder sb = new StringBuilder(pemstr);
sb.Replace(pemprivheader, ""); //remove headers/footers, if present
sb.Replace(pemprivfooter, "");
String pvkstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace
try
{ // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
binkey = Convert.FromBase64String(pvkstr);
return binkey;
}
catch (System.FormatException)
{ //if can't b64 decode, it must be an encrypted private key
//Console.WriteLine("Not an unencrypted OpenSSL PEM private key");
}
StringReader str = new StringReader(pvkstr);
//-------- read PEM encryption info. lines and extract salt -----
if (!str.ReadLine().StartsWith("Proc-Type: 4,ENCRYPTED"))
return null;
String saltline = str.ReadLine();
if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,"))
return null;
String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim();
byte[] salt = new byte[saltstr.Length / 2];
for (int i = 0; i < salt.Length; i++)
salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16);
if (!(str.ReadLine() == ""))
return null;
//------ remaining b64 data is encrypted RSA key ----
String encryptedstr = str.ReadToEnd();
try
{ //should have b64 encrypted RSA key now
binkey = Convert.FromBase64String(encryptedstr);
}
catch (System.FormatException)
{ // bad b64 data.
return null;
}
//------ Get the 3DES 24 byte key using PDK used by OpenSSL ----
SecureString despswd = GetSecPswd("Enter password to derive 3DES key==>");
//Console.Write("\nEnter password to derive 3DES key: ");
//String pswd = Console.ReadLine();
byte[] deskey = GetOpenSSL3deskey(salt, despswd, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
if (deskey == null)
return null;
//showBytes("3DES key", deskey) ;
//------ Decrypt the encrypted 3des-encrypted RSA private key ------
byte[] rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
if (rsakey != null)
return rsakey; //we have a decrypted RSA private key
else
{
Console.WriteLine("Failed to decrypt RSA private key; probably wrong password.");
return null;
}
}
static byte[] GetOpenSSL3deskey(byte[] salt, SecureString secpswd, int count, int miter)
static byte[] GetEncryptedKey(byte[] salt, SecureString secpswd, int count, int miter)
{
IntPtr unmanagedPswd = IntPtr.Zero;
int HASHLENGTH = 16; //MD5 bytes
byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store contatenated Mi hashed results
byte[] psbytes = new byte[secpswd.Length];
unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length);
Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
//UTF8Encoding utf8 = new UTF8Encoding();
//byte[] psbytes = utf8.GetBytes(pswd);
// --- contatenate salt and pswd bytes into fixed data array ---
byte[] data00 = new byte[psbytes.Length + salt.Length];
Array.Copy(psbytes, data00, psbytes.Length); //copy the pswd bytes
@@ -248,15 +240,12 @@ namespace RSAEncryption
Array.Copy(result, hashtarget, result.Length);
Array.Copy(data00, 0, hashtarget, result.Length, data00.Length);
result = hashtarget;
//Console.WriteLine("Updated new initial hash target:") ;
//showBytes(result) ;
}
for (int i = 0; i < count; i++)
result = md5.ComputeHash(result);
Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial
}
//showBytes("Final key material", keymaterial);
byte[] deskey = new byte[24];
Array.Copy(keymaterial, deskey, deskey.Length);
@@ -265,46 +254,9 @@ namespace RSAEncryption
Array.Clear(result, 0, result.Length);
Array.Clear(hashtarget, 0, hashtarget.Length);
Array.Clear(keymaterial, 0, keymaterial.Length);
return deskey;
}
public static string GetRSASignb64encode(string private_key_path, byte[] digest)
{
RSACryptoServiceProvider cipher = new RSACryptoServiceProvider();
cipher = GetRSAProviderFromPemFile(private_key_path);
RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(cipher);
RSAFormatter.SetHashAlgorithm("SHA256");
byte[] signedHash = RSAFormatter.CreateSignature(digest);
return Convert.ToBase64String(signedHash);
}
public static RSACryptoServiceProvider GetRSAProviderFromPemFile(String pemfile)
{
bool isPrivateKeyFile = true;
if (!File.Exists(pemfile))
{
throw new Exception("pemfile does not exist.");
}
string pemstr = File.ReadAllText(pemfile).Trim();
if (pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter))
isPrivateKeyFile = false;
byte[] pemkey = null;
if (isPrivateKeyFile)
pemkey = DecodeOpenSSLPrivateKey(pemstr);
if (pemkey == null)
return null;
if (isPrivateKeyFile)
{
return DecodeRSAPrivateKey(pemkey);
}
return null;
}
static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV)
{
MemoryStream memst = new MemoryStream();
@@ -317,61 +269,11 @@ namespace RSAEncryption
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
catch (Exception){
return null;
}
byte[] decryptedData = memst.ToArray();
return decryptedData;
}
static SecureString GetSecPswd(String prompt)
{
SecureString password = new SecureString();
Console.ForegroundColor = ConsoleColor.Gray;
Console.ForegroundColor = ConsoleColor.Magenta;
while (true)
{
ConsoleKeyInfo cki = Console.ReadKey(true);
if (cki.Key == ConsoleKey.Enter)
{
Console.ForegroundColor = ConsoleColor.Gray;
return password;
}
else if (cki.Key == ConsoleKey.Backspace)
{
// remove the last asterisk from the screen...
if (password.Length > 0)
{
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
password.RemoveAt(password.Length - 1);
}
}
else if (cki.Key == ConsoleKey.Escape)
{
Console.ForegroundColor = ConsoleColor.Gray;
return password;
}
else if (Char.IsLetterOrDigit(cki.KeyChar) || Char.IsSymbol(cki.KeyChar))
{
if (password.Length < 20)
{
password.AppendChar(cki.KeyChar);
}
else
{
Console.Beep();
}
}
else
{
Console.Beep();
}
}
}
}
}
}