diff --git a/modules/openapi-generator/src/main/resources/powershell/api_client.mustache b/modules/openapi-generator/src/main/resources/powershell/api_client.mustache index f98ae6695e6b..95b23b26d5fa 100644 --- a/modules/openapi-generator/src/main/resources/powershell/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/api_client.mustache @@ -91,11 +91,13 @@ function Invoke-{{{apiNamePrefix}}}ApiClient { {{#hasHttpSignatureMethods}} # http signature authentication - if ($null -ne $Configuration['ApiKey'] -and $Configuration['ApiKey'].Count -gt 0) { + $httpSigningConfig = Get-{{{apiNamePrefix}}}ConfigurationHttpSigning + if ($null -ne $httpSigningConfig) { $httpSignHeaderArgument = @{ Method = $Method UriBuilder = $UriBuilder Body = $Body + RequestHeader = $HeaderParameters } $signedHeader = Get-{{{apiNamePrefix}}}HttpSignedHeader @httpSignHeaderArgument if($null -ne $signedHeader -and $signedHeader.Count -gt 0){ diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index 4d17452be17d..622bce2b65ae 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -372,3 +372,140 @@ function Get-{{apiNamePrefix}}UrlFromHostSetting { } } + +<# +.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-{{{apiNamePrefix}}}ConfigurationHttpSigning { + [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-{{{apiNamePrefix}}}KeyTypeFromFile -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 '{{{apiNamePrefix}}}ConfigurationHttpSigning'. + +.DESCRIPTION + +Get the configuration object '{{{apiNamePrefix}}}ConfigurationHttpSigning'. + +.OUTPUTS + +[PSCustomObject] +#> +function Get-{{{apiNamePrefix}}}ConfigurationHttpSigning{ + + $httpSignatureConfiguration = $Script:Configuration["HttpSigning"] + return $httpSignatureConfiguration +} diff --git a/modules/openapi-generator/src/main/resources/powershell/http_signature_auth.mustache b/modules/openapi-generator/src/main/resources/powershell/http_signature_auth.mustache index aeb74be678dc..69ab0b43bb10 100644 --- a/modules/openapi-generator/src/main/resources/powershell/http_signature_auth.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/http_signature_auth.mustache @@ -1,64 +1,11 @@ {{>partial_header}} <# .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-{{{apiNamePrefix}}}APIKeyInfo { - $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 @@ -70,93 +17,393 @@ function Get-{{{apiNamePrefix}}}HttpSignedHeader { 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 + $httpSigningConfiguration = Get-{{{apiNamePrefix}}}ConfigurationHttpSigning + $Digest = $null + + #get the body digest + $bodyHash = Get-{{{apiNamePrefix}}}StringHash -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") + + 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-{{{apiNamePrefix}}}UnixTime -Date $dateTime -TotalTime TotalSeconds + $HttpSignatureHeader.Add($HEADER_CREATED, $created) + } + elseif ($headerItem -eq $HEADER_EXPIRES) { + $expire = $dateTime.AddSeconds($httpSigningConfiguration.SignatureValidityPeriod) + $expireEpocTime = Get-{{{apiNamePrefix}}}UnixTime -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." + } + } - #Check for Authentication type - $apiKeyInfo = Get-{{{apiNamePrefix}}}APIKeyInfo - if ($null -eq $apiKeyInfo) { - throw "Unable to reterieve the api key info " + # 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" + + #Gets the hash of the headers value + $signatureHashString = Get-{{{apiNamePrefix}}}StringHash -String $headerValuesString -HashName $httpSigningConfiguration.HashAlgorithm - #get the body digest - $bodyHash = Get-{{{apiNamePrefix}}}StringHash -String $Body - $Digest = [String]::Format("SHA-256={0}", [Convert]::ToBase64String($bodyHash)) + #Gets the Key type to select the correct signing alogorithm + $KeyType = Get-{{{apiNamePrefix}}}KeyTypeFromFile -KeyFilePath $httpSigningConfiguration.KeyFilePath - #get the date in UTC - $dateTime = Get-Date - $currentDate = $dateTime.ToUniversalTime().ToString("r") + if ($keyType -eq "RSA") { + $headerSignatureStr = Get-{{{apiNamePrefix}}}RSASignature -PrivateKeyFilePath $httpSigningConfiguration.KeyFilePath ` + -DataToSign $signatureHashString ` + -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm ` + -KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase ` + -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm + } + elseif ($KeyType -eq "EC") { + $headerSignatureStr = Get-{{{apiNamePrefix}}}ECDSASignature -ECKeyFilePath $httpSigningConfiguration.KeyFilePath ` + -DataToSign $signatureHashString ` + -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm ` + -KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase + } + #Depricated + <#$cryptographicScheme = Get-{{{apiNamePrefix}}}CryptographicScheme -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm ` + -HashAlgorithm $httpSigningConfiguration.HashAlgorithm + #> + $cryptographicScheme = "hs2019" + $authorizationHeaderValue = [string]::Format("Signature keyId=""{0}"",algorithm=""{1}""", + $httpSigningConfiguration.KeyId, $cryptographicScheme) - $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) + if ($HttpSignatureHeader.ContainsKey($HEADER_CREATED)) { + $authorizationHeaderValue += [string]::Format(",created={0}", $HttpSignatureHeader[$HEADER_CREATED]) + } - $stringToSign = [String]::Format("{0}`n{1}`n{2}`n{3}", - $h_requestTarget,$h_cdate, - $h_targetHost,$h_digest) + if ($HttpSignatureHeader.ContainsKey($HEADER_EXPIRES)) { + $authorizationHeaderValue += [string]::Format(",expires={0}", $HttpSignatureHeader[$HEADER_EXPIRES]) + } - $hashedString = Get-{{{apiNamePrefix}}}StringHash -String $stringToSign - $signedHeader = Get-{{{apiNamePrefix}}}RSASHA256SignedString -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) + $authorizationHeaderValue += [string]::Format(",headers=""{0}"",signature=""{1}""", + $headersKeysString , $headerSignatureStr) - $HttpSignedHeader["Date"] = $currentDate - $HttpSignedHeader["Host"] = $TargetHost - $HttpSignedHeader["Content-Type"] = "application/json" - $HttpSignedHeader["Digest"] = $Digest - $HttpSignedHeader["Authorization"] = $authorizationHeader - return $HttpSignedHeader + $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-{{{apiNamePrefix}}}RSASHA256SignedString { +function Get-{{{apiNamePrefix}}}RSASignature { Param( - [string]$APIKeyFilePath, - [byte[]]$DataToSign + [string]$PrivateKeyFilePath, + [byte[]]$DataToSign, + [string]$HashAlgorithmName, + [string]$SigningAlgorithm, + [securestring]$KeyPassPhrase ) try { - $rsa_provider_path = Join-Path -Path $PSScriptRoot -ChildPath "{{{apiNamePrefix}}}RSAEncryptionProvider.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 + } + 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 "{{{apiNamePrefix}}}RSAEncryptionProvider.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) + } + } - return $signed_string + + $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-{{{apiNamePrefix}}}ECDSASignature { + 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-{{{apiNamePrefix}}}StringHash([String] $String, $HashName = "SHA256") { - +Function Get-{{{apiNamePrefix}}}StringHash { + 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-{{{apiNamePrefix}}}UnixTime { + 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-{{{apiNamePrefix}}}CryptographicScheme { + 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-{{{apiNamePrefix}}}KeyTypeFromFile { + 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 } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/powershell/rsa_provider.mustache b/modules/openapi-generator/src/main/resources/powershell/rsa_provider.mustache index a23490b366e4..7a671929fb2b 100644 --- a/modules/openapi-generator/src/main/resources/powershell/rsa_provider.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/rsa_provider.mustache @@ -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(); - } - } - } } -} +} \ No newline at end of file