Azure Text Analytics using C# giving errors - c#

I am trying to use code that I got from github: https://github.com/liamca/azure-search-machine-learning-text-analytics and the creating of an index works perfect, but the Keyphrase portion is giving me a 403 - Forbidden: Access is denied error. This happens in the TextExtractionHelper class on the following line of code:
if (!response.IsSuccessStatusCode)
{
throw new Exception("Call to get key phrases failed with HTTP status code: " +
response.StatusCode + " and contents: " + content);
}
Based on the information in the comments, I created an account at this link: https://datamarket.azure.com/account/keys and used the key that it provided, but I am getting the above error.
Here is the code in case you do not want to download from github:
class Program
{
static string searchServiceName = "<removed>"; // Learn more here: https://azure.microsoft.com/en-us/documentation/articles/search-what-is-azure-search/
static string searchServiceAPIKey = "<removed>";
static string azureMLTextAnalyticsKey = "<removed>"; // Learn more here: https://azure.microsoft.com/en-us/documentation/articles/machine-learning-apps-text-analytics/
static string indexName = "textanalytics";
static SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(searchServiceAPIKey));
static SearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
static void Main(string[] args)
{
string filetext = "Build great search experiences for your web and mobile apps. " +
"Many applications use search as the primary interaction pattern for their users. When it comes to search, user expectations are high. They expect great relevance, suggestions, near-instantaneous responses, multiple languages, faceting, and more. Azure Search makes it easy to add powerful and sophisticated search capabilities to your website or application. The integrated Microsoft natural language stack, also used in Bing and Office, has been improved over 16 years of development. Quickly and easily tune search results, and construct rich, fine-tuned ranking models to tie search results to business goals. Reliable throughput and storage provide fast search indexing and querying to support time-sensitive search scenarios. " +
"Reduce complexity with a fully managed service. " +
"Azure Search removes the complexity of setting up and managing your own search index. This fully managed service helps you avoid the hassle of dealing with index corruption, service availability, scaling, and service updates. Create multiple indexes with no incremental cost per index. Easily scale up or down as the traffic and data volume of your application changes.";
// Note, this will create a new Azure Search Index for the text and the key phrases
Console.WriteLine("Creating Azure Search index...");
AzureSearch.CreateIndex(serviceClient, indexName);
// Apply the Machine Learning Text Extraction to retrieve only the key phrases
Console.WriteLine("Extracting key phrases from processed text... \r\n");
KeyPhraseResult keyPhraseResult = TextExtraction.ProcessText(azureMLTextAnalyticsKey, filetext);
Console.WriteLine("Found the following phrases... \r\n");
foreach (var phrase in keyPhraseResult.KeyPhrases)
Console.WriteLine(phrase);
// Take the resulting key phrases to a new Azure Search Index
// It is highly recommended that you upload documents in batches rather
// individually like is done here
Console.WriteLine("Uploading extracted text to Azure Search...\r\n");
AzureSearch.UploadDocuments(indexClient, "1", keyPhraseResult);
Console.WriteLine("Wait 5 seconds for content to become searchable...\r\n");
Thread.Sleep(5000);
// Execute a test search
Console.WriteLine("Execute Search...");
AzureSearch.SearchDocuments(indexClient, "Azure Search");
Console.WriteLine("All done. Press any key to continue.");
Console.ReadLine();
}
}
The below is in the TextExtractionHelper class:
/// <summary>
/// This is a sample program that shows how to use the Azure ML Text Analytics app (https://datamarket.azure.com/dataset/amla/text-analytics)
/// </summary>
public class TextExtraction
{
private const string ServiceBaseUri = "https://api.datamarket.azure.com/";
public static KeyPhraseResult ProcessText(string accountKey, string inputText)
{
KeyPhraseResult keyPhraseResult = new KeyPhraseResult();
using (var httpClient = new HttpClient())
{
string inputTextEncoded = HttpUtility.UrlEncode(inputText);
httpClient.BaseAddress = new Uri(ServiceBaseUri);
string creds = "AccountKey:" + accountKey;
string authorizationHeader = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(creds));
httpClient.DefaultRequestHeaders.Add("Authorization", authorizationHeader);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// get key phrases
string keyPhrasesRequest = "data.ashx/amla/text-analytics/v1/GetKeyPhrases?Text=" + inputTextEncoded;
Task<HttpResponseMessage> responseTask = httpClient.GetAsync(keyPhrasesRequest);
responseTask.Wait();
HttpResponseMessage response = responseTask.Result;
Task<string> contentTask = response.Content.ReadAsStringAsync();
contentTask.Wait();
string content = contentTask.Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception("Call to get key phrases failed with HTTP status code: " +
response.StatusCode + " and contents: " + content);
}
keyPhraseResult = JsonConvert.DeserializeObject<KeyPhraseResult>(content);
}
return keyPhraseResult;
}
}
/// <summary>
/// Class to hold result of Key Phrases call
/// </summary>
public class KeyPhraseResult
{
public List<string> KeyPhrases { get; set; }
}
/// <summary>
/// Class to hold result of Sentiment call
/// </summary>
public class SentimentResult
{
public double Score { get; set; }
}
/// <summary>
/// Class to hold result of Language detection call
/// </summary>
public class LanguageResult
{
public bool UnknownLanguage { get; set; }
public IList<DetectedLanguage> DetectedLanguages { get; set; }
}
/// <summary>
/// Class to hold information about a single detected language
/// </summary>
public class DetectedLanguage
{
public string Name { get; set; }
/// <summary>
/// This is the short ISO 639-1 standard form of representing
/// all languages. The short form is a 2 letter representation of the language.
/// en = English, fr = French for example
/// </summary>
public string Iso6391Name { get; set; }
public double Score { get; set; }
}
UPDATE
After many hours of taking different sample code and trying to put them together, I finally got something "kind of" working. Here is all my code:
class Program
{
static string searchServiceName = "<removed>"; // Learn more here: https://azure.microsoft.com/en-us/documentation/articles/search-what-is-azure-search/
static string searchServiceAPIKey = "<removed>";
//static string azureMLTextAnalyticsKey = "<removed>"; // Learn more here: https://azure.microsoft.com/en-us/documentation/articles/machine-learning-apps-text-analytics/
static string indexName = "textanalytics";
static SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(searchServiceAPIKey));
static SearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
static void Main()
{
MakeRequests();
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
static async void MakeRequests()
{
// Note, this will create a new Azure Search Index for the text and the key phrases
Console.WriteLine("Creating Azure Search index...");
AzureSearch.CreateIndex(serviceClient, indexName);
// Apply the Machine Learning Text Extraction to retrieve only the key phrases
Console.WriteLine("Extracting key phrases from processed text... \r\n");
KeyPhraseResult keyPhraseResult = await TextExtraction.ProcessText();
Console.WriteLine("Found the following phrases... \r\n");
foreach (var phrase in keyPhraseResult.KeyPhrases)
Console.WriteLine(phrase);
// Take the resulting key phrases to a new Azure Search Index
// It is highly recommended that you upload documents in batches rather
// individually like is done here
Console.WriteLine("Uploading extracted text to Azure Search...\r\n");
AzureSearch.UploadDocuments(indexClient, "1", keyPhraseResult);
Console.WriteLine("Wait 5 seconds for content to become searchable...\r\n");
Thread.Sleep(5000);
// Execute a test search
Console.WriteLine("Execute Search...");
AzureSearch.SearchDocuments(indexClient, "Azure Search");
Console.WriteLine("All done. Press any key to continue.");
Console.ReadLine();
}
}
Here is my TextExtractionHelper class:
public class TextExtraction
{
static string azureMLTextAnalyticsKey = "<Removed>"; // Learn more here: https://azure.microsoft.com/en-us/documentation/articles/machine-learning-apps-text-analytics/
private const string ServiceBaseUri = "https://westus.api.cognitive.microsoft.com/";
public static async Task<KeyPhraseResult> ProcessText()
{
string filetext = "Build great search experiences for your web and mobile apps. " +
"Many applications use search as the primary interaction pattern for their users. When it comes to search, user expectations are high. They expect great relevance, suggestions, near-instantaneous responses, multiple languages, faceting, and more. Azure Search makes it easy to add powerful and sophisticated search capabilities to your website or application. The integrated Microsoft natural language stack, also used in Bing and Office, has been improved over 16 years of development. Quickly and easily tune search results, and construct rich, fine-tuned ranking models to tie search results to business goals. Reliable throughput and storage provide fast search indexing and querying to support time-sensitive search scenarios. " +
"Reduce complexity with a fully managed service. " +
"Azure Search removes the complexity of setting up and managing your own search index. This fully managed service helps you avoid the hassle of dealing with index corruption, service availability, scaling, and service updates. Create multiple indexes with no incremental cost per index. Easily scale up or down as the traffic and data volume of your application changes.";
KeyPhraseResult keyPhraseResult = new KeyPhraseResult();
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(ServiceBaseUri);
// Request headers.
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", azureMLTextAnalyticsKey);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
byte[] byteData = Encoding.UTF8.GetBytes("{\"documents\":[" +
"{\"id\":\"1\",\"text\":\"" + filetext + "\"},]}");
//byte[] byteData = Encoding.UTF8.GetBytes("{\"documents\":[" +
// "{\"id\":\"1\",\"text\":\"Build great search experiences for your web and mobile apps." +
// "Many applications use search as the primary interaction pattern for their users. When it comes to search, user expectations are high. They expect great relevance, suggestions, near-instantaneous responses, multiple languages, faceting, and more. Azure Search makes it easy to add powerful and sophisticated search capabilities to your website or application. The integrated Microsoft natural language stack, also used in Bing and Office, has been improved over 16 years of development. Quickly and easily tune search results, and construct rich, fine-tuned ranking models to tie search results to business goals. Reliable throughput and storage provide fast search indexing and querying to support time-sensitive search scenarios." +
// "Reduce complexity with a fully managed service. " +
// "Azure Search removes the complexity of setting up and managing your own search index. This fully managed service helps you avoid the hassle of dealing with index corruption, service availability, scaling, and service updates. Create multiple indexes with no incremental cost per index. Easily scale up or down as the traffic and data volume of your application changes.\"}," +
// "]}");
// Detect key phrases:
var keyPhrasesRequest = "text/analytics/v2.0/keyPhrases";
//var response = await CallEndpoint(httpClient, uri, byteData);
// get key phrases
using (var getcontent = new ByteArrayContent(byteData))
{
getcontent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync(keyPhrasesRequest, getcontent);
Task<string> contentTask = response.Content.ReadAsStringAsync();
string content = contentTask.Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception("Call to get key phrases failed with HTTP status code: " +
response.StatusCode + " and contents: " + content);
}
keyPhraseResult = JsonConvert.DeserializeObject<KeyPhraseResult>(content);
//return await response.Content.ReadAsStringAsync();
}
}
return keyPhraseResult;
}
}
/// <summary>
/// Class to hold result of Key Phrases call
/// </summary>
public class KeyPhraseResult
{
public List<string> KeyPhrases { get; set; }
}
/// <summary>
/// Class to hold result of Sentiment call
/// </summary>
public class SentimentResult
{
public double Score { get; set; }
}
/// <summary>
/// Class to hold result of Language detection call
/// </summary>
public class LanguageResult
{
public bool UnknownLanguage { get; set; }
public IList<DetectedLanguage> DetectedLanguages { get; set; }
}
/// <summary>
/// Class to hold information about a single detected language
/// </summary>
public class DetectedLanguage
{
public string Name { get; set; }
/// <summary>
/// This is the short ISO 639-1 standard form of representing
/// all languages. The short form is a 2 letter representation of the language.
/// en = English, fr = French for example
/// </summary>
public string Iso6391Name { get; set; }
public double Score { get; set; }
}
So I am now able to pull the KeyPhrases from the text! But, now I am sitting with a problem where it doesnt seem like the JSON string is being deserialized and my keyPhraseResult is now getting a null value.
What am I missing ?
If anyone is able to help, I would greatly appreciate it.
Thanks!

So I got it working! With the help from this link: Deserializing JSON using C# to return items, which i posted to simplify where my issue was occurring now.
So what this code is doing is the following:
Creating an Index in Azure called textanalytics.
Creating a JSON string of the text provided.
Retrieving the KeyPhrases and adding these to the Index created in point 1 above.
Below is my entire code, in case it helps someone else:
(Please ensure that you add the relevant references from Nuget packages: Microsoft.Azure.Search and Newtonsoft.Json)
Program.cs(This is a console application):
using Microsoft.Azure.Search;
using System;
using System.Configuration;
using System.IO;
using System.Threading;
namespace AzureSearchTextAnalytics
{
class Program
{
static string searchServiceName = "<removed>"; // This is the Azure Search service name that you create in Azure
static string searchServiceAPIKey = "<removed>"; // This is the Primary key that is provided after creating a Azure Search Service
static string indexName = "textanalytics";
static SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(searchServiceAPIKey));
static SearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
static void Main()
{
MakeRequests();
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
static async void MakeRequests()
{
// Note, this will create a new Azure Search Index for the text and the key phrases
Console.WriteLine("Creating Azure Search index...");
AzureSearch.CreateIndex(serviceClient, indexName);
// Apply the Machine Learning Text Extraction to retrieve only the key phrases
Console.WriteLine("Extracting key phrases from processed text... \r\n");
KeyPhraseResult keyPhraseResult = await TextExtraction.ProcessText();
Console.WriteLine("Found the following phrases... \r\n");
foreach (var phrase in keyPhraseResult.KeyPhrases)
Console.WriteLine(phrase);
// Take the resulting key phrases to a new Azure Search Index
// It is highly recommended that you upload documents in batches rather
// individually like is done here
Console.WriteLine("Uploading extracted text to Azure Search...\r\n");
AzureSearch.UploadDocuments(indexClient, "1", keyPhraseResult);
Console.WriteLine("Wait 5 seconds for content to become searchable...\r\n");
Thread.Sleep(5000);
// Execute a test search
Console.WriteLine("Execute Search...");
AzureSearch.SearchDocuments(indexClient, "Azure Search");
Console.WriteLine("All done. Press any key to continue.");
Console.ReadLine();
}
}
}
My TextExtractionHelper.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json;
using System.Configuration; // get it from http://www.newtonsoft.com/json
using Newtonsoft.Json.Linq;
namespace AzureSearchTextAnalytics
{
/// </summary>
public class TextExtraction
{
static string azureMLTextAnalyticsKey = "<removed>"; // This key you will get when you have added TextAnalytics in Azure.
private const string ServiceBaseUri = "https://westus.api.cognitive.microsoft.com/"; //This you will get when you have added TextAnalytics in Azure
public static async Task<KeyPhraseResult> ProcessText()
{
string filetext = "Build great search experiences for your web and mobile apps. " +
"Many applications use search as the primary interaction pattern for their users. When it comes to search, user expectations are high. They expect great relevance, suggestions, near-instantaneous responses, multiple languages, faceting, and more. Azure Search makes it easy to add powerful and sophisticated search capabilities to your website or application. The integrated Microsoft natural language stack, also used in Bing and Office, has been improved over 16 years of development. Quickly and easily tune search results, and construct rich, fine-tuned ranking models to tie search results to business goals. Reliable throughput and storage provide fast search indexing and querying to support time-sensitive search scenarios. " +
"Reduce complexity with a fully managed service. " +
"Azure Search removes the complexity of setting up and managing your own search index. This fully managed service helps you avoid the hassle of dealing with index corruption, service availability, scaling, and service updates. Create multiple indexes with no incremental cost per index. Easily scale up or down as the traffic and data volume of your application changes.";
KeyPhraseResult keyPhraseResult = new KeyPhraseResult();
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(ServiceBaseUri);
// Request headers.
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", azureMLTextAnalyticsKey);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
byte[] byteData = Encoding.UTF8.GetBytes("{\"documents\":[" +
"{\"id\":\"1\",\"text\":\"" + filetext + "\"},]}");
// Detect key phrases:
var keyPhrasesRequest = "text/analytics/v2.0/keyPhrases";
// get key phrases
using (var getcontent = new ByteArrayContent(byteData))
{
getcontent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync(keyPhrasesRequest, getcontent);
Task<string> contentTask = response.Content.ReadAsStringAsync();
string content = contentTask.Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception("Call to get key phrases failed with HTTP status code: " +
response.StatusCode + " and contents: " + content);
}
var result = JsonConvert.DeserializeObject<RootObject>(content);
keyPhraseResult.KeyPhrases = result.documents[0].keyPhrases;
}
}
return keyPhraseResult;
}
}
public class Documents
{
public List<string> keyPhrases { get; set; }
public string id { get; set; }
}
public class RootObject
{
public List<Documents> documents { get; set; }
public List<object> errors { get; set; }
}
/// <summary>
/// Class to hold result of Key Phrases call
/// </summary>
public class KeyPhraseResult
{
public List<string> KeyPhrases { get; set; }
}
}
AzureSearch.cs:
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureSearchTextAnalytics
{
public class AzureSearch
{
public static void CreateIndex(SearchServiceClient serviceClient, string indexName)
{
if (serviceClient.Indexes.Exists(indexName))
{
serviceClient.Indexes.Delete(indexName);
}
var definition = new Index()
{
Name = indexName,
Fields = new[]
{
new Field("fileId", DataType.String) { IsKey = true },
new Field("fileText", DataType.String) { IsSearchable = true, IsFilterable = false, IsSortable = false, IsFacetable = false },
new Field("keyPhrases", DataType.Collection(DataType.String)) { IsSearchable = true, IsFilterable = true, IsFacetable = true }
}
};
serviceClient.Indexes.Create(definition);
}
public static void UploadDocuments(SearchIndexClient indexClient, string fileId, KeyPhraseResult keyPhraseResult)
{
List<IndexAction> indexOperations = new List<IndexAction>();
var doc = new Document();
doc.Add("fileId", fileId);
doc.Add("keyPhrases", keyPhraseResult.KeyPhrases.ToList());
indexOperations.Add(IndexAction.Upload(doc));
try
{
indexClient.Documents.Index(new IndexBatch(indexOperations));
}
catch (IndexBatchException e)
{
// Sometimes when your Search service is under load, indexing will fail for some of the documents in
// the batch. Depending on your application, you can take compensating actions like delaying and
// retrying. For this simple demo, we just log the failed document keys and continue.
Console.WriteLine(
"Failed to index some of the documents: {0}",
String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
}
}
public static void SearchDocuments(SearchIndexClient indexClient, string searchText)
{
// Search using the supplied searchText and output documents that match
try
{
var sp = new SearchParameters();
DocumentSearchResult<OCRTextIndex> response = indexClient.Documents.Search<OCRTextIndex>(searchText, sp);
foreach (SearchResult<OCRTextIndex> result in response.Results)
{
Console.WriteLine("File ID: {0}", result.Document.fileId);
Console.WriteLine("Key Phrases: {0}", string.Join(",", result.Document.keyPhrases));
}
}
catch (Exception e)
{
Console.WriteLine("Failed search: {0}", e.Message.ToString());
}
}
}
}
DataModel.cs
using Microsoft.Azure.Search.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureSearchTextAnalytics
{
[SerializePropertyNamesAsCamelCase]
public class OCRTextIndex
{
public string fileId { get; set; }
public string[] keyPhrases { get; set; }
}
}

Related

Does ActiveMQ Artemis support getting messages from the ActiveMQ.Advisory.Queue?

I have a .NETcore application/client which has several defined endpoints to get data from the broker. One end point is to get a list of all of the addresses but when I run it it gives back an empty response. We are using amq7. Would the C# code need to be refactored to leverage Artemis? How would I be able to get Broker/Queue metrics in Artemis with .NET?
Below is an example of the code the code we are using from the ActiveMQ NMS website
using System;
using Apache.NMS;
using Apache.NMS.Util;
using Apache.NMS.ActiveMQ;
using Apache.NMS.ActiveMQ.Commands;
namespace AdvisoryExample
{
class AdvisoryExample
{
private IConnection connection;
private ISession session;
public const String QUEUE\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.Queue";
public const String TOPIC\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.Topic";
public const String TEMPQUEUE\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.TempQueue";
public const String TEMPTOPIC\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.TempTopic";
public const String ALLDEST\_ADVISORY\_DESTINATION = QUEUE\_ADVISORY\_DESTINATION + "," +
TOPIC\_ADVISORY\_DESTINATION + "," +
TEMPQUEUE\_ADVISORY\_DESTINATION + "," +
TEMPTOPIC\_ADVISORY\_DESTINATION;
AdvisoryExample()
{
IConnectionFactory factory = new ConnectionFactory();
connection = factory.CreateConnection();
connection.Start();
session = connection.CreateSession();
}
void EnumerateQueues()
{
Console.WriteLine("Listing all Queues on Broker:");
IDestination dest = session.GetTopic(QUEUE\_ADVISORY\_DESTINATION);
using(IMessageConsumer consumer = session.CreateConsumer(dest))
{
IMessage advisory;
while((advisory = consumer.Receive(TimeSpan.FromMilliseconds(2000))) != null)
{
ActiveMQMessage amqMsg = advisory as ActiveMQMessage;
if(amqMsg.DataStructure != null)
{
DestinationInfo info = amqMsg.DataStructure as DestinationInfo;
if(info != null)
{
Console.WriteLine(" Queue: " + info.Destination.ToString() );
}
}
}
}
Console.WriteLine("Listing Complete.");
}
The default broker.xml disables advisory support, e.g.:
<acceptor name="artemis">tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true;supportAdvisory=false;suppressInternalManagementObjects=false</acceptor>
The key parameter is supportAdvisory=false which you can read more about in the documentation. You can set supportAdvisory=true or omit it entirely at which point you should be able to use advisories from your client.
Regarding other ways to get broker/queue metrics, ActiveMQ Artemis supports a number of different ways to access this data (e.g. JMX, HTTP via Jolokia, management messages, web console, CLI tools). You can read more about this in the documentation. The broker also supports exporting metrics via plugins so you can integrate with solutions like Prometheus.

The endpoint for my Azure APIs (Translator Text, Computer Vision) is not accesible (error code 404)

I followed the Hololens Tutorials provided by Microsoft and encountered some problems regarding Azure services in 301 (https://learn.microsoft.com/en-us/windows/mixed-reality/mr-azure-301#chapter-4--setup-debug-canvas) and 302 (https://learn.microsoft.com/en-us/windows/mixed-reality/mr-azure-302). I think the problem has something to do with the endpoint, because the error code 404 appears when checking https://westeurope.api.cognitive.microsoft.com/.
I use the Hololens first generation, Unity 2017.4.27f1, HoloToolkit-Unity-Examples-2017.4.3.0 and Visual Studio Community 2017. I've tried some of the Hololens tutorials, which all worked fine with these versions. My code is the one provided by Microsoft for the Tutorials 301 and 302. I've changed the location in Azure from
'global' to 'Western Europe' by creating a new ressource group and have now https://westeurope.api.cognitive.microsoft.com/ as my endpoint, I also created new authentification keys for the APIs and updated them in my code.
Instead of myKey I inserted the first key of the respective API (Translator Text, Computer Vision). The code below is from Tutorial 302 and the class 'VisionManager.cs' (which seems to be the responsible one for the communication with the services of the Computer Vision API).
public static VisionManager instance;
// you must insert your service key here!
private string authorizationKey = "myKey";
private const string ocpApimSubscriptionKeyHeader = "Ocp-Apim-Subscription-Key";
private string visionAnalysisEndpoint = "https://westcentralus.api.cognitive.microsoft.com/vision/v2.0"; // This is where you need to update your endpoint, if you set your location to something other than west-us.
internal byte[] imageBytes;
internal string imagePath;
/// <summary>
/// Call the Computer Vision Service to submit the image.
/// </summary>
public IEnumerator AnalyseLastImageCaptured()
{
WWWForm webForm = new WWWForm();
using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(visionAnalysisEndpoint, webForm))
{
// gets a byte array out of the saved image
imageBytes = GetImageAsByteArray(imagePath);
unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
unityWebRequest.SetRequestHeader(ocpApimSubscriptionKeyHeader, authorizationKey);
// the download handler will help receiving the analysis from Azure
unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
// the upload handler will help uploading the byte array with the request
unityWebRequest.uploadHandler = new UploadHandlerRaw(imageBytes);
unityWebRequest.uploadHandler.contentType = "application/octet-stream";
yield return unityWebRequest.SendWebRequest();
long responseCode = unityWebRequest.responseCode;
try
{
string jsonResponse = null;
jsonResponse = unityWebRequest.downloadHandler.text;
// The response will be in Json format
// therefore it needs to be deserialized into the classes AnalysedObject and TagData
AnalysedObject analysedObject = new AnalysedObject();
analysedObject = JsonUtility.FromJson<AnalysedObject>(jsonResponse);
if (analysedObject.tags == null)
{
Debug.Log("analysedObject.tagData is null");
}
else
{
Dictionary<string, float> tagsDictionary = new Dictionary<string, float>();
foreach (TagData td in analysedObject.tags)
{
TagData tag = td as TagData;
tagsDictionary.Add(tag.name, tag.confidence);
}
ResultsLabel.instance.SetTagsToLastLabel(tagsDictionary);
}
}
catch (Exception exception)
{
Debug.Log("Json exception.Message: " + exception.Message);
}
yield return null;
}
}
[System.Serializable]
public class TagData
{
public string name;
public float confidence;
}
[System.Serializable]
public class AnalysedObject
{
public TagData[] tags;
public string requestId;
public object metadata;
}
// Use this for initialization
void Start () {
}
private void Awake()
{
// allows this instance to behave like a singleton
instance = this;
}
// Update is called once per frame
void Update () {
}
/// <summary>
/// Returns the contents of the specified file as a byte array.
/// </summary>
private static byte[] GetImageAsByteArray(string imageFilePath)
{
FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fileStream);
return binaryReader.ReadBytes((int)fileStream.Length);
}
I expected the output to be like the examples in the tutorials, but instead I got the same GUI results without any updates. In 301 I could see the same screen, but the text field 'You just said' and the 'Translation' never updated when I spoke. In 302 I could airtap and make a screenshot and could see at first 'Analyzing' and later on 'I see:' but without any results of detected objects. So it seems to me, that the Azure APIs services can't be contacted and I don't get any analysis data in return (probably you notice, that I'm quite new to the whole topic, so maybe my assumption is wrong).

C# Save Data To Database and Exit Program

Hey Guys i have a script written in c# that generates some encryption keys that i want to save into my database my code looks like this:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.WindowsAzure.MediaServices.Client;
using Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization;
using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption;
using Microsoft.WindowsAzure.MediaServices.Client.Widevine;
using Newtonsoft.Json;
namespace DeliverDRMLicenses
{
class Program
{
// Read values from the App.config file.
private static readonly string _mediaServicesAccountName =
ConfigurationManager.AppSettings["MediaServicesAccountName"];
private static readonly string _mediaServicesAccountKey =
ConfigurationManager.AppSettings["MediaServicesAccountKey"];
private static readonly Uri _sampleIssuer =
new Uri(ConfigurationManager.AppSettings["Issuer"]);
private static readonly Uri _sampleAudience =
new Uri(ConfigurationManager.AppSettings["Audience"]);
// Field for service context.
private static CloudMediaContext _context = null;
private static MediaServicesCredentials _cachedCredentials = null;
static void Main(string[] args)
{
// Create and cache the Media Services credentials in a static class variable.
_cachedCredentials = new MediaServicesCredentials(
_mediaServicesAccountName,
_mediaServicesAccountKey);
// Used the cached credentials to create CloudMediaContext.
_context = new CloudMediaContext(_cachedCredentials);
bool tokenRestriction = true;
string tokenTemplateString = null;
string drm_key_id = null;
IContentKey key = CreateCommonTypeContentKey();
// Print out the key ID and Key in base64 string format
drm_key_id = key.Id;
Console.WriteLine(" key:{0}",
key.Id, System.Convert.ToBase64String(key.GetClearKeyValue()));
Console.WriteLine(" key value:{1} ",
key.Id, System.Convert.ToBase64String(key.GetClearKeyValue()));
sbasedrmdataDataSetTableAdapters.sbase_drm_keysTableAdapter sbasedrmTableAdapter =
new sbasedrmdataDataSetTableAdapters.sbase_drm_keysTableAdapter();
sbasedrmTableAdapter.Insert(drm_key_id);
Console.WriteLine("Protection key: {0} ",
key.ProtectionKeyId, System.Convert.ToBase64String(key.GetClearKeyValue()));
Console.WriteLine("PlayReady URL: {0}",
key.GetKeyDeliveryUrl(ContentKeyDeliveryType.PlayReadyLicense));
Console.WriteLine("Widevin URL: {0}",
key.GetKeyDeliveryUrl(ContentKeyDeliveryType.Widevine));
if (tokenRestriction)
tokenTemplateString = AddTokenRestrictedAuthorizationPolicy(key);
else
AddOpenAuthorizationPolicy(key);
Console.WriteLine("Auth policy: {0}",
key.AuthorizationPolicyId);
Console.WriteLine();
Console.ReadLine();
Environment.Exit(0);
}
static public void AddOpenAuthorizationPolicy(IContentKey contentKey)
{
// Create ContentKeyAuthorizationPolicy with Open restrictions
// and create authorization policy
List<ContentKeyAuthorizationPolicyRestriction> restrictions =
new List<ContentKeyAuthorizationPolicyRestriction>
{
new ContentKeyAuthorizationPolicyRestriction
{
Name = "Open",
KeyRestrictionType = (int)ContentKeyRestrictionType.Open,
Requirements = null
}
};
// Configure PlayReady and Widevine license templates.
string PlayReadyLicenseTemplate = ConfigurePlayReadyLicenseTemplate();
string WidevineLicenseTemplate = ConfigureWidevineLicenseTemplate();
IContentKeyAuthorizationPolicyOption PlayReadyPolicy =
_context.ContentKeyAuthorizationPolicyOptions.Create("",
ContentKeyDeliveryType.PlayReadyLicense,
restrictions, PlayReadyLicenseTemplate);
IContentKeyAuthorizationPolicyOption WidevinePolicy =
_context.ContentKeyAuthorizationPolicyOptions.Create("",
ContentKeyDeliveryType.Widevine,
restrictions, WidevineLicenseTemplate);
IContentKeyAuthorizationPolicy contentKeyAuthorizationPolicy = _context.
ContentKeyAuthorizationPolicies.
CreateAsync("Deliver Common Content Key with no restrictions").
Result;
contentKeyAuthorizationPolicy.Options.Add(PlayReadyPolicy);
contentKeyAuthorizationPolicy.Options.Add(WidevinePolicy);
// Associate the content key authorization policy with the content key.
contentKey.AuthorizationPolicyId = contentKeyAuthorizationPolicy.Id;
contentKey = contentKey.UpdateAsync().Result;
}
public static string AddTokenRestrictedAuthorizationPolicy(IContentKey contentKey)
{
string tokenTemplateString = GenerateTokenRequirements();
List<ContentKeyAuthorizationPolicyRestriction> restrictions =
new List<ContentKeyAuthorizationPolicyRestriction>
{
new ContentKeyAuthorizationPolicyRestriction
{
Name = "Token Authorization Policy",
KeyRestrictionType = (int)ContentKeyRestrictionType.TokenRestricted,
Requirements = tokenTemplateString,
}
};
// Configure PlayReady and Widevine license templates.
string PlayReadyLicenseTemplate = ConfigurePlayReadyLicenseTemplate();
string WidevineLicenseTemplate = ConfigureWidevineLicenseTemplate();
IContentKeyAuthorizationPolicyOption PlayReadyPolicy =
_context.ContentKeyAuthorizationPolicyOptions.Create("Token option",
ContentKeyDeliveryType.PlayReadyLicense,
restrictions, PlayReadyLicenseTemplate);
IContentKeyAuthorizationPolicyOption WidevinePolicy =
_context.ContentKeyAuthorizationPolicyOptions.Create("Token option",
ContentKeyDeliveryType.Widevine,
restrictions, WidevineLicenseTemplate);
IContentKeyAuthorizationPolicy contentKeyAuthorizationPolicy = _context.
ContentKeyAuthorizationPolicies.
CreateAsync("Deliver Common Content Key with token restrictions").
Result;
contentKeyAuthorizationPolicy.Options.Add(PlayReadyPolicy);
contentKeyAuthorizationPolicy.Options.Add(WidevinePolicy);
// Associate the content key authorization policy with the content key
contentKey.AuthorizationPolicyId = contentKeyAuthorizationPolicy.Id;
contentKey = contentKey.UpdateAsync().Result;
return tokenTemplateString;
}
static private string GenerateTokenRequirements()
{
TokenRestrictionTemplate template = new TokenRestrictionTemplate(TokenType.SWT);
template.PrimaryVerificationKey = new SymmetricVerificationKey();
template.AlternateVerificationKeys.Add(new SymmetricVerificationKey());
template.Audience = _sampleAudience.ToString();
template.Issuer = _sampleIssuer.ToString();
template.RequiredClaims.Add(TokenClaim.ContentKeyIdentifierClaim);
return TokenRestrictionTemplateSerializer.Serialize(template);
}
static private string ConfigurePlayReadyLicenseTemplate()
{
// The following code configures PlayReady License Template using .NET classes
// and returns the XML string.
//The PlayReadyLicenseResponseTemplate class represents the template
//for the response sent back to the end user.
//It contains a field for a custom data string between the license server
//and the application (may be useful for custom app logic)
//as well as a list of one or more license templates.
PlayReadyLicenseResponseTemplate responseTemplate =
new PlayReadyLicenseResponseTemplate();
// The PlayReadyLicenseTemplate class represents a license template
// for creating PlayReady licenses
// to be returned to the end users.
// It contains the data on the content key in the license
// and any rights or restrictions to be
// enforced by the PlayReady DRM runtime when using the content key.
PlayReadyLicenseTemplate licenseTemplate = new PlayReadyLicenseTemplate();
// Configure whether the license is persistent
// (saved in persistent storage on the client)
// or non-persistent (only held in memory while the player is using the license).
licenseTemplate.LicenseType = PlayReadyLicenseType.Nonpersistent;
// AllowTestDevices controls whether test devices can use the license or not.
// If true, the MinimumSecurityLevel property of the license
// is set to 150. If false (the default),
// the MinimumSecurityLevel property of the license is set to 2000.
licenseTemplate.AllowTestDevices = true;
// You can also configure the Play Right in the PlayReady license by using the PlayReadyPlayRight class.
// It grants the user the ability to playback the content subject to the zero or more restrictions
// configured in the license and on the PlayRight itself (for playback specific policy).
// Much of the policy on the PlayRight has to do with output restrictions
// which control the types of outputs that the content can be played over and
// any restrictions that must be put in place when using a given output.
// For example, if the DigitalVideoOnlyContentRestriction is enabled,
//then the DRM runtime will only allow the video to be displayed over digital outputs
//(analog video outputs won’t be allowed to pass the content).
// IMPORTANT: These types of restrictions can be very powerful
// but can also affect the consumer experience.
// If the output protections are configured too restrictive,
// the content might be unplayable on some clients.
// For more information, see the PlayReady Compliance Rules document.
// For example:
//licenseTemplate.PlayRight.AgcAndColorStripeRestriction = new AgcAndColorStripeRestriction(1);
responseTemplate.LicenseTemplates.Add(licenseTemplate);
return MediaServicesLicenseTemplateSerializer.Serialize(responseTemplate);
}
private static string ConfigureWidevineLicenseTemplate()
{
var template = new WidevineMessage
{
allowed_track_types = AllowedTrackTypes.SD_HD,
content_key_specs = new[]
{
new ContentKeySpecs
{
required_output_protection =
new RequiredOutputProtection { hdcp = Hdcp.HDCP_NONE},
security_level = 1,
track_type = "SD"
}
},
policy_overrides = new
{
can_play = true,
can_persist = true,
can_renew = false
}
};
string configuration = JsonConvert.SerializeObject(template);
return configuration;
}
static public IContentKey CreateCommonTypeContentKey()
{
// Create envelope encryption content key
Guid keyId = Guid.NewGuid();
byte[] contentKey = GetRandomBuffer(16);
IContentKey key = _context.ContentKeys.Create(
keyId,
contentKey,
"ContentKey",
ContentKeyType.CommonEncryption);
return key;
}
static private byte[] GetRandomBuffer(int length)
{
var returnValue = new byte[length];
using (var rng =
new System.Security.Cryptography.RNGCryptoServiceProvider())
{
rng.GetBytes(returnValue);
}
return returnValue;
}
}
}
So the issue im having is when i try to run the program i get an error at this line
sbasedrmTableAdapter.Insert(drm_key_id);
and the error i receive is:
Error CS7036 There is no argument given that corresponds to the
required formal parameter 'drm_key' of
'sbase_drm_keysTableAdapter.Insert(string, string, string, string)'
How can i solve this error
It looks like you are trying to call a method that requires 4 strings as parameters with just one parameter. Try supplying the correct parameters to the method.
Your problem looks quite similar to: OOP inheritance and default constructor

How to check EU VAT using VIES SOAP service in C#

I have an ASP.NET website that needs to check a user-supplied VAT. The VIES Service can be used for that which exposes a SOAP API.
I need a dead-simple example on how to validate a VAT using this service. In PHP, it's these 4 lines: https://stackoverflow.com/a/14340495. For C#, I have found some articles from 2010 that do not work or are tens or even hundreds lines of "wrappers", "helper services" etc.
I don't need any of that, can someone provide PHP-like four-liner that would check VAT in C#? Thank you.
The simplest way I found is just to send an XML and parse it when it comes back:
var wc = new WebClient();
var request = #"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:urn=""urn:ec.europa.eu:taxud:vies:services:checkVat:types"">
<soapenv:Header/>
<soapenv:Body>
<urn:checkVat>
<urn:countryCode>COUNTRY</urn:countryCode>
<urn:vatNumber>VATNUMBER</urn:vatNumber>
</urn:checkVat>
</soapenv:Body>
</soapenv:Envelope>";
request = request.Replace("COUNTRY", countryCode);
request = request.Replace("VATNUMBER", theRest);
String response;
try
{
response = wc.UploadString("http://ec.europa.eu/taxation_customs/vies/services/checkVatService", request);
}
catch
{
// service throws WebException e.g. when non-EU VAT is supplied
}
var isValid = response.Contains("<valid>true</valid>");
Here is a self-sufficient (no WCF, no WSDL, ...) utility class that will check the VAT number and get information on the company (name and address). It will return null if the VAT number is invalid or if any error occurred.
Update 2022/10/17 : I have added namespace handling to parse the response as it seems the EU web service was modified. If you need the old version, just look at the history
// sample calling code
Console.WriteLine(EuropeanVatInformation.Get("FR89831948815"));
...
public class EuropeanVatInformation
{
private EuropeanVatInformation() { }
public string CountryCode { get; private set; }
public string VatNumber { get; private set; }
public string Address { get; private set; }
public string Name { get; private set; }
public override string ToString() => CountryCode + " " + VatNumber + ": " + Name + ", " + Address.Replace("\n", ", ");
public static EuropeanVatInformation Get(string countryCodeAndVatNumber)
{
if (countryCodeAndVatNumber == null)
throw new ArgumentNullException(nameof(countryCodeAndVatNumber));
if (countryCodeAndVatNumber.Length < 3)
return null;
return Get(countryCodeAndVatNumber.Substring(0, 2), countryCodeAndVatNumber.Substring(2));
}
public static EuropeanVatInformation Get(string countryCode, string vatNumber)
{
if (countryCode == null)
throw new ArgumentNullException(nameof(countryCode));
if (vatNumber == null)
throw new ArgumentNullException(nameof(vatNumber));
countryCode = countryCode.Trim();
vatNumber = vatNumber.Trim().Replace(" ", string.Empty);
const string ns = "urn:ec.europa.eu:taxud:vies:services:checkVat:types";
const string url = "http://ec.europa.eu/taxation_customs/vies/services/checkVatService";
const string xml = #"<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'><s:Body><checkVat xmlns='" + ns + "'><countryCode>{0}</countryCode><vatNumber>{1}</vatNumber></checkVat></s:Body></s:Envelope>";
try
{
using (var client = new WebClient())
{
var doc = new XmlDocument();
doc.LoadXml(client.UploadString(url, string.Format(xml, countryCode, vatNumber)));
var response = doc.SelectSingleNode("//*[local-name()='checkVatResponse']") as XmlElement;
if (response == null || response["valid", ns]?.InnerText != "true")
return null;
var info = new EuropeanVatInformation
{
CountryCode = response["countryCode", ns].InnerText,
VatNumber = response["vatNumber", ns].InnerText,
Name = response["name", ns]?.InnerText,
Address = response["address", ns]?.InnerText
};
return info;
}
}
catch
{
return null;
}
}
}
On .NET platform it is common to consume a Web service so that we generate a proxy class. This can usually be done using Visual Studio "Add Web Reference" where you just fill in a path to the WSDL. An alternative is to generate source classes using wsdl.exe or svcutil.exe.
Then just consume this class and verifying VAT becomes a one-liner:
DateTime date = new checkVatPortTypeClient().checkVat(ref countryCode, ref vatNumber, out isValid, out name, out address);
Generating proxy provides strongly typed API to consume whole service and we don't need to manually create soap envelope and parse output text. It is much more easier, safer and generic solution than yours.
Updated: I've published this as a NuGet library.
https://github.com/TriggerMe/CSharpVatChecker
var vatQuery = new VATQuery();
var vatResult = await vatQuery.CheckVATNumberAsync("IE", "3041081MH"); // The Squarespace VAT Number
Console.WriteLine(vatResult.Valid); // Is the VAT Number valid?
Console.WriteLine(vatResult.Name); // Name of the organisation
Based on Pavel Hodek's:
Make sure you have the Microsoft WCF Web Service Reference
Provide extension installed for visual studio (I'm using VS 2017
Community).
In the solution explorer right click on Connected Services > Add Connected Service
Select the WCF extension.
Type in the URL provided by VIES
http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl to generate the Service class from the wsdl.
Press Go and select the service, give a proper name for the namespace e.g. Services.VATCheck
Press Finish, which will create a new folder and a file called reference.cs in the Connected Services rename the file to VATCheck which will also rename the Class.
In a controller use the following code to invoke the call, make sure it's async (it can take awhile to eventually load all the data)
public async Task<IActionResult> CheckVAT()
{
var countryCode = "BE";
var vatNumber = "123456789";
try
{
checkVatPortType test = new checkVatPortTypeClient(checkVatPortTypeClient.EndpointConfiguration.checkVatPort, "http://ec.europa.eu/taxation_customs/vies/services/checkVatService");
checkVatResponse response = await test.checkVatAsync(new checkVatRequest { countryCode = countryCode, vatNumber = vatNumber });
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return Ok();
}
Please note that you can clean-up this call, but that's totally up to you.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BTWCheck.eu.europa.ec;
namespace BTWCheck
{
class Program
{
static void Main(string[] args)
{
// VS 2017
// add service reference -> button "Advanced" -> button "Add Web Reference" ->
// URL = http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
string Landcode = "NL";
string BTWNummer = "820471616B01"; // VAT nr BOL.COM
checkVatService test = new checkVatService();
test.checkVat(ref Landcode, ref BTWNummer, out bool GeldigBTWNr, out string Naam, out string Adres);
Console.WriteLine(Landcode + BTWNummer + " " + GeldigBTWNr);
Console.WriteLine(Naam+Adres);
Console.ReadKey();
}
}
}

How do I read the Common Name from the client certificate?

Our application needs a piece of data that it is included in the client cert's common name. Currently, I'm trying to get it from HttpContext.Current.Request.ClientCertificate. How do I read this out? Sadly, I'm trying to code this blind while I figure out why SoapUI isn't sending the cert, so I haven't tried much other than reading about the object on MSDN and poking through the empty properties but I'm not sure what I'm looking for. So to recap, what do I need to do to pull out the common name from this cert? TIA
I am maybe too late to answer your question but i hope this would help others who are looking for the way to get common name from certificate.
If you use 'Subject', you might need to trim away other unnecessary information.
For example, CN = localhost,OU = DepartmentName,O = CompanyName,L = Location,S = State,C = Country
Dim store As New X509Store(StoreName.My, StoreLocation.LocalMachine)
store.Open(OpenFlags.ReadOnly)
store.Certificates(0).Subject
But if you use the code below, you will get 'localhost' which directly give you the common name of the certificate.
Dim store As New X509Store(StoreName.My, StoreLocation.LocalMachine)
store.Open(OpenFlags.ReadOnly)
store.Certificates(0).GetNameInfo(X509NameType.SimpleName, False)
Here's the link for reference:-
https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.getnameinfo(v=vs.110).aspx
I know a tiny bit about certificates. Here was my workflow:
I started at:
HttpRequest.ClientCertificate
Which lead me to:
HttpClientCertificate (as the return type).
It appears to have a few properties, but none that explicitly are named common name.
Googled: HttpClientCertificate Common Name:
Problem with extracting X509 certificate from Context on web service side
Which had some code:
//extracting Common name from certificate
Subject = cert.Subject.ToString();
Then went to:
HttpClientCertificate.Subject
which remarks:
If String is specified without a subfield, the HttpClientCertificate collection returns a comma-separated list of subfields. For example, C=US, O= Msft.
And with the extreme limited knowledge I do have, I know the Common Name = is in this list. I have no actual way to test this at the moment, but it shouldn't be hard to parse this value for the name you are looking for.
It's good question (+1), I am happy you asked it as it will probably be useful for future readers.
I created a DotNetFiddle Example and even though it uses HttpWebRequest to get an X509Certificate class, it does have a Subject property that returned the following value for https on www.google.com:
CN=www.google.com, O=Google Inc, L=Mountain View, S=California, C=US
So I'm inclined to believe that Subject on the HttpClientCertificate would be the same values (knowing that CN means CommonName).
Just a oneliner in Linq.
var kvs = cert.Subject.Split(',').Select(x => new KeyValuePair<string, string>(x.Split('=')[0], x.Split('=')[1])).ToList();
Returns a generic list. Dont use dictionary here, because Subject can contain duplicate fields.
You may have to adjust due to differences in certificate formats.
Here is some code to do this:
HttpClientCertificate theHttpCertificate = HttpContext.Current.Request.ClientCertificate;
// split the subject into its parts
string[] subjectArray = theHttpCertificate.Subject.Split(',');
string[] nameParts;
string CN = string.Empty;
string firstName = string.Empty;
string lastName = string.Empty;
foreach (string item in subjectArray)
{
string[] oneItem = item.Split('=');
// Split the Subject CN information
if (oneItem[0].Trim() == "CN")
{
CN = oneItem[1];
if (CN.IndexOf(".") > 0)
{// Split the name information
nameParts = CN.Split('.');
lastName = nameParts[0];
firstName = nameParts[1];
}
}
}
The best thing to do is to use the build-in GetNameInfo methods using the name type and the boolean flag
Lets assume you use this extension method:
[return:MaybeNull]
public static X509Certificate2? GetCodeSignCertificate(this Assembly asm)
{
if (asm is null)
{
throw new ArgumentNullException(nameof(asm));
}
if (!File.Exists(asm.Location))
{
return null;
}
using var cert=System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromSignedFile(asm.Location);
if (cert is null)
return null;
return new X509Certificate2(cert);
}
You can then use a type in the class to get the certificate like so:
[TestMethod()]
public void TryGetCodeSigning()
{
//var item = new Walter.CallStack();
var item = new List<string>();
using var cert = item.GetType().Assembly.GetCodeSignCertificate();
Assert.IsNotNull(cert.GetNameInfo(X509NameType.SimpleName,true));
}
to get the name of the certificate signing authority
cert.GetNameInfo(X509NameType.SimpleName,true)
to get the name for who the certificate was signed
cert.GetNameInfo(X509NameType.SimpleName,false)
Have a look at X509NameType and look if this works for you.
I created this extension to handle all individual elements of a subject name.
public enum SubjectNameElements {CN,O,L,OU,S,C,}
public static readonly Dictionary<SubjectNameElements, string> SubNameSybms =
new Dictionary<SubjectNameElements, string>{
{ SubjectNameElements.CN,"CN="},
{ SubjectNameElements.O ,"O="},
{ SubjectNameElements.L ,"L="},
{ SubjectNameElements.OU,"OU="},
{ SubjectNameElements.S ,"S="},
{ SubjectNameElements.C ,"C="},
};
/// <summary>
/// Retrieve CN from subject Name of a certificate
/// </summary>
/// <param name="subjName"></param>
/// <param name="symb"></param>
/// <remarks>
/// Example:
/// GetOneElementFromSubjectName("C=IT, S=Italy, L=Torino, O=Example Italy S.p.A., OU=Example-Client, CN=www.google.com",SubjectNameElements.CN) => www.google.com
/// </remarks>
/// <returns> a string value or empty string in case of invalid options </returns>
public static string GetOneElementFromSubjectName(this X500DistinguishedName subjName, SubjectNameElements symb=SubjectNameElements.CN)
{
string subjNameString = subjName.Name;
try
{
string Symb = SubNameSybms[symb];
string CName = subjNameString.Substring(subjNameString.IndexOf(Symb)).Split(",").First().Replace(Symb, string.Empty).Trim();
return CName;
}
catch (Exception ex)
{
Log.Error("Error in GetOneElementFromSubjectName. Ex.Message: '" + ex.Message + "'. Ex.StackTrace: '" + ex.StackTrace + "'");
return string.Empty;
}
}

Categories