Skip to content

Commit

Permalink
[powershell-experimental] : http signature authentication implementat…
Browse files Browse the repository at this point in the history
…ion (#6176)

* ValidatePattern having double quote(") throws exception on running Build.ps1

* fix tab with space

* [powershell-experimental] : http signature auth

* fix the tab issue

Co-authored-by: Ghufran Zahidi <gzahidi@cisco.com>
  • Loading branch information
Ghufz and Ghufz authored May 7, 2020
1 parent 5f2270a commit 13f329e
Show file tree
Hide file tree
Showing 4 changed files with 559 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,9 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("api_client.mustache", infrastructureFolder + "Private", apiNamePrefix + "ApiClient.ps1"));
supportingFiles.add(new SupportingFile("Get-CommonParameters.mustache", infrastructureFolder + File.separator + "Private" + File.separator, "Get-CommonParameters.ps1"));
supportingFiles.add(new SupportingFile("Out-DebugParameter.mustache", infrastructureFolder + File.separator + "Private" + File.separator, "Out-DebugParameter.ps1"));
supportingFiles.add(new SupportingFile("http_signature_auth.mustache", infrastructureFolder + "Private", apiNamePrefix + "HttpSignatureAuth.ps1"));
supportingFiles.add(new SupportingFile("rsa_provider.mustache", infrastructureFolder + "Private", apiNamePrefix + "RSAEncryptionProvider.cs"));


// en-US
supportingFiles.add(new SupportingFile("about_Org.OpenAPITools.help.txt.mustache", infrastructureFolder + File.separator + "en-US" + File.separator + "about_" + packageName + ".help.txt"));
Expand All @@ -626,7 +629,7 @@ public void processOpts() {
@SuppressWarnings("static-method")
@Override
public String escapeText(String input) {

if (input == null) {
return input;
}
Expand All @@ -643,6 +646,7 @@ public String escapeText(String input) {
.replaceAll("[\\t\\n\\r]", " ")
.replace("\\", "\\\\")
.replace("\"", "\"\""));

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ function Invoke-{{{apiNamePrefix}}}ApiClient {
$RequestBody = $Body
}

# http signature authentication
if ($null -ne $Configuration['ApiKey'] -and $Configuration['ApiKey'].Count -gt 0) {
$httpSignHeaderArgument = @{
Method = $Method
UriBuilder = $UriBuilder
Body = $Body
}
$signedHeader = Get-{{{apiNamePrefix}}}HttpSignedHeader @httpSignHeaderArgument
if($null -ne $signedHeader -and $signedHeader.Count -gt 0){
foreach($item in $signedHeader.GetEnumerator()){
$HeaderParameters[$item.Name] = $item.Value
}
}
}

if ($SkipCertificateCheck -eq $true) {
$Response = Invoke-WebRequest -Uri $UriBuilder.Uri `
-Method $Method `
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
{{>partial_header}}
<#
.SYNOPSIS
Get the API key Id and API key file path.

.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.
.PARAMETER Method
Http method
.PARAMETER UriBuilder
UriBuilder for url and query parameter
.PARAMETER Body
Request body
.OUTPUTS
Hashtable
#>
function Get-{{{apiNamePrefix}}}HttpSignedHeader {
param(
[string]$Method,
[System.UriBuilder]$UriBuilder,
[string]$Body
)
#Hash table to store singed headers
$HttpSignedHeader = @{}
$TargetHost = $UriBuilder.Host

#Check for Authentication type
$apiKeyInfo = Get-{{{apiNamePrefix}}}APIKeyInfo
if ($null -eq $apiKeyInfo) {
throw "Unable to reterieve the api key info "
}

#get the body digest
$bodyHash = Get-{{{apiNamePrefix}}}StringHash -String $Body
$Digest = [String]::Format("SHA-256={0}", [Convert]::ToBase64String($bodyHash))

#get the date in UTC
$dateTime = Get-Date
$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)

$stringToSign = [String]::Format("{0}`n{1}`n{2}`n{3}",
$h_requestTarget,$h_cdate,
$h_targetHost,$h_digest)

$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)

$HttpSignedHeader["Date"] = $currentDate
$HttpSignedHeader["Host"] = $TargetHost
$HttpSignedHeader["Content-Type"] = "application/json"
$HttpSignedHeader["Digest"] = $Digest
$HttpSignedHeader["Authorization"] = $authorizationHeader
return $HttpSignedHeader
}

<#
.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.
.PARAMETER APIKeyFilePath
Specify the API key file path
.PARAMETER DataToSign
Specify the data to sign
.OUTPUTS
String
#>
function Get-{{{apiNamePrefix}}}RSASHA256SignedString {
Param(
[string]$APIKeyFilePath,
[byte[]]$DataToSign
)
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"
}
return $signed_string
}
catch {
throw $_
}
}
<#
.Synopsis
Gets the hash of string.
.Description
Gets the hash of string
.Outputs
String
#>
Function Get-{{{apiNamePrefix}}}StringHash([String] $String, $HashName = "SHA256") {
$hashAlogrithm = [System.Security.Cryptography.HashAlgorithm]::Create($HashName)
$hashAlogrithm.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))
}
Loading

0 comments on commit 13f329e

Please sign in to comment.