<style>p { margin: 0; }span.fr-emoticon.fr-emoticon-img { background-repeat: no-repeat !important; font-size: inherit; height: 1em; width: 1em; min-height: 20px; min-width: 20px; display: inline-block; margin: -0.1em 0.1em 0.1em; line-height: 1; vertical-align: middle; } span.fr-emoticon { font-weight: normal; font-family: "Apple Color Emoji", "Segoe UI Emoji", "NotoColorEmoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols"; display: inline; line-height: 0; } blockquote { border-left: solid 2px #5e35b1; color: #5e35b1; margin-left:0; padding-left:5px;}blockquote blockquote{ border-color: #00bcd4; color: #00bcd4;}blockquote blockquote blockquote{ border-color: #43a047; color: #43a047;} table.grid{ border-collapse: collapse;} table.grid td, table.grid th { border: 1px solid #ddd;} .fr-fic.fr-dib{ display: block; margin: 5px auto;}.fr-fic.fr-dib.fr-fir{ text-align: right; margin: 5px 0 5px auto;}.fr-fic.fr-dib.fr-fil{ text-align: left; margin: 5px auto 5px 0;}.fr-fic.fr-dii{ float: none; margin: 5px auto;}.fr-fic.fr-dii.fr-fil{ float: left; margin: 5px auto;}.fr-fic.fr-dii.fr-fir{ float: right; margin: 5px auto;}img.fr-dib.fr-fir { margin-right: 0; text-align: right;}img.fr-dib.fr-fil { margin-left: 0; text-align: left;}img.fr-dib { margin: 5px auto; display: block; float: none;}img.fr-bordered { box-sizing: content-box; border: solid 5px #CCC;}img.fr-shadow { box-shadow: 10px 10px 5px 0px #cccccc;}img.fr-rounded { border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;}</style><p>This article gives example code for how to implement http request signing for the "Validate Signature using Secret Key" authentication method in Integration Runbooks.</p><p><br></p><p>This involves your application and Halo sharing a "Secret Key". Your application will sign all requests that are sent to the runbook start URL using this secret key. Halo will verify the signature on each request using the same secret key and reject it if the signature is not valid or if the payload does not match the original payload that the signature is for.</p><p><br></p><p>The example is using C# in .Net 8. By replicating similar logic you can do this in other programming languages.</p>
<style>p { margin: 0; }span.fr-emoticon.fr-emoticon-img { background-repeat: no-repeat !important; font-size: inherit; height: 1em; width: 1em; min-height: 20px; min-width: 20px; display: inline-block; margin: -0.1em 0.1em 0.1em; line-height: 1; vertical-align: middle; } span.fr-emoticon { font-weight: normal; font-family: "Apple Color Emoji", "Segoe UI Emoji", "NotoColorEmoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols"; display: inline; line-height: 0; } blockquote { border-left: solid 2px #5e35b1; color: #5e35b1; margin-left:0; padding-left:5px;}blockquote blockquote{ border-color: #00bcd4; color: #00bcd4;}blockquote blockquote blockquote{ border-color: #43a047; color: #43a047;} table.grid{ border-collapse: collapse;} table.grid td, table.grid th { border: 1px solid #ddd;} .fr-fic.fr-dib{ display: block; margin: 5px auto;}.fr-fic.fr-dib.fr-fir{ text-align: right; margin: 5px 0 5px auto;}.fr-fic.fr-dib.fr-fil{ text-align: left; margin: 5px auto 5px 0;}.fr-fic.fr-dii{ float: none; margin: 5px auto;}.fr-fic.fr-dii.fr-fil{ float: left; margin: 5px auto;}.fr-fic.fr-dii.fr-fir{ float: right; margin: 5px auto;}img.fr-dib.fr-fir { margin-right: 0; text-align: right;}img.fr-dib.fr-fil { margin-left: 0; text-align: left;}img.fr-dib { margin: 5px auto; display: block; float: none;}img.fr-bordered { box-sizing: content-box; border: solid 5px #CCC;}img.fr-shadow { box-shadow: 10px 10px 5px 0px #cccccc;}img.fr-rounded { border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box;}</style><p>In your Integration Runbook, set "Runbook Start Access" to "Can be started from Halo and from a public endpoint". Set "Authentication" to "Validate Signature using Secret Key".</p><p><br></p><p>Generate a new random secret key - ensure this is at least 32 characters for optimal security. For simplicity, in this example our Secret Key is "abcdefg".</p><p><br></p><p>Populate the form as follows;</p><p><br></p><p>1. Set Secret Key to your secret key you generated. In this case "abcdefg".</p><p>2. Set Signature header name to "x-halo-signature". This can have any name but this is the name we've chosen for this example.</p><p>3. Signature prefix to "sha256=".</p><p>4. Signing Algorithm to "SHA-256".</p><p>5. Digest to "Base64".</p><p><br></p><p><img src="https://halo.haloservicedesk.com/api/attachment/image?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Ijc5YTJjMmFlLWQ0OGEtNGY1NS05NDE5LTVkNjBlNTkyOGE1OSJ9.00h8MH8GZ7XPL8xEbMfOR59cjVk4s0mhB5Gph_hNnbc" class="fr-fic fr-fil fr-dib" width="435" style="width: 437px; height: 414.71px;" height="415"></p><p><br></p><p><strong>Remember to store your secret securely.</strong></p><p><br></p><p>The following example code shows how to start this runbook and pass the Authentication.</p><p><br></p><p>If successfully validated this is recorded in the log. If validation is unsuccessful the request is not recorded and a 401 Unauthorized response is returned.</p><p><br></p><p><br></p><p><br></p><p id="isPasted"><br></p><pre><code class="lang-c#"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">StartHaloRunbook</span>()
</span>{
<span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> client = <span class="hljs-keyword">new</span> HttpClient())
{
<span class="hljs-keyword">try</span>
{
<span class="hljs-keyword">string</span> secretKey = <span class="hljs-string">"abcdefg"</span>; <span class="hljs-comment">// This is the secret key you add to the Integration Runbook record. ENSURE YOU STORE THIS SECURELY.</span>
<span class="hljs-keyword">string</span> signatureHeader = <span class="hljs-string">"x-halo-signature"</span>; <span class="hljs-comment">// This is the signature header you add to the Integration Runbook record.</span>
<span class="hljs-keyword">string</span> url = <span class="hljs-string">"http://localhost:3000/api/automation/a6212ef8-8c89-4bb6-a31f-989ba1248cbb"</span>; <span class="hljs-comment">// Set this to the runbook URL you are given on the Integration Runbook config screen.</span>
<span class="hljs-keyword">string</span> jsonPayload = <span class="hljs-string">"{\"value\": \"test\"}"</span>; <span class="hljs-comment">// Your payload.</span>
<span class="hljs-keyword">var</span> date = DateTimeOffset.UtcNow.ToString(<span class="hljs-string">"r"</span>, CultureInfo.InvariantCulture);
client.DefaultRequestHeaders.Add(<span class="hljs-string">"x-halo-date"</span>, date);
client.DefaultRequestHeaders.Add(<span class="hljs-string">"x-halo-signature-algorithm"</span>, <span class="hljs-string">"rsa-sha256"</span>);
<span class="hljs-keyword">var</span> encoding = <span class="hljs-keyword">new</span> UTF8Encoding();
<span class="hljs-keyword">byte</span>[] webhookKeyBytes = encoding.GetBytes(secretKey);
<span class="hljs-keyword">byte</span>[] payloadStringBytes = encoding.GetBytes(jsonPayload);
<span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> hmac = <span class="hljs-keyword">new</span> HMACSHA256(webhookKeyBytes))
{
<span class="hljs-keyword">byte</span>[] hashMessage = hmac.ComputeHash(payloadStringBytes);
client.DefaultRequestHeaders.Add(signatureHeader, Convert.ToBase64String(hashMessage));
}
<span class="hljs-keyword">var</span> body = <span class="hljs-keyword">new</span> StringContent(jsonPayload, Encoding.UTF8, <span class="hljs-string">"application/json"</span>);
<span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> client.PostAsync(url, body);
<span class="hljs-comment">// Handle the response</span>
}
<span class="hljs-keyword">catch</span> (Exception e)
{
<span class="hljs-comment">// Handle the error.</span>
}
}
}
</code></pre><p><br></p>