Azure KeyVault how to load X509Certificate? [duplicate] - x509certificate

This question already has answers here:
How to serialize and deserialize a PFX certificate in Azure Key Vault?
(5 answers)
Closed 6 years ago.
I uploaded a Certificate to Azure KeyVault and obtained "all" access to it using an application registered into the Active Directory. That all works fine. Now I need to load the obtained key into an X509Certificate to be able to use it as a client certificate for calling a 3rdparty legacy SOAP webservice. As far as I know I can only use a X509Certificate to call that webservice.
It does not matter if I upload it as a Key or Secret? I have tried both.
var clientId = "...."
var clientSecret = "...."
..
var token = authenticationContext.GetAccessToken(resource, adCredential);
var keyClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(token));
KeyBundle key = keyClient.GetKeyAsync("https://mycertifcates.vault.azure.net/keys/MyCertificate/123456789");
So far so good, I got a KeyBundle as a result and it seems like I can convert it to a Base64String, but whatever I try I end up with exceptions :(
1st attempt I tried:
var publicKey = Convert.ToBase64String(key.Key.N);
var cert = X509Certificate.FromBase64String(publicKey);
System.Security.Cryptography.CryptographicException {"Cannot find the requested object.\r\n"}
Ahhhh
2nd attempt, loading it into an RSACryptoServiceProvider, but what to do with that? Results into the same exception and I'm not even able to get the private key if I want to
var rsaCryptoProvider = new RSACryptoServiceProvider();
var rsaParameters = new RSAParameters()
{
Modulus = key.Key.N,
Exponent = key.Key.E
};
rsaCryptoProvider.ImportParameters(rsaParameters);
var cspBlob = rsaCryptoProvider.ExportCspBlob(false);
// what to do with the cspBlob ?
var publicKey = Convert.ToBase64String(cspBlob);
No private key as well, the public key is different. Ofcourse this does not work either.
3rd attempt
I uploaded it as a Secret Certificate ContentType using the management portal.
var secret = helper.GetSecret("https://myCertificate.vault.azure.net/secrets/MyCertificate/1234567890");
var value = secret.Value;
// Now I got the secret.. works flawless
// But it crash with the same exception at .Import
var exportedCertCollection = new X509Certificate2Collection();
exportedCertCollection.Import(Convert.FromBase64String(value));
var cert2 = exportedCertCollection.Cast<X509Certificate2>().Single(s => s.HasPrivateKey);
System.Security.Cryptography.CryptographicException {"Cannot find the
requested object.\r\n"}
Any suggestion is welcome.
I need the pfx including private key, looking at the tracelog
ystem.Net Information: 0 : [37880] SecureChannel#23107755 - Left with 1 client certificates to choose from.
System.Net Information: 0 : [37880] SecureChannel#23107755 - Trying to find a matching certificate in the certificate store.
System.Net Information: 0 : [37880] SecureChannel#23107755 - Locating the private key for the certificate: [Subject]

To answer my own question (I asked it too quickly I guess)
It turns out that this question was in fact a duplicate, but I did not understand it at first. The fact is that the Azure Management Portal is limited. For now uploading a certificate can only be done by using a powershell (or other code for that matter).
https://stackoverflow.com/a/34186811/578552
The 3rd attempt code works fine for the X509Certificate2
var secret = helper.GetSecret("https://myCertificate.vault.azure.net/secrets/MyCertificate/1234567890");
var exportedCertCollection = new X509Certificate2Collection();
exportedCertCollection.Import(Convert.FromBase64String(secret.Value));
var cert2 = exportedCertCollection.Cast<X509Certificate2>().Single(s => s.HasPrivateKey);

Related

Simple Odata Client to consume Odata with Authentication not working

I m new to Simple.Odata.client. I had a problem to access the Odata Service with below code. The below code return null. but Postman return with result.
suspected Problem : How to pass a url string with '1000' &format=json
Is the below Simple odata client setup correctly?
There is no UrlBase in Simple Odata client, but there is BAseUri
Is this ODataClientSettings working??
var settings = new Simple.OData.Client.ODataClientSettings();
settings.BaseUri = new Uri("https://..../UoM?$filter=wer eg '1000' &format=json");
settings.Credentials = new NetworkCredential("user1", "usrpwd");
var client = new ODataClient(settings);
please help
Thanks
This worked for me
var credentials = new NetworkCredential(userName, password); //you can use the override with the domain too.
var settings = new ODataClientSettings(baseUrl, credentials) //baseUrl is a string.
{
IgnoreResourceNotFoundException = true,
OnTrace = (x, y) => Debug.WriteLine(x, y),
PayloadFormat = ODataPayloadFormat.Json, //here is where you specify the format
IgnoreUnmappedProperties = true,
RenewHttpConnection = true,
TraceFilter = ODataTrace.All,
PreferredUpdateMethod = ODataUpdateMethod.Merge
};
var client = new ODataClient(settings);
Your baseUrl should not contain all those OData tags but the endpoint of your service like https://myservice.mysite.com/api.svc. Then as you use the Simple.OData.Client the resource url will be automatically completed.
Please, take a look at the OData standard to figure out how it works and see the Simple.OData.Client repo's examples to better understand how to use it.
To better understand how to use the Windows Authentication you can check Authentication and Authorization with Windows Accounts and how to access website with Windows credential
Hope this help.

ASP.NET WCF REST Linq-to-SQL POST/WebInvoke JSON, Object with EntitySet generated by Linq-to-SQL

So my problem is little bit complicated.
Main goal: register account (class / table) that contains list of Players (class / table) from a web application into a SQL Server database through a WCF service.
So for creating the classes I used Linq-to-SQL which created the Account and Player class. The Player has a foreign key AccountEmail to the Account table. Due to that the class Account has EntitySet<Player> _Players;.
Now the web application has reference to this service and when user finish registration I am making an POST request with WebClient and DataContractJsonSerializer to the service .
Unfortunately the service or the http protocol cannot understand the request :
System.Net.WebException: The remote server returned an error: (400)
Full error response from server
https://s31.postimg.org/x6b27uqqj/errorrr.png
The fail is on service side for some reason it doesn't know how to read the json player DB.designer.cs:line 295 at ReadPlayerFromJson(XmlReaderDelegator
Service:
[OperationContract]
[WebInvoke(UriTemplate = "/Register",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, Method = "POST")]
void RegisterAccount(Account account);
Client side :
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Account));
MemoryStream mem = new MemoryStream();
Account a = new Account { EMAIL = Email.Text, PASSWORD = Password.Text, NAME = nickname.Text};
a.Players = accountPLayers.ToArray();
ser.WriteObject(mem, a);
string data = Encoding.UTF8.GetString(mem.ToArray(), 0, (int)mem.Length);
WebClient webClient = new WebClient();
webClient.Headers["Content-type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
webClient.UploadString(WEB_SITE_URL+ "/Register", "POST", data);
When sending Account without the list of players, the operation succeeds:
{"EMAIL":"test#gmail.com","NAME":"1","PASSWORD":"test","Players":null}
With the list of players the operation fails :
{"EMAIL":"test#gmail.com","NAME":"1","PASSWORD":"test","Players":[{"Account":null,"AccountEmail":"test#gmail.com","FirstName":"test","Id":0,"LastName":"test","Type":-1}]}
Questions:
I guess that REST service expecting to get only Account and doesn't know what is the list of players? I have to define that somehow.
Why in the service does the Account have EntitySet<Player> _Players;? And in the client after adding reference to service it is an array Player[] ?
Why does Linq-to-SQL add field Account to player? What should it contain? As you can see this field is null in the json.
Is complex object/known types has to do something with my problem ?
Please help me to solve this issue, thanks!
Solved !!!
So if any one ever will get this problem ,
The thing is that player has foreign key of EMAIL column to Account Email .
When you create your JSON DO NOT PUT EMAIL INSIDE EACH PLAYER
LINQTOSQL knows the association and will add it automatically when parsing the JSON .
So my JSON Sent to the service looks like this :
{"EMAIL":"test#gmail.com","NAME":"test","PASSWORD":"test#A","Players":[{"Account":null,"AccountEmail":null,"FirstName":"wow","Id":0,"LastName":"koko","Type":null}]}.

How x509chain works in C#? What is purpose of using it?

I have implement client certificate based security in C#. Everything is clear and working good for me. I was finding perfect way to verify client certificate on server side, through which i came to x509Chain. But i am not clear about How it exactly works? How can we configure it? Is it secure way to verify certificate?
Any help will be Appreciated! Thanks!
Note: By theoretical point of view, i have read my documents on that but i am not sure for it's functionality. Please provide practical example and guide for that.
A bit of theory I wrote some time ago: http://social.technet.microsoft.com/wiki/contents/articles/3147.certificate-chaining-engine-cce.aspx
This article describes what is certificate chaining engine (CCE) and how it works in Windows in general. It is based on RFC5280 and Microsoft-specific implementation of certificate chaining engine. .NET uses native CryptoAPI functions, so X509Chain behaves in the same way as in native CryptoAPI.
The X509Chain does not work reliably for scenarios where you do not have the root certificate in the trusted CA store on the machine.
Others will advocate using bouncy castle. I wanted to avoid bringing in another library just for this task, so I wrote my own.
As see in RFC3280 Section 4.1 the certificate is a ASN1 encoded structure, and at it's base level is comprised of only 3 elements.
The "TBS" (to be signed) certificate
The signature algorithm
and the signature value
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
C# actually has a handy tool for parsing ASN1, the System.Formats.Asn1.AsnDecoder.
Using this, we can extract these 3 elements from the certificate to verify the chain.
The first step was extracting the certificate signature, since the X509Certificate2 class does not expose this information and it is necessary for the purpose of certificate validation.
Example code to extract the signature value part:
public static byte[] Signature(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
var offsetSpan = certificateSpan[(tbsOffset + tbsLength)..];
AsnDecoder.ReadSequence(
offsetSpan,
encodingRules,
out var algOffset,
out var algLength,
out _
);
return AsnDecoder.ReadBitString(
offsetSpan[(algOffset + algLength)..],
encodingRules,
out _,
out _
);
}
The next step is to extract the TBS certificate. This is the original data which was signed.
example code to extract the TBS certificate data:
public static ReadOnlySpan<byte> TbsCertificate(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
// include ASN1 4 byte header to get WHOLE TBS Cert
return certificateSpan.Slice(tbsOffset - 4, tbsLength + 4);
}
You may notice that when extracting the TBS certiifcate I needed to include the ASN1 header in the data, this is because the signature of the TBS Certificate INCLUDES this data (this annoyed me for a while).
For the first time in history, the Microsoft does not impede us with their API design, and we are able to obtain the Signature Algorithm directly from the X509Certificate2 object. Then we just need to decide to what extend we are going to implement different hash algorithms.
var signature = signed.Signature();
var tbs = signed.TbsCertificate();
var alg = signed.SignatureAlgorithm;
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad
switch (alg)
{
case { Value: var value } when value?.StartsWith("1.2.840.113549.1.1.") ?? false:
return signedBy.GetRSAPublicKey()?.VerifyData(
tbs,
signature,
value switch {
"1.2.840.113549.1.1.11" => HashAlgorithmName.SHA256,
"1.2.840.113549.1.1.12" => HashAlgorithmName.SHA384,
"1.2.840.113549.1.1.13" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
RSASignaturePadding.Pkcs1
) ?? false;
case { Value: var value } when value?.StartsWith("1.2.840.10045.4.3.") ?? false:
return signedBy.GetECDsaPublicKey()?.VerifyData(
tbs,
signature,
value switch
{
"1.2.840.10045.4.3.2" => HashAlgorithmName.SHA256,
"1.2.840.10045.4.3.3" => HashAlgorithmName.SHA384,
"1.2.840.10045.4.3.4" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
DSASignatureFormat.Rfc3279DerSequence
) ?? false;
default: throw new UnsupportedSignatureAlgorithm(alg);
}
As shown in the code above, https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad is a good resource to see the mapping of algorithms and OIDs.
Another thing you should be aware of is that there are some articles out there that claim that for elliptical curve algorithms, microsoft expects a R,S formatted key instead of a DER formatted key. I tried to convert the key to this format but it ultimately didn't work. What I discovered was that it was necessary to use the DSASignatureFormat.Rfc3279DerSequence parameter.
Additional certificate checks, like "not before" and "not after", or CRL and OCSP checks can be done in addition to the chain verification.

.Net Support of SHA256withRSA

I need to support the following signature:
Sign the UTF-8 representation of the input using SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function) with the private key obtained from the Google Developers Console. The output will be a byte array.
The following code fails, with "Invalid algorithem specified". Is this a limitation of .NET? Here is a snippet of my code:
var rsa2 = new RSAPKCS1SignatureFormatter(rsa);
rsa2.SetHashAlgorithm("SHA256");
bytes = rsa2.CreateSignature(bytes);
The above requirement is from computing the signature for a Server to Server Applications for Google API.
https://developers.google.com/accounts/docs/OAuth2ServiceAccount#computingsignature
Thanks for any help.
Karl..
Here is the code that signs the JWT. I've removed the use of RSAPKC1SingatureFormatter class and using another HASHCompute method in an effort to get something to work (still not working)
I'm not sure this is correct and unfortunately my response from the Rest service is always the same "Invalid Grant" so hard to tell.
public string Generate(string HeadJWT, string ContentJWT, X509Certificate2 certificate)
{
var bytes = Utility.getBytes(HeadJWT);
var base64Head = Utility.Base64UrlEncode(bytes);
// base64 Url Encode Payload (Json Content)
bytes = Utility.getBytes(ContentJWT);
var base64Payload = Utility.Base64UrlEncode(bytes);
var secureInputValue = String.Format("{0}.{1}", base64Head, base64Payload);
bytes = Stub.Jwt.Utility.getBytes(secureInputValue);
bytes = Stub.Jwt.Utility.ComputeHMACSha265(bytes, certificate.PublicKey.EncodedKeyValue.RawData);
_signature = Stub.Jwt.Utility.Base64UrlEncode(bytes);
return String.Format("{0}.{1}.{2}", base64Head, base64Payload, _signature);
}
This cannot be a limitation of .NET in general, as the example Microsoft code seems to use "SHA256" itself. But it could be a limitation of your particular runtime.

Get ALL Mailboxes via EWS (Exchange WebServices) - not my own but also shared and group mailboxes

Can anyone provide me with a .NET (C# / VB) sample of how to get all mailboxes that I have access to ?
I have only been able to get my OWN mailbox via EWS - not ALL THE OTHER mailboxes that I also have access to through Outlook.
I don't have the names nor the id's of these mailboxes but isn't it possible to retrieve ALL mailboxes - including my own - that I am allowed to see - just as I can in Outlook ?
I am using Autodiscover to get my mailbox like this: service.AutodiscoverUrl("xxxx#ee.dd") - this will perhaps only get my own mailbox and not all the rest?
Please help !?
The way I got around this was to define the group mailbox in question as a "mailbox" object and then obtain the FolderID for the particular folder.
Define mailbox object
Mailbox gpmailbox = new Mailbox("mailbox#yourdomainname.com");
Get the FolderID (in this case, the Inbox)
FolderId gpInbox = new FolderId(WellKnownFolderName.Inbox, gpmailbox);
Use FolderID in your normal code (in this case I'm obtaining 100 messages)
ItemView view = new ItemView(100);
FindItemsResults<Item> results = hookToServer.FindItems(new FolderId(WellKnownFolderName.Inbox, gpmailbox), view);
The key is to grab the FolderID of the folder you need. Hope this helps.
Edit: I also failed to note that my object "hookToServer" is simply the ExchangeService object. Here's how I defined it:
ExchangeService hookToServer = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
hookToServer.UseDefaultCredentials = true;
hookToServer.Url = new Uri("TheExchangeServer")
I also used this as reference:
EWS 2007 Group Mailbox Guide
You can do this by Using Autodiscover to get user settings, this is a completely separate service to the one with the AutodiscoverUrl method.
The setting name you need is AlternateMailboxes, this will give a collection of all the 'other' mailboxes you have access to. You might then add the user's default mailbox to get a complete list.
In c#:
using Microsoft.Exchange.WebServices.Autodiscover; // from nuget package "Microsoft.Exchange.WebServices"
internal List<string> GetAccessibleMailboxes()
{
AutodiscoverService autodiscoverService = new AutodiscoverService("outlook.office365.com");
autodiscoverService.Credentials = networkCredential;
var userSmtpAddress = networkCredential.UserName;
GetUserSettingsResponse userresponse = autodiscoverService.GetUserSettings(
userSmtpAddress,
UserSettingName.AlternateMailboxes);
var alternateMailboxCollection = (AlternateMailboxCollection)userresponse.Settings[UserSettingName.AlternateMailboxes];
var smtpAddressList = alternateMailboxCollection.Entries.ToList().Select(a => a.SmtpAddress).ToList();
smtpAddressList.Add(userSmtpAddress);
return smtpAddressList;
}

Resources