From ad24d348d28835edfc90385a33357d6d51eef02d Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Thu, 16 Apr 2020 16:39:53 +0530 Subject: [PATCH 01/15] ValidatePattern having double quote(") throws exception on running Build.ps1 --- .../PowerShellExperimentalClientCodegen.java | 24 +++++++++++++++++++ .../resources/3_0/powershell/petstore.yaml | 1 + .../src/PSPetstore/Model/User.ps1 | 1 + 3 files changed, 26 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java index 2b5ae61765cf..149cfa03b1ea 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java @@ -19,6 +19,7 @@ import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; import org.openapitools.codegen.meta.GeneratorMetadata; @@ -622,6 +623,29 @@ public void processOpts() { supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml")); } + @SuppressWarnings("static-method") + @Override + public String escapeText(String input) { + + if (input == null) { + return input; + } + + // remove \t, \n, \r + // replace \ with \\ + // replace " with \" + // outter unescape to retain the original multi-byte characters + // finally escalate characters avoiding code injection + return escapeUnsafeCharacters( + StringEscapeUtils.unescapeJava( + StringEscapeUtils.escapeJava(input) + .replace("\\/", "/")) + .replaceAll("[\\t\\n\\r]", " ") + .replace("\\", "\\\\") + .replace("\"", "\"\"")); + + } + @Override public String escapeUnsafeCharacters(String input) { return input.replace("#>", "#_>").replace("<#", "<_#"); diff --git a/modules/openapi-generator/src/test/resources/3_0/powershell/petstore.yaml b/modules/openapi-generator/src/test/resources/3_0/powershell/petstore.yaml index a2e1dbaefa6d..11ed2854ba5a 100644 --- a/modules/openapi-generator/src/test/resources/3_0/powershell/petstore.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/powershell/petstore.yaml @@ -689,6 +689,7 @@ components: type: string password: type: string + pattern: '["A-Z]+-[0-9][0-9]' phone: type: string userStatus: diff --git a/samples/client/petstore/powershell-experimental/src/PSPetstore/Model/User.ps1 b/samples/client/petstore/powershell-experimental/src/PSPetstore/Model/User.ps1 index a0147c19298e..7e99caf62fbf 100644 --- a/samples/client/petstore/powershell-experimental/src/PSPetstore/Model/User.ps1 +++ b/samples/client/petstore/powershell-experimental/src/PSPetstore/Model/User.ps1 @@ -62,6 +62,7 @@ function Initialize-PSUser { [String] ${Email}, [Parameter(Position = 5, ValueFromPipelineByPropertyName = $true)] + [ValidatePattern("[""A-Z]+-[0-9][0-9]")] [String] ${Password}, [Parameter(Position = 6, ValueFromPipelineByPropertyName = $true)] From ff0094804ab7c885251b31ad3a60a058c24006c0 Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Fri, 17 Apr 2020 13:20:52 +0530 Subject: [PATCH 02/15] fix tab with space --- .../languages/PowerShellExperimentalClientCodegen.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java index 149cfa03b1ea..5896ee99efea 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java @@ -626,8 +626,8 @@ public void processOpts() { @SuppressWarnings("static-method") @Override public String escapeText(String input) { - - if (input == null) { + + if (input == null) { return input; } @@ -643,7 +643,6 @@ public String escapeText(String input) { .replaceAll("[\\t\\n\\r]", " ") .replace("\\", "\\\\") .replace("\"", "\"\"")); - } @Override From 1d0d4e14ecb102e453559d04b65c9dc4377bb0d7 Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Tue, 5 May 2020 16:17:45 +0530 Subject: [PATCH 03/15] [powershell-experimental] : http signature auth --- .../PowerShellExperimentalClientCodegen.java | 8 +- .../api_client.mustache | 15 + .../http_signature_auth.mustache | 162 ++++++++ .../rsa_provider.mustache | 377 ++++++++++++++++++ 4 files changed, 560 insertions(+), 2 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/powershell-experimental/http_signature_auth.mustache create mode 100644 modules/openapi-generator/src/main/resources/powershell-experimental/rsa_provider.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java index 5896ee99efea..e4704963cf58 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java @@ -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")); @@ -626,8 +629,8 @@ public void processOpts() { @SuppressWarnings("static-method") @Override public String escapeText(String input) { - - if (input == null) { + + if (input == null) { return input; } @@ -643,6 +646,7 @@ public String escapeText(String input) { .replaceAll("[\\t\\n\\r]", " ") .replace("\\", "\\\\") .replace("\"", "\"\"")); + } @Override diff --git a/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache b/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache index 2d785b840d1e..c6a456c481ec 100644 --- a/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache @@ -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 ` diff --git a/modules/openapi-generator/src/main/resources/powershell-experimental/http_signature_auth.mustache b/modules/openapi-generator/src/main/resources/powershell-experimental/http_signature_auth.mustache new file mode 100644 index 000000000000..aeb74be678dc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/powershell-experimental/http_signature_auth.mustache @@ -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)) +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/powershell-experimental/rsa_provider.mustache b/modules/openapi-generator/src/main/resources/powershell-experimental/rsa_provider.mustache new file mode 100644 index 000000000000..a23490b366e4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/powershell-experimental/rsa_provider.mustache @@ -0,0 +1,377 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Cryptography; +using System.Text; + +namespace RSAEncryption +{ + public class RSAEncryptionProvider + { + + 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; + + // --------- Set up stream to decode the asn.1 encoded RSA private key ------ + MemoryStream mem = new MemoryStream(privkey); + BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading + byte bt = 0; + ushort twobytes = 0; + int elems = 0; + try + { + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + return null; + bt = binr.ReadByte(); + if (bt != 0x00) + return null; + + + //------ all private key components are Integer sequences ---- + elems = GetIntegerSize(binr); + MODULUS = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + E = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + D = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + P = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + Q = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DP = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DQ = binr.ReadBytes(elems); + + 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(); + RSAparams.Modulus = MODULUS; + RSAparams.Exponent = E; + RSAparams.D = D; + RSAparams.P = P; + RSAparams.Q = Q; + RSAparams.DP = DP; + RSAparams.DQ = DQ; + RSAparams.InverseQ = IQ; + RSA.ImportParameters(RSAparams); + return RSA; + } + catch (Exception) + { + return null; + } + finally { binr.Close(); } + } + + private static int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + byte lowbyte = 0x00; + byte highbyte = 0x00; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) //expect integer + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); // data size in next byte + else + if (bt == 0x82) + { + highbyte = binr.ReadByte(); // data size in next 2 bytes + lowbyte = binr.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; // we already have the data size + } + while (binr.ReadByte() == 0x00) + { //remove high order zeros in data + count -= 1; + } + binr.BaseStream.Seek(-1, SeekOrigin.Current); + //last ReadByte wasn't a removed zero, so back up a byte + 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) + { + 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 + Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes + + // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- + MD5 md5 = new MD5CryptoServiceProvider(); + byte[] result = null; + byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget + + for (int j = 0; j < miter; j++) + { + // ---- Now hash consecutively for count times ------ + if (j == 0) + result = data00; //initialize + else + { + 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); + + Array.Clear(psbytes, 0, psbytes.Length); + Array.Clear(data00, 0, data00.Length); + 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(); + TripleDES alg = TripleDES.Create(); + alg.Key = desKey; + alg.IV = IV; + try + { + CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(cipherData, 0, cipherData.Length); + cs.Close(); + } + catch (Exception exc) + { + Console.WriteLine(exc.Message); + 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(); + } + } + } + } +} From 5372b5ae8adb29e532afb3d45f51571b6a171127 Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Tue, 5 May 2020 16:55:19 +0530 Subject: [PATCH 04/15] fix the tab issue --- .../languages/PowerShellExperimentalClientCodegen.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java index e4704963cf58..3151617e438b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java @@ -629,8 +629,8 @@ public void processOpts() { @SuppressWarnings("static-method") @Override public String escapeText(String input) { - - if (input == null) { + + if (input == null) { return input; } @@ -646,7 +646,7 @@ public String escapeText(String input) { .replaceAll("[\\t\\n\\r]", " ") .replace("\\", "\\\\") .replace("\"", "\"\"")); - + } @Override From c6a6a82a76a2326799093b6ffe219a174dd62620 Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Fri, 22 May 2020 13:00:16 +0530 Subject: [PATCH 05/15] merge cnflict fix for powershell experimental --- .../PowerShellExperimentalClientCodegen.java | 1105 ----------------- .../api_client.mustache | 203 --- 2 files changed, 1308 deletions(-) delete mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java delete mode 100644 modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java deleted file mode 100644 index 3151617e438b..000000000000 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PowerShellExperimentalClientCodegen.java +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.openapitools.codegen.languages; - -import io.swagger.v3.oas.models.media.ArraySchema; -import io.swagger.v3.oas.models.media.Schema; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.openapitools.codegen.*; -import org.openapitools.codegen.meta.GeneratorMetadata; -import org.openapitools.codegen.meta.Stability; -import org.openapitools.codegen.meta.features.*; -import org.openapitools.codegen.utils.ModelUtils; -import org.openapitools.codegen.utils.ProcessUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.util.*; - -import static java.util.UUID.randomUUID; -import static org.openapitools.codegen.utils.StringUtils.camelize; - -public class PowerShellExperimentalClientCodegen extends DefaultCodegen implements CodegenConfig { - private static final Logger LOGGER = LoggerFactory.getLogger(PowerShellExperimentalClientCodegen.class); - - private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}"; - - protected String sourceFolder = "src"; - protected String packageName = "PSOpenAPITools"; - protected String packageVersion = "0.1.2"; - protected String apiDocPath = "docs/"; - protected String modelDocPath = "docs/"; - protected String apiTestPath = "tests/Api"; - protected String modelTestPath = "tests/Model"; - protected HashSet nullablePrimitives; - protected String powershellGalleryUrl; - protected HashSet powershellVerbs; - protected Map commonVerbs; // verbs not in the official ps verb list but can be mapped to one of the verbs - protected HashSet methodNames; // store a list of method names to detect duplicates - - /** - * Constructs an instance of `PowerShellExperimentalClientCodegen`. - */ - public PowerShellExperimentalClientCodegen() { - super(); - - modifyFeatureSet(features -> features - .includeDocumentationFeatures(DocumentationFeature.Readme) - .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML)) - .securityFeatures(EnumSet.of( - SecurityFeature.BasicAuth, - SecurityFeature.ApiKey, - SecurityFeature.OAuth2_Implicit - )) - .excludeGlobalFeatures( - GlobalFeature.XMLStructureDefinitions, - GlobalFeature.Callbacks, - GlobalFeature.LinkObjects, - GlobalFeature.ParameterStyling - ) - .excludeSchemaSupportFeatures( - SchemaSupportFeature.Polymorphism - ) - .excludeParameterFeatures( - ParameterFeature.Cookie - ) - ); - - generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) - .stability(Stability.BETA) - .build(); - - outputFolder = "generated-code" + File.separator + "powershell-expiermental"; - modelTemplateFiles.put("model.mustache", ".ps1"); - apiTemplateFiles.put("api.mustache", ".ps1"); - modelTestTemplateFiles.put("model_test.mustache", ".ps1"); - apiTestTemplateFiles.put("api_test.mustache", ".ps1"); - modelDocTemplateFiles.put("model_doc.mustache", ".md"); - apiDocTemplateFiles.put("api_doc.mustache", ".md"); - embeddedTemplateDir = templateDir = "powershell-experimental"; - apiPackage = packageName + File.separator + "Api"; - modelPackage = packageName + File.separator + "Model"; - - // https://blogs.msdn.microsoft.com/powershell/2010/01/07/how-objects-are-sent-to-and-from-remote-sessions/ - languageSpecificPrimitives = new HashSet(Arrays.asList( - "Byte", - "SByte", - "Byte[]", - "Int16", - "Int32", - "Int64", - "UInt16", - "UInt32", - "UInt64", - "Decimal", - "Single", - "Double", - "TimeSpan", - "System.DateTime", - "ProgressRecord", - "Char", - "String", - "XmlDocument", - "SecureString", - "Boolean", - "Guid", - "Uri", - "System.IO.FileInfo", - "Version" - )); - - commonVerbs = new HashMap(); - - Map> verbMappings = new HashMap>(); - - // common - verbMappings.put("Add", Arrays.asList("Append", "Attach", "Concatenate", "Insert")); - verbMappings.put("Clear", Arrays.asList("Flush", "Erase", "Release", "Unmark", "Unset", "Nullify")); - verbMappings.put("Close", Arrays.asList()); - verbMappings.put("Copy", Arrays.asList("Duplicate", "Clone", "Replicate", "Sync")); - verbMappings.put("Enter", Arrays.asList("PushInto")); - verbMappings.put("Exit", Arrays.asList("PopOut")); - verbMappings.put("Find", Arrays.asList()); - verbMappings.put("Format", Arrays.asList()); - verbMappings.put("Get", Arrays.asList("Read", "Open", "Cat", "Type", "Dir", "Obtain", "Dump", "Acquire", "Examine", "Find", "Search")); - verbMappings.put("Hide", Arrays.asList("Block")); - verbMappings.put("Join", Arrays.asList("Combine", "Unite", "Connect", "Associate")); - verbMappings.put("Lock", Arrays.asList("RestrictSecure")); - verbMappings.put("Move", Arrays.asList("Transfer", "Name", "Migrate")); - verbMappings.put("New", Arrays.asList("Create", "Generate", "Build", "Make", "Allocate")); - verbMappings.put("Open", Arrays.asList()); - verbMappings.put("Optimize", Arrays.asList()); - verbMappings.put("Pop", Arrays.asList()); - verbMappings.put("Push", Arrays.asList()); - verbMappings.put("Redo", Arrays.asList()); - verbMappings.put("Remove", Arrays.asList("Clear", "Cut", "Dispose", "Discard", "Erase")); - verbMappings.put("Rename", Arrays.asList("Change")); - verbMappings.put("Reset", Arrays.asList()); - verbMappings.put("Search", Arrays.asList("FindLocate")); - verbMappings.put("Select", Arrays.asList("FindLocate")); - verbMappings.put("Set", Arrays.asList("Write", "Reset", "Assign", "Configure")); - verbMappings.put("Show", Arrays.asList("DisplayProduce")); - verbMappings.put("Skip", Arrays.asList("BypassJump")); - verbMappings.put("Split", Arrays.asList("parate")); - verbMappings.put("Step", Arrays.asList()); - verbMappings.put("Switch", Arrays.asList()); - verbMappings.put("Undo", Arrays.asList()); - verbMappings.put("Unlock", Arrays.asList("Release", "Unrestrict", "Unsecure")); - verbMappings.put("Watch", Arrays.asList()); - - // communication - verbMappings.put("Connect", Arrays.asList("JoinTelnet")); - verbMappings.put("Disconnect", Arrays.asList("BreakLogoff")); - verbMappings.put("Read", Arrays.asList("Acquire", "Prompt", "Get")); - verbMappings.put("Receive", Arrays.asList("Read", "Accept", "Peek")); - verbMappings.put("Send", Arrays.asList("Put", "Broadcast", "Mail", "Fax")); - verbMappings.put("Write", Arrays.asList("PutPrint")); - - // data - verbMappings.put("Backup", Arrays.asList(" Save", " Burn", " Replicate", "Sync")); - verbMappings.put("Checkpoint", Arrays.asList(" Diff")); - verbMappings.put("Compare", Arrays.asList(" Diff")); - verbMappings.put("Compress", Arrays.asList(" Compact")); - verbMappings.put("Convert", Arrays.asList(" Change", " Resize", "Resample")); - verbMappings.put("ConvertFrom", Arrays.asList(" Export", " Output", "Out")); - verbMappings.put("ConvertTo", Arrays.asList(" Import", " Input", "In")); - verbMappings.put("Dismount", Arrays.asList(" UnmountUnlink")); - verbMappings.put("Edit", Arrays.asList(" Change", " Update", "Modify")); - verbMappings.put("Expand", Arrays.asList(" ExplodeUncompress")); - verbMappings.put("Export", Arrays.asList(" ExtractBackup")); - verbMappings.put("Group", Arrays.asList(" Aggregate", " Arrange", " Associate", "Correlate")); - verbMappings.put("Import", Arrays.asList(" BulkLoadLoad")); - verbMappings.put("Initialize", Arrays.asList(" Erase", " Init", " Renew", " Rebuild", " Reinitialize", "Setup")); - verbMappings.put("Limit", Arrays.asList(" Quota")); - verbMappings.put("Merge", Arrays.asList(" CombineJoin")); - verbMappings.put("Mount", Arrays.asList(" Connect")); - verbMappings.put("Out", Arrays.asList()); - verbMappings.put("Publish", Arrays.asList(" Deploy", " Release", "Install")); - verbMappings.put("Restore", Arrays.asList(" Repair", " Return", " Undo", "Fix")); - verbMappings.put("Save", Arrays.asList()); - verbMappings.put("Sync", Arrays.asList(" Replicate", " Coerce", "Match")); - verbMappings.put("Unpublish", Arrays.asList(" Uninstall", " Revert", "Hide")); - verbMappings.put("Update", Arrays.asList(" Refresh", " Renew", " Recalculate", "Re-index")); - - // diagnostic - verbMappings.put("Debug", Arrays.asList("Diagnose")); - verbMappings.put("Measure", Arrays.asList("Calculate", "Determine", "Analyze")); - verbMappings.put("Ping", Arrays.asList()); - verbMappings.put("Repair", Arrays.asList("FixRestore")); - verbMappings.put("Resolve", Arrays.asList("ExpandDetermine")); - verbMappings.put("Test", Arrays.asList("Diagnose", "Analyze", "Salvage", "Verify")); - verbMappings.put("Trace", Arrays.asList("Track", "Follow", "Inspect", "Dig")); - - // lifecycle - verbMappings.put("Approve", Arrays.asList()); - verbMappings.put("Assert", Arrays.asList("Certify")); - verbMappings.put("Build", Arrays.asList()); - verbMappings.put("Complete", Arrays.asList()); - verbMappings.put("Confirm", Arrays.asList("Acknowledge", "Agree", "Certify", "Validate", "Verify")); - verbMappings.put("Deny", Arrays.asList("Block", "Object", "Refuse", "Reject")); - verbMappings.put("Deploy", Arrays.asList()); - verbMappings.put("Disable", Arrays.asList("HaltHide")); - verbMappings.put("Enable", Arrays.asList("StartBegin")); - verbMappings.put("Install", Arrays.asList("Setup")); - verbMappings.put("Invoke", Arrays.asList("RunStart")); - verbMappings.put("Register", Arrays.asList()); - verbMappings.put("Request", Arrays.asList()); - verbMappings.put("Restart", Arrays.asList("Recycle")); - verbMappings.put("Resume", Arrays.asList()); - verbMappings.put("Start", Arrays.asList("Launch", "Initiate", "Boot")); - verbMappings.put("Stop", Arrays.asList("End", "Kill", "Terminate", "Cancel")); - verbMappings.put("Submit", Arrays.asList("Post")); - verbMappings.put("Suspend", Arrays.asList("Pause")); - verbMappings.put("Uninstall", Arrays.asList()); - verbMappings.put("Unregister", Arrays.asList("Remove")); - verbMappings.put("Wait", Arrays.asList("SleepPause")); - - // security - verbMappings.put("Block", Arrays.asList("Prevent", "Limit", "Deny")); - verbMappings.put("Grant", Arrays.asList("AllowEnable")); - verbMappings.put("Protect", Arrays.asList("Encrypt", "Safeguard", "Seal")); - verbMappings.put("Revoke", Arrays.asList("RemoveDisable")); - verbMappings.put("Unblock", Arrays.asList("ClearAllow")); - verbMappings.put("Unprotect", Arrays.asList("DecryptUnseal")); - - // other - verbMappings.put("Use", Arrays.asList()); - - for (Map.Entry> entry : verbMappings.entrySet()) { - // loop through each verb in the list - for (String verb : entry.getValue()) { - if (verbMappings.containsKey(verb)) { - // the verb to be mapped is also a common verb, do nothing - LOGGER.debug("verbmapping: skipped {}", verb); - } else { - commonVerbs.put(verb, entry.getKey()); - LOGGER.debug("verbmapping: adding {} => {}", verb, entry.getKey()); - } - } - } - - powershellVerbs = new HashSet(Arrays.asList( - "Add", - "Clear", - "Close", - "Copy", - "Enter", - "Exit", - "Find", - "Format", - "Get", - "Hide", - "Join", - "Lock", - "Move", - "New", - "Open", - "Optimize", - "Pop", - "Push", - "Redo", - "Remove", - "Rename", - "Reset", - "Search", - "Select", - "Set", - "Show", - "Skip", - "Split", - "Step", - "Switch", - "Undo", - "Unlock", - "Watch", - "Connect", - "Disconnect", - "Read", - "Receive", - "Send", - "Write", - "Backup", - "Checkpoint", - "Compare", - "Compress", - "Convert", - "ConvertFrom", - "ConvertTo", - "Dismount", - "Edit", - "Expand", - "Export", - "Group", - "Import", - "Initialize", - "Limit", - "Merge", - "Mount", - "Out", - "Publish", - "Restore", - "Save", - "Sync", - "Unpublish", - "Update", - "Debug", - "Measure", - "Ping", - "Repair", - "Resolve", - "Test", - "Trace", - "Approve", - "Assert", - "Build", - "Complete", - "Confirm", - "Deny", - "Deploy", - "Disable", - "Enable", - "Install", - "Invoke", - "Register", - "Request", - "Restart", - "Resume", - "Start", - "Stop", - "Submit", - "Suspend", - "Uninstall", - "Unregister", - "Wait", - "Block", - "Grant", - "Protect", - "Revoke", - "Unblock", - "Unprotect", - "Use" - )); - - methodNames = new HashSet(); - - nullablePrimitives = new HashSet(Arrays.asList( - "System.Nullable[Byte]", - "System.Nullable[SByte]", - "System.Nullable[Int16]", - "System.Nullable[Int32]", - "System.Nullable[Int64]", - "System.Nullable[UInt16]", - "System.Nullable[UInt32]", - "System.Nullable[UInt64]", - "System.Nullable[Decimal]", - "System.Nullable[Single]", - "System.Nullable[Double]", - "System.Nullable[Boolean]" - )); - - // list of reserved words - must be in lower case - reservedWords = new HashSet(Arrays.asList( - // https://richardspowershellblog.wordpress.com/2009/05/02/powershell-reserved-words/ - "begin", - "break", - "catch", - "continue", - "data", - "do", - "dynamicparam", - "else", - "elseif", - "end", - "exit", - "filter", - "finally", - "for", - "foreach", - "from", - "function", - "if", - "in", - "param", - "process", - "return", - "switch", - "throw", - "trap", - "try", - "until", - "while", - "local", - "private", - "where", - // special variables - "args", - "consolefilename", - "error", - "event", - "eventargs", - "eventsubscriber", - "executioncontext", - "false", - "foreach", - "home", - "host", - "input", - "lastexitcode", - "matches", - "myinvocation", - "nestedpromptlevel", - "null", - "pid", - "profile", - "pscmdlet", - "pscommandpath", - "psculture", - "psdebugcontext", - "pshome", - "psitem", - "psscriptroot", - "pssenderinfo", - "psuiculture", - "psversiontable", - "sender", - "shellid", - "stacktrace", - "this", - "true" - )); - - defaultIncludes = new HashSet(Arrays.asList( - "Byte", - "SByte", - "Byte[]", - "Int16", - "Int32", - "Int64", - "UInt16", - "UInt32", - "UInt64", - "Decimal", - "Single", - "Double", - "TimeSpan", - "System.DateTime", - "ProgressRecord", - "Char", - "String", - "XmlDocument", - "SecureString", - "Boolean", - "Guid", - "Uri", - "System.IO.FileInfo", - "Version" - )); - - typeMapping = new HashMap(); - typeMapping.put("string", "String"); - typeMapping.put("boolean", "Boolean"); - typeMapping.put("integer", "Int32"); - typeMapping.put("float", "Double"); - typeMapping.put("long", "Int64"); - typeMapping.put("double", "Double"); - typeMapping.put("number", "Decimal"); - typeMapping.put("object", "System.Collections.Hashtable"); - typeMapping.put("file", "System.IO.FileInfo"); - typeMapping.put("ByteArray", "System.Byte[]"); - typeMapping.put("binary", "System.IO.FileInfo"); - typeMapping.put("date", "System.DateTime"); - typeMapping.put("date-time", "System.DateTime"); - typeMapping.put("Date", "System.DateTime"); - typeMapping.put("DateTime", "System.DateTime"); - typeMapping.put("UUID", "String"); - typeMapping.put("URI", "String"); - - cliOptions.clear(); - cliOptions.add(new CliOption("powershellGalleryUrl", "URL to the module in PowerShell Gallery (e.g. https://www.powershellgallery.com/packages/PSTwitter/).")); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. PSTwitter).").defaultValue(this.packageName)); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Package version (e.g. 0.1.2).").defaultValue(this.packageVersion)); - cliOptions.add(new CliOption(CodegenConstants.OPTIONAL_PROJECT_GUID, "GUID for PowerShell module (e.g. a27b908d-2a20-467f-bc32-af6f3a654ac5). A random GUID will be generated by default.")); - cliOptions.add(new CliOption(CodegenConstants.API_NAME_PREFIX, "Prefix that will be appended to all PS objects. Default: empty string. e.g. Pet => PSPet.")); - cliOptions.add(new CliOption("commonVerbs", "PS common verb mappings. e.g. Delete=Remove:Patch=Update to map Delete with Remove and Patch with Update accordingly.")); - - } - - public CodegenType getTag() { - return CodegenType.CLIENT; - } - - public String getName() { - return "powershell-experimental"; - } - - public String getHelp() { - return "Generates a PowerShell API client (beta)"; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - this.apiPackage = packageName + File.separator + "Api"; - this.modelPackage = packageName + File.separator + "Model"; - } - - public void setPackageVersion(String packageVersion) { - this.packageVersion = packageVersion; - } - - public void setSourceFolder(String sourceFolder) { - this.sourceFolder = sourceFolder; - } - - public void setPackageGuid(String packageGuid) { - this.packageGuid = packageGuid; - } - - public void setPowershellGalleryUrl(String powershellGalleryUrl) { - this.powershellGalleryUrl = powershellGalleryUrl; - } - - @Override - public void processOpts() { - super.processOpts(); - - if (StringUtils.isEmpty(System.getenv("POWERSHELL_POST_PROCESS_FILE"))) { - LOGGER.info("Environment variable POWERSHELL_POST_PROCESS_FILE not defined so the PowerShell code may not be properly formatted. To define it, try 'export POWERSHELL_POST_PROCESS_FILE=\"Edit-DTWBeautifyScript\"'"); - LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI)."); - } - - if (additionalProperties.containsKey("powershellGalleryUrl")) { - setPowershellGalleryUrl((String) additionalProperties.get("powershellGalleryUrl")); - } else { - additionalProperties.put("powershellGalleryUrl", powershellGalleryUrl); - } - - if (StringUtils.isNotBlank(powershellGalleryUrl)) { - // get the last segment of the URL - // e.g. https://www.powershellgallery.com/packages/PSTwitter => PSTwitter - additionalProperties.put("powershellGalleryId", powershellGalleryUrl.replaceFirst(".*/([^/?]+).*", "$1")); - } - - if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) { - setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID)); - } else { - additionalProperties.put("packageGuid", packageGuid); - } - - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - this.setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); - } else { - additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); - } - - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) { - this.setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION)); - } else { - additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion); - } - - if (additionalProperties.containsKey("commonVerbs")) { - String[] entries = ((String)additionalProperties.get("commonVerbs")).split(":"); - for (String entry : entries) { - String[] pair = entry.split("="); - if (pair.length == 2) { - commonVerbs.put(pair[0], pair[1]); - LOGGER.debug("Add commonVerbs: {} => {}", pair[0], pair[1]); - } else { - LOGGER.error("Failed to parse commonVerbs: {}", entry); - } - } - } - - if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) { - LOGGER.warn(CodegenConstants.MODEL_PACKAGE + " with " + this.getName() + " generator is ignored. Setting this value independently of " + CodegenConstants.PACKAGE_NAME + " is not currently supported."); - } - - if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { - LOGGER.warn(CodegenConstants.API_PACKAGE + " with " + this.getName() + " generator is ignored. Setting this value independently of " + CodegenConstants.PACKAGE_NAME + " is not currently supported."); - } - - additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); - additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); - - additionalProperties.put("apiDocPath", apiDocPath); - additionalProperties.put("modelDocPath", modelDocPath); - - supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("Build.ps1.mustache", "", "Build.ps1")); - - final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator); - - supportingFiles.add(new SupportingFile("Org.OpenAPITools.psm1.mustache", infrastructureFolder, packageName + ".psm1")); - - // client/configuration - supportingFiles.add(new SupportingFile("configuration.mustache", infrastructureFolder + "Client", apiNamePrefix + "Configuration.ps1")); - - // private - 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")); - - // appveyor - supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml")); - } - - @SuppressWarnings("static-method") - @Override - public String escapeText(String input) { - - if (input == null) { - return input; - } - - // remove \t, \n, \r - // replace \ with \\ - // replace " with \" - // outter unescape to retain the original multi-byte characters - // finally escalate characters avoiding code injection - return escapeUnsafeCharacters( - StringEscapeUtils.unescapeJava( - StringEscapeUtils.escapeJava(input) - .replace("\\/", "/")) - .replaceAll("[\\t\\n\\r]", " ") - .replace("\\", "\\\\") - .replace("\"", "\"\"")); - - } - - @Override - public String escapeUnsafeCharacters(String input) { - return input.replace("#>", "#_>").replace("<#", "<_#"); - } - - @Override - public String escapeQuotationMark(String input) { - // remove " to avoid code injection - return input.replace("\"", ""); - } - - @Override - public String toApiTestFilename(String name) { - return toApiFilename(name) + ".Tests"; - } - - @Override - public String toModelTestFilename(String name) { - return toModelFilename(name) + ".Tests"; - } - - @Override - public String apiTestFileFolder() { - return (outputFolder + "/" + apiTestPath).replace('/', File.separatorChar); - } - - @Override - public String apiDocFileFolder() { - return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); - } - - @Override - public String apiFileFolder() { - return outputFolder + File.separator + sourceFolder + File.separator + apiPackage(); - } - - @Override - public String modelTestFileFolder() { - return (outputFolder + "/" + modelTestPath).replace('/', File.separatorChar); - } - - @Override - public String modelDocFileFolder() { - return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar); - } - - - @Override - public String modelFileFolder() { - return outputFolder + File.separator + sourceFolder + File.separator + modelPackage(); - } - - @Override - public String escapeReservedWord(String name) { - return "Var" + name; - } - - /** - * Output the proper model name (capitalized). - * In case the name belongs to the TypeSystem it won't be renamed. - * - * @param name the name of the model - * @return capitalized model name - */ - @Override - public String toModelName(String name) { - if (!StringUtils.isEmpty(modelNamePrefix)) { - name = modelNamePrefix + "_" + name; - } - - if (!StringUtils.isEmpty(modelNameSuffix)) { - name = name + "_" + modelNameSuffix; - } - - // camelize the model name - // phone_number => PhoneNumber - name = camelize(sanitizeName(name)); - - // model name cannot use reserved keyword, e.g. return - if (isReservedWord(name)) { - LOGGER.warn(name + " (reserved word or special variable name) cannot be used as model name. Renamed to " + camelize("model_" + name)); - name = camelize("model_" + name); // e.g. return => ModelReturn (after camelize) - } - - // model name starts with number - if (name.matches("^\\d.*")) { - LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + name)); - name = camelize("model_" + name); // e.g. 200Response => Model200Response (after camelize) - } - - return name; - } - - @Override - public String toModelFilename(String name) { - // should be the same as the model name - return toModelName(name); - } - - /** - * returns the OpenAPI type for the property - * - * @param p OpenAPI property object - * @return string presentation of the type - **/ - @Override - public String getSchemaType(Schema p) { - String openAPIType = super.getSchemaType(p); - String type; - - // This maps, for example, long -> Long based on hashes in this type's constructor - if (typeMapping.containsKey(openAPIType)) { - type = typeMapping.get(openAPIType); - if (languageSpecificPrimitives.contains(type)) { - return type; - } - } else { - type = openAPIType; - } - - // model/object - return toModelName(type); - } - - /** - * Output the type declaration of the property - * - * @param p OpenAPI Schema object - * @return a string presentation of the property type - */ - @Override - public String getTypeDeclaration(Schema p) { - if (ModelUtils.isArraySchema(p)) { - ArraySchema ap = (ArraySchema) p; - Schema inner = ap.getItems(); - return getTypeDeclaration(inner) + "[]"; - } else if (ModelUtils.isMapSchema(p)) { - return "System.Collections.Hashtable"; - } else if (!languageSpecificPrimitives.contains(getSchemaType(p))) { - return super.getTypeDeclaration(p); - } - return super.getTypeDeclaration(p); - } - - @Override - public String toOperationId(String operationId) { - // throw exception if method name is empty (should not occur as an auto-generated method name will be used) - if (StringUtils.isEmpty(operationId)) { - throw new RuntimeException("Empty method name (operationId) not allowed"); - } - - return sanitizeName(operationId); - } - - @Override - public String toParamName(String name) { - return toVarName(name); - } - - @Override - public Map postProcessOperationsWithModels(Map objs, List allModels) { - Map operations = (Map) objs.get("operations"); - HashMap modelMaps = new HashMap(); - HashMap processedModelMaps = new HashMap(); - - for (Object o : allModels) { - HashMap h = (HashMap) o; - CodegenModel m = (CodegenModel) h.get("model"); - modelMaps.put(m.classname, m); - } - - List operationList = (List) operations.get("operation"); - for (CodegenOperation op : operationList) { - int index = 0; - for (CodegenParameter p : op.allParams) { - p.vendorExtensions.put("x-powershell-data-type", getPSDataType(p)); - p.vendorExtensions.put("x-powershell-example", constructExampleCode(p, modelMaps, processedModelMaps)); - p.vendorExtensions.put("x-index", index); - index++; - } - - if (!op.vendorExtensions.containsKey("x-powershell-method-name")) { // x-powershell-method-name not set - String methodName = toMethodName(op.operationId); - op.vendorExtensions.put("x-powershell-method-name", methodName); - op.vendorExtensions.put("x-powershell-method-name-lowercase", methodName); - } else { - op.vendorExtensions.put("x-powershell-method-name-lowercase", ((String) op.vendorExtensions.get("x-powershell-method-name")).toLowerCase(Locale.ROOT)); - } - - // detect duplicated method name - if (methodNames.contains(op.vendorExtensions.get("x-powershell-method-name"))) { - LOGGER.error("Duplicated method name found: {}", op.vendorExtensions.get("x-powershell-method-name")); - } else { - methodNames.add(op.vendorExtensions.get("x-powershell-method-name")); - } - - if (op.produces != null && op.produces.size() > 1) { - op.vendorExtensions.put("x-powershell-select-accept", true); - } - } - - processedModelMaps.clear(); - for (CodegenOperation operation : operationList) { - for (CodegenParameter cp : operation.allParams) { - cp.vendorExtensions.put("x-powershell-example", constructExampleCode(cp, modelMaps, processedModelMaps)); - } - } - - return objs; - } - - @Override - public Map postProcessModels(Map objs) { - List models = (List) objs.get("models"); - // add x-index to properties - ProcessUtils.addIndexToProperties(models); - - // add x-data-type to store powershell type - for (Object _mo : models) { - Map mo = (Map) _mo; - CodegenModel cm = (CodegenModel) mo.get("model"); - - for (CodegenProperty cp : cm.allVars) { - cp.vendorExtensions.put("x-powershell-data-type", getPSDataType(cp)); - } - } - - return objs; - } - - @Override - public String toVarName(String name) { - // sanitize name - name = sanitizeName(name); - - // camelize the variable name - // pet_id => PetId - name = camelize(name); - - // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { - LOGGER.warn(name + " (reserved word or special variable name) cannot be used in naming. Renamed to " + escapeReservedWord(name)); - name = escapeReservedWord(name); - } - - return name; - } - - private String constructExampleCode(CodegenParameter codegenParameter, HashMap modelMaps, HashMap processedModelMap) { - if (codegenParameter.isListContainer) { // array - return "@(" + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap) + ")"; - } else if (codegenParameter.isMapContainer) { // TODO: map, file type - return "@{ \"Key\" = \"Value\" }"; - } else if (languageSpecificPrimitives.contains(codegenParameter.dataType) || - nullablePrimitives.contains(codegenParameter.dataType)) { // primitive type - if ("String".equals(codegenParameter.dataType) || "Character".equals(codegenParameter.dataType)) { - if (StringUtils.isEmpty(codegenParameter.example)) { - return "\"" + codegenParameter.example + "\""; - } else { - return "\"" + codegenParameter.paramName + "_example\""; - } - } else if ("Boolean".equals(codegenParameter.dataType) || - "System.Nullable[Boolean]".equals(codegenParameter.dataType)) { // boolean - if (Boolean.parseBoolean(codegenParameter.example)) { - return "true"; - } else { - return "false"; - } - } else if ("URL".equals(codegenParameter.dataType)) { // URL - return "URL(string: \"https://example.com\")!"; - } else if ("System.DateTime".equals(codegenParameter.dataType)) { // datetime or date - return "Get-Date"; - } else { // numeric - if (StringUtils.isEmpty(codegenParameter.example)) { - return codegenParameter.example; - } else { - return "987"; - } - } - } else { // model - // look up the model - if (modelMaps.containsKey(codegenParameter.dataType)) { - return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap); - } else { - //LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenParameter.dataType); - return "TODO"; - } - } - } - - private String constructExampleCode(CodegenProperty codegenProperty, HashMap modelMaps, HashMap processedModelMap) { - if (codegenProperty.isListContainer) { // array - return "@(" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + ")"; - } else if (codegenProperty.isMapContainer) { // map - return "\"TODO\""; - } else if (languageSpecificPrimitives.contains(codegenProperty.dataType) || // primitive type - nullablePrimitives.contains(codegenProperty.dataType)) { // nullable primitive type - if ("String".equals(codegenProperty.dataType)) { - if (StringUtils.isEmpty(codegenProperty.example)) { - return "\"" + codegenProperty.example + "\""; - } else { - return "\"" + codegenProperty.name + "_example\""; - } - } else if ("Boolean".equals(codegenProperty.dataType) || - "System.Nullable[Boolean]".equals(codegenProperty.dataType)) { // boolean - if (Boolean.parseBoolean(codegenProperty.example)) { - return "$true"; - } else { - return "$false"; - } - } else if ("URL".equals(codegenProperty.dataType)) { // URL - return "URL(string: \"https://example.com\")!"; - } else if ("System.DateTime".equals(codegenProperty.dataType)) { // datetime or date - return "Get-Date"; - } else { // numeric - if (StringUtils.isEmpty(codegenProperty.example)) { - return codegenProperty.example; - } else { - return "123"; - } - } - } else { - // look up the model - if (modelMaps.containsKey(codegenProperty.dataType)) { - return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap); - } else { - //LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenProperty.dataType); - return "\"TODO\""; - } - } - } - - private String constructExampleCode(CodegenModel codegenModel, HashMap modelMaps, HashMap processedModelMap) { - String example; - - // break infinite recursion. Return, in case a model is already processed in the current context. - String model = codegenModel.name; - if (processedModelMap.containsKey(model)) { - int count = processedModelMap.get(model); - if (count == 1) { - processedModelMap.put(model, 2); - } else if (count == 2) { - return ""; - } else { - throw new RuntimeException("Invalid count when constructing example: " + count); - } - } else { - processedModelMap.put(model, 1); - } - - example = "(Initialize-" + codegenModel.name; - List propertyExamples = new ArrayList<>(); - for (CodegenProperty codegenProperty : codegenModel.allVars) { - propertyExamples.add("-" + codegenProperty.name + " " + constructExampleCode(codegenProperty, modelMaps, processedModelMap)); - } - example += StringUtils.join(propertyExamples, " "); - example += ")"; - return example; - } - - private String getPSDataType(CodegenProperty cp) { - String dataType; - if (cp.isPrimitiveType) { - dataType = cp.dataType; - if (!(cp.isString || cp.isFile || cp.isContainer) - && (cp.isNullable || !cp.required)) { - dataType = "System.Nullable[" + dataType + "]"; - } - return dataType; - } else if (cp.isListContainer) { // array - return getPSDataType(cp.items) + "[]"; - } else if (cp.isMapContainer) { // map - return "System.Collections.Hashtable"; - } else { // model - return "PSCustomObject"; - } - } - - private String getPSDataType(CodegenParameter cp) { - String dataType; - if (cp.isPrimitiveType) { - dataType = cp.dataType; - if (!(cp.isString || cp.isFile || cp.isContainer) - && (cp.isNullable || !cp.required)) { - dataType = "System.Nullable[" + dataType + "]"; - } - return dataType; - } else if (cp.isListContainer) { // array - return getPSDataType(cp.items) + "[]"; - } else if (cp.isMapContainer) { // map - return "System.Collections.Hashtable"; - } else { // model - return "PSCustomObject"; - } - } - - private String toMethodName(String operationId) { - String methodName = camelize(operationId); - - // check if method name starts with powershell verbs - for (String verb : (HashSet) powershellVerbs) { - if (methodName.startsWith(verb)) { - methodName = verb + "-" + apiNamePrefix + methodName.substring(verb.length()); - LOGGER.info("Naming the method using the PowerShell verb: {} => {}", operationId, methodName); - return methodName; - } - } - - for (Map.Entry entry : commonVerbs.entrySet()) { - if (methodName.startsWith(entry.getKey())) { - methodName = entry.getValue() + "-" + apiNamePrefix + methodName.substring(entry.getKey().length()); - LOGGER.info("Naming the method by mapping the common verbs (e.g. Create, Change) to PS verbs: {} => {}", operationId, methodName); - return methodName; - } - } - - // not using powershell verb - return "Invoke-" + apiNamePrefix + methodName; - } - - @Override - public void postProcessFile(File file, String fileType) { - if (file == null) { - return; - } - String powershellPostProcessFile = System.getenv("POWERSHELL_POST_PROCESS_FILE"); - if (StringUtils.isEmpty(powershellPostProcessFile)) { - return; // skip if POWERSHELL_POST_PROCESS_FILE env variable is not defined - } - - // only process files with ps extension - if ("ps".equals(FilenameUtils.getExtension(file.toString()))) { - String command = powershellPostProcessFile + " " + file.toString(); - try { - Process p = Runtime.getRuntime().exec(command); - int exitValue = p.waitFor(); - if (exitValue != 0) { - LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue); - } else { - LOGGER.info("Successfully executed: " + command); - } - } catch (Exception e) { - LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage()); - } - } - - } - - @Override - public String toRegularExpression(String pattern) { - return escapeText(pattern); - } - -} diff --git a/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache b/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache deleted file mode 100644 index c6a456c481ec..000000000000 --- a/modules/openapi-generator/src/main/resources/powershell-experimental/api_client.mustache +++ /dev/null @@ -1,203 +0,0 @@ -{{> partial_header}} -function Invoke-{{{apiNamePrefix}}}ApiClient { - [OutputType('System.Collections.Hashtable')] - [CmdletBinding()] - Param( - [Parameter(Mandatory)] - [string]$Uri, - [Parameter(Mandatory)] - [AllowEmptyCollection()] - [string[]]$Accepts, - [Parameter(Mandatory)] - [AllowEmptyCollection()] - [string[]]$ContentTypes, - [Parameter(Mandatory)] - [hashtable]$HeaderParameters, - [Parameter(Mandatory)] - [hashtable]$FormParameters, - [Parameter(Mandatory)] - [hashtable]$QueryParameters, - [Parameter(Mandatory)] - [hashtable]$CookieParameters, - [Parameter(Mandatory)] - [AllowEmptyString()] - [string]$Body, - [Parameter(Mandatory)] - [string]$Method, - [Parameter(Mandatory)] - [AllowEmptyString()] - [string]$ReturnType - ) - - 'Calling method: Invoke-{{{apiNamePrefix}}}ApiClient' | Write-Debug - $PSBoundParameters | Out-DebugParameter | Write-Debug - - $Configuration = Get-{{{apiNamePrefix}}}Configuration - $RequestUri = $Configuration["BaseUrl"] + $Uri - $SkipCertificateCheck = $Configuration["SkipCertificateCheck"] - - # cookie parameters - foreach ($Parameter in $CookieParameters.GetEnumerator()) { - if ($Parameter.Name -eq "cookieAuth") { - $HeaderParameters["Cookie"] = $Parameter.Value - } else { - $HeaderParameters[$Parameter.Name] = $Parameter.Value - } - } - if ($CookieParameters -and $CookieParameters.Count -gt 1) { - Write-Warning "Multipe cookie parameters found. Curently only the first one is supported/used" - } - - # accept, content-type headers - $Accept = SelectHeaders -Headers $Accepts - if ($Accept) { - $HeaderParameters['Accept'] = $Accept - } - - $ContentType= SelectHeaders -Headers $ContentTypes - if ($ContentType) { - $HeaderParameters['Content-Type'] = $ContentType - } - - # add default headers if any - foreach ($header in $Configuration["DefaultHeaders"].GetEnumerator()) { - $HeaderParameters[$header.Name] = $header.Value - } - - - # constrcut URL query string - $HttpValues = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) - foreach ($Parameter in $QueryParameters.GetEnumerator()) { - if ($Parameter.Value.Count -gt 1) { // array - foreach ($Value in $Parameter.Value) { - $HttpValues.Add($Parameter.Key + '[]', $Value) - } - } else { - $HttpValues.Add($Parameter.Key,$Parameter.Value) - } - } - # Build the request and load it with the query string. - $UriBuilder = [System.UriBuilder]($RequestUri) - $UriBuilder.Query = $HttpValues.ToString() - - # include form parameters in the request body - if ($FormParameters -and $FormParameters.Count -gt 0) { - $RequestBody = $FormParameters - } - - if ($Body) { - $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 ` - -Headers $HeaderParameters ` - -Body $RequestBody ` - -ErrorAction Stop ` - -UseBasicParsing ` - -SkipCertificateCheck - - } else { - $Response = Invoke-WebRequest -Uri $UriBuilder.Uri ` - -Method $Method ` - -Headers $HeaderParameters ` - -Body $RequestBody ` - -ErrorAction Stop ` - -UseBasicParsing - } - - return @{ - Response = DeserializeResponse -Response $Response -ReturnType $ReturnType -ContentTypes $Response.Headers["Content-Type"] - StatusCode = $Response.StatusCode - Headers = $Response.Headers - } -} - -# Select JSON MIME if present, otherwise choose the first one if available -function SelectHeaders { - Param( - [Parameter(Mandatory)] - [AllowEmptyCollection()] - [String[]]$Headers - ) - - foreach ($Header in $Headers) { - if (IsJsonMIME -MIME $Header) { - return $Header - } - } - - if (!($Headers) -or $Headers.Count -eq 0) { - return $null - } else { - return $Headers[0] # return the first one - } -} - -function IsJsonMIME { - Param( - [Parameter(Mandatory)] - [string]$MIME - ) - - if ($MIME -match "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$") { - return $true - } else { - return $false - } -} - -function DeserializeResponse { - Param( - [Parameter(Mandatory)] - [AllowEmptyString()] - [string]$ReturnType, - [Parameter(Mandatory)] - [AllowEmptyString()] - [string]$Response, - [Parameter(Mandatory)] - [AllowEmptyCollection()] - [string[]]$ContentTypes - ) - - If ([string]::IsNullOrEmpty($ReturnType)) { # void response - return $Response - } Elseif ($ReturnType -match '\[\]$') { # array - return ConvertFrom-Json $Response - } Elseif (@("String", "Boolean", "System.DateTime") -contains $ReturnType) { # string, boolean ,datetime - return $Response - } Else { # others (e.g. model, file) - if ($ContentTypes) { - $ContentType = $null - if ($ContentTypes.Count -gt 1) { - $ContentType = SelectHeaders -Headers $ContentTypes - } else { - $ContentType = $ContentTypes[0] - } - - if (IsJsonMIME -MIME $ContentType) { # JSON - return ConvertFrom-Json $Response - } else { # XML, file, etc - return $Response - } - } else { # no content type in response header, returning raw response - return $Response - } - } -} From 0d66824619223487bd519b0bceb243621a0df8ce Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Fri, 22 May 2020 14:14:14 +0530 Subject: [PATCH 06/15] Htpp signing : added support for ecdsa --- .../resources/powershell/api_client.mustache | 5 +- .../powershell/configuration.mustache | 131 ++++ .../powershell/http_signature_auth.mustache | 442 +++++++++--- .../powershell/rsa_provider.mustache | 636 ++++++++---------- 4 files changed, 744 insertions(+), 470 deletions(-) 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..0b6e10f95564 100644 --- a/modules/openapi-generator/src/main/resources/powershell/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/api_client.mustache @@ -91,13 +91,14 @@ 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 } - $signedHeader = Get-{{{apiNamePrefix}}}HttpSignedHeader @httpSignHeaderArgument + $signedHeader = Get-{{apiNamePrefix}}HttpSignedHeader @httpSignHeaderArgument if($null -ne $signedHeader -and $signedHeader.Count -gt 0){ foreach($item in $signedHeader.GetEnumerator()){ $HeaderParameters[$item.Name] = $item.Value diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index fdd6bda48cef..ad3b75619563 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -373,3 +373,134 @@ 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, Accepted values are +"(request-target), (created), (expires), Host, Date, Digest". 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 interger 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()] + [ValidateSet("(request-target)","(created)","(expires)","Host","Date","Digest")] + [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 that 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 + } + + $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..0ae27096bf50 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,62 +1,9 @@ {{>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 .PARAMETER UriBuilder @@ -66,97 +13,390 @@ function Get-{{{apiNamePrefix}}}APIKeyInfo { .OUTPUTS Hashtable #> -function Get-{{{apiNamePrefix}}}HttpSignedHeader { +function Get-{{apiNamePrefix}}HttpSignedHeader { param( [string]$Method, [System.UriBuilder]$UriBuilder, [string]$Body ) + $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) + } + } - #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") { + $headerSinatureStr = Get-{{apiNamePrefix}}RSASignature -PrivateKeyFilePath $httpSigningConfiguration.KeyFilePath ` + -DataToSign $signatureHashString ` + -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm ` + -KeyPassPharse $httpSigningConfiguration.KeyPassPharse ` + -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm + } + elseif ($KeyType -eq "EC") { + $headerSinatureStr = Get-{{apiNamePrefix}}ECDSASignature -ECKeyFilePath $httpSigningConfiguration.KeyFilePath ` + -DataToSign $signatureHashString ` + -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm + } + #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 , $headerSinatureStr) - $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 KeyPassPharse + KeyPassPharse 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]$KeyPassPharse ) 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, $KeyPassPharse) + + 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 KeyPassPharse + KeyPassPharse 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]$KeyPassPharse + ) + 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($KeyPassPharse)){ + $ecdsa.ImportEncryptedPkcs8PrivateKey($KeyPassPharse,$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" .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 eader + 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..eba7f25016a7 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; @@ -8,370 +9,271 @@ using System.Text; namespace RSAEncryption { - public class RSAEncryptionProvider - { - - 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; - - // --------- Set up stream to decode the asn.1 encoded RSA private key ------ - MemoryStream mem = new MemoryStream(privkey); - BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading - byte bt = 0; - ushort twobytes = 0; - int elems = 0; - try - { - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number - return null; - bt = binr.ReadByte(); - if (bt != 0x00) - return null; - - - //------ all private key components are Integer sequences ---- - elems = GetIntegerSize(binr); - MODULUS = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - E = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - D = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - P = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - Q = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DP = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DQ = binr.ReadBytes(elems); - - 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(); - RSAparams.Modulus = MODULUS; - RSAparams.Exponent = E; - RSAparams.D = D; - RSAparams.P = P; - RSAparams.Q = Q; - RSAparams.DP = DP; - RSAparams.DQ = DQ; - RSAparams.InverseQ = IQ; - RSA.ImportParameters(RSAparams); - return RSA; - } - catch (Exception) - { - return null; - } - finally { binr.Close(); } - } - - private static int GetIntegerSize(BinaryReader binr) - { - byte bt = 0; - byte lowbyte = 0x00; - byte highbyte = 0x00; - int count = 0; - bt = binr.ReadByte(); - if (bt != 0x02) //expect integer - return 0; - bt = binr.ReadByte(); - - if (bt == 0x81) - count = binr.ReadByte(); // data size in next byte - else - if (bt == 0x82) - { - highbyte = binr.ReadByte(); // data size in next 2 bytes - lowbyte = binr.ReadByte(); - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; - count = BitConverter.ToInt32(modint, 0); - } - else - { - count = bt; // we already have the data size - } - while (binr.ReadByte() == 0x00) - { //remove high order zeros in data - count -= 1; - } - binr.BaseStream.Seek(-1, SeekOrigin.Current); - //last ReadByte wasn't a removed zero, so back up a byte - 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) - { - 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 - Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes - - // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] result = null; - byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget - - for (int j = 0; j < miter; j++) - { - // ---- Now hash consecutively for count times ------ - if (j == 0) - result = data00; //initialize - else - { - 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); - - Array.Clear(psbytes, 0, psbytes.Length); - Array.Clear(data00, 0, data00.Length); - 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(); - TripleDES alg = TripleDES.Create(); - alg.Key = desKey; - alg.IV = IV; - try - { - CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); - cs.Write(cipherData, 0, cipherData.Length); - cs.Close(); - } - catch (Exception exc) - { - Console.WriteLine(exc.Message); - 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(); - } - } - } - } -} + 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; + } + } + + public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) + { + byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; + + // --------- Set up stream to decode the asn.1 encoded RSA private key ------ + MemoryStream mem = new MemoryStream(privkey); + BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading + byte bt = 0; + ushort twobytes = 0; + int elems = 0; + try + { + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + return null; + bt = binr.ReadByte(); + if (bt != 0x00) + return null; + + //------ all private key components are Integer sequences ---- + elems = GetIntegerSize(binr); + MODULUS = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + E = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + D = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + P = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + Q = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DP = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DQ = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + IQ = binr.ReadBytes(elems); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); + RSAParameters RSAparams = new RSAParameters(); + RSAparams.Modulus = MODULUS; + RSAparams.Exponent = E; + RSAparams.D = D; + RSAparams.P = P; + RSAparams.Q = Q; + RSAparams.DP = DP; + RSAparams.DQ = DQ; + RSAparams.InverseQ = IQ; + RSA.ImportParameters(RSAparams); + return RSA; + } + catch (Exception) + { + return null; + } + finally { binr.Close(); } + } + + private static int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + byte lowbyte = 0x00; + byte highbyte = 0x00; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) //expect integer + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); // data size in next byte + else + if (bt == 0x82) + { + highbyte = binr.ReadByte(); // data size in next 2 bytes + lowbyte = binr.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; // we already have the data size + } + while (binr.ReadByte() == 0x00) + { //remove high order zeros in data + count -= 1; + } + binr.BaseStream.Seek(-1, SeekOrigin.Current); + //last ReadByte wasn't a removed zero, so back up a byte + return count; + } + + 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); + + // --- 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 + Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes + + // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- + MD5 md5 = new MD5CryptoServiceProvider(); + byte[] result = null; + byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget + + for (int j = 0; j < miter; j++) + { + // ---- Now hash consecutively for count times ------ + if (j == 0) + result = data00; //initialize + else + { + Array.Copy(result, hashtarget, result.Length); + Array.Copy(data00, 0, hashtarget, result.Length, data00.Length); + result = hashtarget; + } + + for (int i = 0; i < count; i++) + result = md5.ComputeHash(result); + Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial + } + byte[] deskey = new byte[24]; + Array.Copy(keymaterial, deskey, deskey.Length); + + Array.Clear(psbytes, 0, psbytes.Length); + Array.Clear(data00, 0, data00.Length); + Array.Clear(result, 0, result.Length); + Array.Clear(hashtarget, 0, hashtarget.Length); + Array.Clear(keymaterial, 0, keymaterial.Length); + return deskey; + } + + static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) + { + MemoryStream memst = new MemoryStream(); + TripleDES alg = TripleDES.Create(); + alg.Key = desKey; + alg.IV = IV; + try + { + CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(cipherData, 0, cipherData.Length); + cs.Close(); + } + catch (Exception){ + return null; + } + byte[] decryptedData = memst.ToArray(); + return decryptedData; + } + } +} \ No newline at end of file From 7c67a2afec1555568ad1917495463ba81a083a66 Mon Sep 17 00:00:00 2001 From: Ghufz <18732053+Ghufz@users.noreply.github.com> Date: Tue, 26 May 2020 12:06:36 +0530 Subject: [PATCH 07/15] Update modules/openapi-generator/src/main/resources/powershell/configuration.mustache Co-authored-by: Sebastien Rosset --- .../src/main/resources/powershell/configuration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index 3af2c643199e..2430317e7e66 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -463,7 +463,7 @@ function Set-{{apiNamePrefix}}ConfigurationHttpSigning{ if($HttpSigningHeader -contains "(expires)" -and $SignatureValidityPeriod -le 0){ - throw "SignatureValidityPeriod must be greater that 0 seconds." + throw "SignatureValidityPeriod must be greater than 0 seconds." } if($HttpSigningHeader -contains "(expires)"){ From c1f19a20d171fcd8d178cf31922443d78d2da056 Mon Sep 17 00:00:00 2001 From: Ghufz <18732053+Ghufz@users.noreply.github.com> Date: Tue, 26 May 2020 12:07:51 +0530 Subject: [PATCH 08/15] Update modules/openapi-generator/src/main/resources/powershell/configuration.mustache Co-authored-by: Sebastien Rosset --- .../src/main/resources/powershell/configuration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index 2430317e7e66..f3536e62ebe9 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -408,7 +408,7 @@ SigningAlgorithm specifies the signature algorithm, supported values are "RSASSA 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 interger value +SignatureValidityPeriod specifies the signature maximum validity time in seconds. It accepts integer value .OUTPUTS From 1381f3bfa385ae43dc82de5ced662226eaee0dac Mon Sep 17 00:00:00 2001 From: Ghufz <18732053+Ghufz@users.noreply.github.com> Date: Tue, 26 May 2020 12:09:41 +0530 Subject: [PATCH 09/15] Update modules/openapi-generator/src/main/resources/powershell/configuration.mustache Co-authored-by: Sebastien Rosset --- .../src/main/resources/powershell/configuration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index f3536e62ebe9..9c0ee39ff123 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -391,7 +391,7 @@ Ref: https://openapi-generator.tech KeyId for http signing .PARAMETER KeyFilePath -KeyFilePath for http signing +KeyFilePath for HTTP signing .PARAMETER KeyPassPhrase KeyPassPhrase, if the http signing key is protected From 83e81bc52971de380aebae999438151a04c18130 Mon Sep 17 00:00:00 2001 From: Ghufz <18732053+Ghufz@users.noreply.github.com> Date: Tue, 26 May 2020 12:09:57 +0530 Subject: [PATCH 10/15] Update modules/openapi-generator/src/main/resources/powershell/configuration.mustache Co-authored-by: Sebastien Rosset --- .../src/main/resources/powershell/configuration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index 9c0ee39ff123..8c2fb66872a8 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -394,7 +394,7 @@ KeyId for http signing KeyFilePath for HTTP signing .PARAMETER KeyPassPhrase -KeyPassPhrase, if the http signing key is protected +KeyPassPhrase, if the HTTP signing key is protected .PARAMETER HttpSigningHeader HttpSigningHeader list of http headers used to calculate the signature, Accepted values are From dd6d0b5576fa525e1b19c5d504260cc3c694bf8b Mon Sep 17 00:00:00 2001 From: Ghufz <18732053+Ghufz@users.noreply.github.com> Date: Tue, 26 May 2020 12:10:15 +0530 Subject: [PATCH 11/15] Update modules/openapi-generator/src/main/resources/powershell/configuration.mustache Co-authored-by: Sebastien Rosset --- .../src/main/resources/powershell/configuration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index 8c2fb66872a8..5fb5c3f4edf7 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -397,7 +397,7 @@ KeyFilePath for HTTP signing KeyPassPhrase, if the HTTP signing key is protected .PARAMETER HttpSigningHeader -HttpSigningHeader list of http headers used to calculate the signature, Accepted values are +HttpSigningHeader list of HTTP headers used to calculate the signature, Accepted values are "(request-target), (created), (expires), Host, Date, Digest". If no headers are specified then '(created)' sets as default. .PARAMETER HashAlgorithm From 984f77f9475da5a4965d6c2952ece69f02bbc790 Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Tue, 26 May 2020 16:25:17 +0530 Subject: [PATCH 12/15] HttpSigningHeader accepts any header available in request to calculate the signature. --- .../resources/powershell/api_client.mustache | 1 + .../powershell/configuration.mustache | 23 +++++++---- .../powershell/http_signature_auth.mustache | 41 +++++++++++-------- 3 files changed, 40 insertions(+), 25 deletions(-) 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 0b6e10f95564..90b5fcb89611 100644 --- a/modules/openapi-generator/src/main/resources/powershell/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/api_client.mustache @@ -97,6 +97,7 @@ function Invoke-{{{apiNamePrefix}}}ApiClient { 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 3af2c643199e..4ea8a37ee0f5 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -388,17 +388,21 @@ a key. The 'Authorization' header is added to outbound HTTP requests. Ref: https://openapi-generator.tech .PARAMETER KeyId -KeyId for http signing +KeyId for HTTP signing .PARAMETER KeyFilePath -KeyFilePath for http signing +KeyFilePath for HTTP signing .PARAMETER KeyPassPhrase -KeyPassPhrase, if the http signing key is protected +KeyPassPhrase, if the HTTP signing key is protected .PARAMETER HttpSigningHeader -HttpSigningHeader list of http headers used to calculate the signature, Accepted values are -"(request-target), (created), (expires), Host, Date, Digest". If no headers are specified then '(created)' sets as default. +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" @@ -408,7 +412,7 @@ SigningAlgorithm specifies the signature algorithm, supported values are "RSASSA 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 interger value +SignatureValidityPeriod specifies the signature maximum validity time in seconds. It accepts integer value .OUTPUTS @@ -426,7 +430,6 @@ function Set-{{apiNamePrefix}}ConfigurationHttpSigning{ [securestring]$KeyPassPhrase, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] - [ValidateSet("(request-target)","(created)","(expires)","Host","Date","Digest")] [string[]] $HttpSigningHeader = @("(created)"), [Parameter(Mandatory = $false)] [ValidateSet("sha256","sha512")] @@ -463,7 +466,7 @@ function Set-{{apiNamePrefix}}ConfigurationHttpSigning{ if($HttpSigningHeader -contains "(expires)" -and $SignatureValidityPeriod -le 0){ - throw "SignatureValidityPeriod must be greater that 0 seconds." + throw "SignatureValidityPeriod must be greater than 0 seconds." } if($HttpSigningHeader -contains "(expires)"){ @@ -481,6 +484,10 @@ function Set-{{apiNamePrefix}}ConfigurationHttpSigning{ $httpSignatureConfiguration["SigningAlgorithm"] = $SigningAlgorithm } + if($null -ne $KeyPassPhrase){ + $httpSignatureConfiguration["KeyPassPhrase"] = $KeyPassPhrase + } + $Script:Configuration["HttpSigning"] = New-Object -TypeName PSCustomObject -Property $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 0ae27096bf50..78e4887ae416 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,11 +1,11 @@ {{>partial_header}} <# .SYNOPSIS - Gets the headers for http signature. + Gets the headers for HTTP signature. .DESCRIPTION Gets the headers for the http sigature. .PARAMETER Method - Http method + HTTP method .PARAMETER UriBuilder UriBuilder for url and query parameter .PARAMETER Body @@ -17,7 +17,8 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { param( [string]$Method, [System.UriBuilder]$UriBuilder, - [string]$Body + [string]$Body, + [hashtable]$RequestHeader ) $HEADER_REQUEST_TARGET = '(request-target)' @@ -83,6 +84,10 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { 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." } } @@ -102,16 +107,17 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { $KeyType = Get-{{apiNamePrefix}}KeyTypeFromFile -KeyFilePath $httpSigningConfiguration.KeyFilePath if ($keyType -eq "RSA") { - $headerSinatureStr = Get-{{apiNamePrefix}}RSASignature -PrivateKeyFilePath $httpSigningConfiguration.KeyFilePath ` + $headerSignatureStr = Get-{{apiNamePrefix}}RSASignature -PrivateKeyFilePath $httpSigningConfiguration.KeyFilePath ` -DataToSign $signatureHashString ` -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm ` - -KeyPassPharse $httpSigningConfiguration.KeyPassPharse ` + -KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase ` -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm } elseif ($KeyType -eq "EC") { - $headerSinatureStr = Get-{{apiNamePrefix}}ECDSASignature -ECKeyFilePath $httpSigningConfiguration.KeyFilePath ` + $headerSignatureStr = Get-{{apiNamePrefix}}ECDSASignature -ECKeyFilePath $httpSigningConfiguration.KeyFilePath ` -DataToSign $signatureHashString ` - -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm + -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm ` + -KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase } #Depricated <#$cryptographicScheme = Get-{{apiNamePrefix}}CryptographicScheme -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm ` @@ -130,7 +136,7 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { } $authorizationHeaderValue += [string]::Format(",headers=""{0}"",signature=""{1}""", - $headersKeysString , $headerSinatureStr) + $headersKeysString , $headerSignatureStr) $HttpSignedRequestHeader[$HEADER_AUTHORIZATION] = $authorizationHeaderValue return $HttpSignedRequestHeader @@ -148,8 +154,8 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { Specify the data to sign .PARAMETER HashAlgorithmName HashAlgorithm to calculate the hash -.PARAMETER KeyPassPharse - KeyPassPharse for the encrypted key +.PARAMETER KeyPassPhrase + KeyPassPhrase for the encrypted key .OUTPUTS Base64String #> @@ -159,7 +165,7 @@ function Get-{{apiNamePrefix}}RSASignature { [byte[]]$DataToSign, [string]$HashAlgorithmName, [string]$SigningAlgorithm, - [securestring]$KeyPassPharse + [securestring]$KeyPassPhrase ) try { @@ -192,7 +198,7 @@ function Get-{{apiNamePrefix}}RSASignature { $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, $KeyPassPharse) + [System.Security.Cryptography.RSA]$rsa = [RSAEncryption.RSAEncryptionProvider]::GetRSAProviderFromPemFile($PrivateKeyFilePath, $KeyPassPhrase) if ($SigningAlgorithm -eq "RSASSA-PSS") { throw "$SigningAlgorithm is not supported on $($PSVersionTable.PSVersion)" @@ -223,8 +229,8 @@ function Get-{{apiNamePrefix}}RSASignature { Specify the data to sign .PARAMETER HashAlgorithmName HashAlgorithm to calculate the hash -.PARAMETER KeyPassPharse - KeyPassPharse for the encrypted key +.PARAMETER KeyPassPhrase + KeyPassPhrase for the encrypted key .OUTPUTS Base64String #> @@ -237,7 +243,7 @@ function Get-{{apiNamePrefix}}ECDSASignature { [Parameter(Mandatory = $false)] [String]$HashAlgorithmName, [Parameter(Mandatory = $false)] - [securestring]$KeyPassPharse + [securestring]$KeyPassPhrase ) if (!(Test-Path -Path $ECKeyFilePath)) { throw "key file path does not exist." @@ -257,8 +263,8 @@ function Get-{{apiNamePrefix}}ECDSASignature { #$ecdsa = [System.Security.Cryptography.ECDsaCng]::New($cngKey) $ecdsa = [System.Security.Cryptography.ECDsaCng]::New() [int]$bytCount =0 - if(![string]::IsNullOrEmpty($KeyPassPharse)){ - $ecdsa.ImportEncryptedPkcs8PrivateKey($KeyPassPharse,$keyBytes,[ref]$bytCount) + if(![string]::IsNullOrEmpty($KeyPassPhrase)){ + $ecdsa.ImportEncryptedPkcs8PrivateKey($KeyPassPhrase,$keyBytes,[ref]$bytCount) } else{ $ecdsa.ImportPkcs8PrivateKey($keyBytes,[ref]$bytCount) @@ -287,6 +293,7 @@ function Get-{{apiNamePrefix}}ECDSASignature { 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 #> From e954b4ff2cb31304daa8f6eab8b7786e9dc2db1d Mon Sep 17 00:00:00 2001 From: Ghufz <18732053+Ghufz@users.noreply.github.com> Date: Tue, 26 May 2020 16:35:04 +0530 Subject: [PATCH 13/15] Update modules/openapi-generator/src/main/resources/powershell/http_signature_auth.mustache Co-authored-by: Sebastien Rosset --- .../main/resources/powershell/http_signature_auth.mustache | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 78e4887ae416..bc2c808b748f 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 @@ -396,7 +396,7 @@ function Get-{{apiNamePrefix}}KeyTypeFromFile { $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 eader + <#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 @@ -406,4 +406,4 @@ function Get-{{apiNamePrefix}}KeyTypeFromFile { throw "Either the key is invalid or key is not supported" } return $keyType -} \ No newline at end of file +} From 9ef5ac6c13b59e918c75eb8ab206b769e7a0c99c Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Wed, 27 May 2020 11:54:26 +0530 Subject: [PATCH 14/15] Incorporated the review comments --- .../resources/powershell/api_client.mustache | 4 +- .../powershell/configuration.mustache | 85 ++- .../powershell/http_signature_auth.mustache | 34 +- .../powershell/rsa_provider.mustache | 534 +++++++++--------- 4 files changed, 328 insertions(+), 329 deletions(-) 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 90b5fcb89611..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,7 +91,7 @@ function Invoke-{{{apiNamePrefix}}}ApiClient { {{#hasHttpSignatureMethods}} # http signature authentication - $httpSigningConfig = Get-{{apiNamePrefix}}ConfigurationHttpSigning + $httpSigningConfig = Get-{{{apiNamePrefix}}}ConfigurationHttpSigning if ($null -ne $httpSigningConfig) { $httpSignHeaderArgument = @{ Method = $Method @@ -99,7 +99,7 @@ function Invoke-{{{apiNamePrefix}}}ApiClient { Body = $Body RequestHeader = $HeaderParameters } - $signedHeader = Get-{{apiNamePrefix}}HttpSignedHeader @httpSignHeaderArgument + $signedHeader = Get-{{{apiNamePrefix}}}HttpSignedHeader @httpSignHeaderArgument if($null -ne $signedHeader -and $signedHeader.Count -gt 0){ foreach($item in $signedHeader.GetEnumerator()){ $HeaderParameters[$item.Name] = $item.Value diff --git a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache index 4ea8a37ee0f5..622bce2b65ae 100644 --- a/modules/openapi-generator/src/main/resources/powershell/configuration.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/configuration.mustache @@ -418,7 +418,7 @@ SignatureValidityPeriod specifies the signature maximum validity time in seconds System.Collections.Hashtable #> -function Set-{{apiNamePrefix}}ConfigurationHttpSigning{ +function Set-{{{apiNamePrefix}}}ConfigurationHttpSigning { [CmdletBinding()] param( [Parameter(Mandatory = $true)] @@ -432,80 +432,79 @@ function Set-{{apiNamePrefix}}ConfigurationHttpSigning{ [ValidateNotNullOrEmpty()] [string[]] $HttpSigningHeader = @("(created)"), [Parameter(Mandatory = $false)] - [ValidateSet("sha256","sha512")] + [ValidateSet("sha256", "sha512")] [string] $HashAlgorithm = "sha256", [Parameter(Mandatory = $false)] - [ValidateSet("RSASSA-PKCS1-v1_5","RSASSA-PSS")] + [ValidateSet("RSASSA-PKCS1-v1_5", "RSASSA-PSS")] [string]$SigningAlgorithm , [Parameter(Mandatory = $false)] [int]$SignatureValidityPeriod ) - Process{ - $httpSignatureConfiguration = @{} + Process { + $httpSignatureConfiguration = @{ } - if(Test-Path -Path $KeyFilePath){ - $httpSignatureConfiguration["KeyId"] = $KeyId - $httpSignatureConfiguration["KeyFilePath"] = $KeyFilePath - }else{ - throw "Private key file path does not exist" - } + 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" + $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" )) - { + 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)" -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 ($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 $HashAlgorithm ) { + $httpSignatureConfiguration["HashAlgorithm"] = $HashAlgorithm + } - if($null -ne $SigningAlgorithm){ - $httpSignatureConfiguration["SigningAlgorithm"] = $SigningAlgorithm - } + if ($null -ne $SigningAlgorithm) { + $httpSignatureConfiguration["SigningAlgorithm"] = $SigningAlgorithm + } - if($null -ne $KeyPassPhrase){ - $httpSignatureConfiguration["KeyPassPhrase"] = $KeyPassPhrase - } + if ($null -ne $KeyPassPhrase) { + $httpSignatureConfiguration["KeyPassPhrase"] = $KeyPassPhrase + } - $Script:Configuration["HttpSigning"] = New-Object -TypeName PSCustomObject -Property $httpSignatureConfiguration -} + $Script:Configuration["HttpSigning"] = New-Object -TypeName PSCustomObject -Property $httpSignatureConfiguration + } } <# .SYNOPSIS -Get the configuration object '{{apiNamePrefix}}ConfigurationHttpSigning'. +Get the configuration object '{{{apiNamePrefix}}}ConfigurationHttpSigning'. .DESCRIPTION -Get the configuration object '{{apiNamePrefix}}ConfigurationHttpSigning'. +Get the configuration object '{{{apiNamePrefix}}}ConfigurationHttpSigning'. .OUTPUTS [PSCustomObject] #> -function Get-{{apiNamePrefix}}ConfigurationHttpSigning{ +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 78e4887ae416..3428137f264d 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 @@ -13,7 +13,7 @@ .OUTPUTS Hashtable #> -function Get-{{apiNamePrefix}}HttpSignedHeader { +function Get-{{{apiNamePrefix}}}HttpSignedHeader { param( [string]$Method, [System.UriBuilder]$UriBuilder, @@ -42,11 +42,11 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { $HttpSignedRequestHeader = @{ } $HttpSignatureHeader = @{ } $TargetHost = $UriBuilder.Host - $httpSigningConfiguration = Get-{{apiNamePrefix}}ConfigurationHttpSigning + $httpSigningConfiguration = Get-{{{apiNamePrefix}}}ConfigurationHttpSigning $Digest = $null #get the body digest - $bodyHash = Get-{{apiNamePrefix}}StringHash -String $Body -HashName $httpSigningConfiguration.HashAlgorithm + $bodyHash = Get-{{{apiNamePrefix}}}StringHash -String $Body -HashName $httpSigningConfiguration.HashAlgorithm if ($httpSigningConfiguration.HashAlgorithm -eq "SHA256") { $Digest = [String]::Format("SHA-256={0}", [Convert]::ToBase64String($bodyHash)) } @@ -65,12 +65,12 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { $HttpSignatureHeader.Add($HEADER_REQUEST_TARGET, $requestTargetPath) } elseif ($headerItem -eq $HEADER_CREATED) { - $created = Get-{{apiNamePrefix}}UnixTime -Date $dateTime -TotalTime TotalSeconds + $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 + $expireEpocTime = Get-{{{apiNamePrefix}}}UnixTime -Date $expire -TotalTime TotalSeconds $HttpSignatureHeader.Add($HEADER_EXPIRES, $expireEpocTime) } elseif ($headerItem -eq $HEADER_HOST) { @@ -101,26 +101,26 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { $headerValuesString = $headerValuesList -join "`n" #Gets the hash of the headers value - $signatureHashString = Get-{{apiNamePrefix}}StringHash -String $headerValuesString -HashName $httpSigningConfiguration.HashAlgorithm + $signatureHashString = Get-{{{apiNamePrefix}}}StringHash -String $headerValuesString -HashName $httpSigningConfiguration.HashAlgorithm #Gets the Key type to select the correct signing alogorithm - $KeyType = Get-{{apiNamePrefix}}KeyTypeFromFile -KeyFilePath $httpSigningConfiguration.KeyFilePath + $KeyType = Get-{{{apiNamePrefix}}}KeyTypeFromFile -KeyFilePath $httpSigningConfiguration.KeyFilePath if ($keyType -eq "RSA") { - $headerSignatureStr = Get-{{apiNamePrefix}}RSASignature -PrivateKeyFilePath $httpSigningConfiguration.KeyFilePath ` + $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 ` + $headerSignatureStr = Get-{{{apiNamePrefix}}}ECDSASignature -ECKeyFilePath $httpSigningConfiguration.KeyFilePath ` -DataToSign $signatureHashString ` -HashAlgorithmName $httpSigningConfiguration.HashAlgorithm ` -KeyPassPhrase $httpSigningConfiguration.KeyPassPhrase } #Depricated - <#$cryptographicScheme = Get-{{apiNamePrefix}}CryptographicScheme -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm ` + <#$cryptographicScheme = Get-{{{apiNamePrefix}}}CryptographicScheme -SigningAlgorithm $httpSigningConfiguration.SigningAlgorithm ` -HashAlgorithm $httpSigningConfiguration.HashAlgorithm #> $cryptographicScheme = "hs2019" @@ -159,7 +159,7 @@ function Get-{{apiNamePrefix}}HttpSignedHeader { .OUTPUTS Base64String #> -function Get-{{apiNamePrefix}}RSASignature { +function Get-{{{apiNamePrefix}}}RSASignature { Param( [string]$PrivateKeyFilePath, [byte[]]$DataToSign, @@ -194,7 +194,7 @@ function Get-{{apiNamePrefix}}RSASignature { } } else { - $rsa_provider_path = Join-Path -Path $PSScriptRoot -ChildPath "{{apiNamePrefix}}RSAEncryptionProvider.cs" + $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 @@ -234,7 +234,7 @@ function Get-{{apiNamePrefix}}RSASignature { .OUTPUTS Base64String #> -function Get-{{apiNamePrefix}}ECDSASignature { +function Get-{{{apiNamePrefix}}}ECDSASignature { param( [Parameter(Mandatory = $true)] [string]$ECKeyFilePath, @@ -297,7 +297,7 @@ function Get-{{apiNamePrefix}}ECDSASignature { .Outputs String #> -Function Get-{{apiNamePrefix}}StringHash { +Function Get-{{{apiNamePrefix}}}StringHash { param( [Parameter(Mandatory = $true)] [AllowEmptyString()] @@ -322,7 +322,7 @@ Function Get-{{apiNamePrefix}}StringHash { .Outputs Integer #> -function Get-{{apiNamePrefix}}UnixTime { +function Get-{{{apiNamePrefix}}}UnixTime { param( [Parameter(Mandatory = $true)] [DateTime]$Date, @@ -341,7 +341,7 @@ function Get-{{apiNamePrefix}}UnixTime { } } -function Get-{{apiNamePrefix}}CryptographicScheme { +function Get-{{{apiNamePrefix}}}CryptographicScheme { param( [Parameter(Mandatory = $true)] [string]$SigningAlgorithm, @@ -370,7 +370,7 @@ function Get-{{apiNamePrefix}}CryptographicScheme { .Outputs String #> -function Get-{{apiNamePrefix}}KeyTypeFromFile { +function Get-{{{apiNamePrefix}}}KeyTypeFromFile { param( [Parameter(Mandatory = $true)] [string]$KeyFilePath 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 eba7f25016a7..7a671929fb2b 100644 --- a/modules/openapi-generator/src/main/resources/powershell/rsa_provider.mustache +++ b/modules/openapi-generator/src/main/resources/powershell/rsa_provider.mustache @@ -9,271 +9,271 @@ using System.Text; 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; - } - } - - public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) - { - byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; - - // --------- Set up stream to decode the asn.1 encoded RSA private key ------ - MemoryStream mem = new MemoryStream(privkey); - BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading - byte bt = 0; - ushort twobytes = 0; - int elems = 0; - try - { - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number - return null; - bt = binr.ReadByte(); - if (bt != 0x00) - return null; - - //------ all private key components are Integer sequences ---- - elems = GetIntegerSize(binr); - MODULUS = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - E = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - D = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - P = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - Q = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DP = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DQ = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - IQ = binr.ReadBytes(elems); - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); - RSAParameters RSAparams = new RSAParameters(); - RSAparams.Modulus = MODULUS; - RSAparams.Exponent = E; - RSAparams.D = D; - RSAparams.P = P; - RSAparams.Q = Q; - RSAparams.DP = DP; - RSAparams.DQ = DQ; - RSAparams.InverseQ = IQ; - RSA.ImportParameters(RSAparams); - return RSA; - } - catch (Exception) - { - return null; - } - finally { binr.Close(); } - } - - private static int GetIntegerSize(BinaryReader binr) - { - byte bt = 0; - byte lowbyte = 0x00; - byte highbyte = 0x00; - int count = 0; - bt = binr.ReadByte(); - if (bt != 0x02) //expect integer - return 0; - bt = binr.ReadByte(); - - if (bt == 0x81) - count = binr.ReadByte(); // data size in next byte - else - if (bt == 0x82) - { - highbyte = binr.ReadByte(); // data size in next 2 bytes - lowbyte = binr.ReadByte(); - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; - count = BitConverter.ToInt32(modint, 0); - } - else - { - count = bt; // we already have the data size - } - while (binr.ReadByte() == 0x00) - { //remove high order zeros in data - count -= 1; - } - binr.BaseStream.Seek(-1, SeekOrigin.Current); - //last ReadByte wasn't a removed zero, so back up a byte - return count; - } - - 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); - - // --- 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 - Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes - - // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] result = null; - byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget - - for (int j = 0; j < miter; j++) - { - // ---- Now hash consecutively for count times ------ - if (j == 0) - result = data00; //initialize - else - { - Array.Copy(result, hashtarget, result.Length); - Array.Copy(data00, 0, hashtarget, result.Length, data00.Length); - result = hashtarget; - } - - for (int i = 0; i < count; i++) - result = md5.ComputeHash(result); - Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial - } - byte[] deskey = new byte[24]; - Array.Copy(keymaterial, deskey, deskey.Length); - - Array.Clear(psbytes, 0, psbytes.Length); - Array.Clear(data00, 0, data00.Length); - Array.Clear(result, 0, result.Length); - Array.Clear(hashtarget, 0, hashtarget.Length); - Array.Clear(keymaterial, 0, keymaterial.Length); - return deskey; - } - - static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) - { - MemoryStream memst = new MemoryStream(); - TripleDES alg = TripleDES.Create(); - alg.Key = desKey; - alg.IV = IV; - try - { - CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); - cs.Write(cipherData, 0, cipherData.Length); - cs.Close(); - } - catch (Exception){ - return null; - } - byte[] decryptedData = memst.ToArray(); - return decryptedData; - } - } + 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; + } + } + + public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) + { + byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; + + // --------- Set up stream to decode the asn.1 encoded RSA private key ------ + MemoryStream mem = new MemoryStream(privkey); + BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading + byte bt = 0; + ushort twobytes = 0; + int elems = 0; + try + { + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + return null; + bt = binr.ReadByte(); + if (bt != 0x00) + return null; + + //------ all private key components are Integer sequences ---- + elems = GetIntegerSize(binr); + MODULUS = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + E = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + D = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + P = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + Q = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DP = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DQ = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + IQ = binr.ReadBytes(elems); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); + RSAParameters RSAparams = new RSAParameters(); + RSAparams.Modulus = MODULUS; + RSAparams.Exponent = E; + RSAparams.D = D; + RSAparams.P = P; + RSAparams.Q = Q; + RSAparams.DP = DP; + RSAparams.DQ = DQ; + RSAparams.InverseQ = IQ; + RSA.ImportParameters(RSAparams); + return RSA; + } + catch (Exception) + { + return null; + } + finally { binr.Close(); } + } + + private static int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + byte lowbyte = 0x00; + byte highbyte = 0x00; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) //expect integer + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); // data size in next byte + else + if (bt == 0x82) + { + highbyte = binr.ReadByte(); // data size in next 2 bytes + lowbyte = binr.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; // we already have the data size + } + while (binr.ReadByte() == 0x00) + { //remove high order zeros in data + count -= 1; + } + binr.BaseStream.Seek(-1, SeekOrigin.Current); + //last ReadByte wasn't a removed zero, so back up a byte + return count; + } + + 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); + + // --- 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 + Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes + + // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ---- + MD5 md5 = new MD5CryptoServiceProvider(); + byte[] result = null; + byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget + + for (int j = 0; j < miter; j++) + { + // ---- Now hash consecutively for count times ------ + if (j == 0) + result = data00; //initialize + else + { + Array.Copy(result, hashtarget, result.Length); + Array.Copy(data00, 0, hashtarget, result.Length, data00.Length); + result = hashtarget; + } + + for (int i = 0; i < count; i++) + result = md5.ComputeHash(result); + Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial + } + byte[] deskey = new byte[24]; + Array.Copy(keymaterial, deskey, deskey.Length); + + Array.Clear(psbytes, 0, psbytes.Length); + Array.Clear(data00, 0, data00.Length); + Array.Clear(result, 0, result.Length); + Array.Clear(hashtarget, 0, hashtarget.Length); + Array.Clear(keymaterial, 0, keymaterial.Length); + return deskey; + } + + static byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) + { + MemoryStream memst = new MemoryStream(); + TripleDES alg = TripleDES.Create(); + alg.Key = desKey; + alg.IV = IV; + try + { + CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(cipherData, 0, cipherData.Length); + cs.Close(); + } + catch (Exception){ + return null; + } + byte[] decryptedData = memst.ToArray(); + return decryptedData; + } + } } \ No newline at end of file From a78a8dd75b17355c633a8df9edab16635faf8c55 Mon Sep 17 00:00:00 2001 From: Ghufran Zahidi Date: Wed, 27 May 2020 12:00:49 +0530 Subject: [PATCH 15/15] addressed the merge conflict --- .../src/main/resources/powershell/http_signature_auth.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eb4910ee9380..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 @@ -406,4 +406,4 @@ function Get-{{{apiNamePrefix}}}KeyTypeFromFile { throw "Either the key is invalid or key is not supported" } return $keyType -} +} \ No newline at end of file