Did New-AzureADApplicationKeyCredential double base64 encode for CustomKeyIdentifier and Value? - c#

I am using New-AzureADApplicationKeyCredential to create a KeyCredential for an application. document
At first, I base64 encode the Thumbprint
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("C:\Users\PFuller\Desktop\abc.cer")
...
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
...
New-AzureADApplicationKeyCredential -ObjectId <id>
-CustomKeyIdentifier $base64Thumbprint
-Type AsymmetricX509Cert
-Usage Verify -Value $base64Value
But the result on AAD manifest is double base64 encoded:
"keyCredentials": [
{
"customKeyIdentifier": "base64(base64Thumbprint)",
...
}
],
According to Microsoft identity platform application authentication certificate credentials
The customKeyIdentifier should only base64 encoded once and store on manifest's keyCredentials.
Did I misuse this cmd or something wrong here? becase after I turned the manifest to base64 encoded once, every thing works properly.
Thanks for help.

Based on my test, both customKeyIdentifier and value are double base64 encoded when we create a KeyCredential for an application using New-AzureADApplicationKeyCredential.
After that I use the following code to get the access token with this cert:
var app = ConfidentialClientApplicationBuilder.Create("{clientId}")
.WithAuthority(AzureCloudInstance.AzurePublic, "{tenantId}")
.WithCertificate(cer)
.Build();
var result = await app.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" }).ExecuteAsync();
Console.WriteLine(result.AccessToken);
I find that we can successfully get the access token with correct roles.
So I think New-AzureADApplicationKeyCredential base64 encodes customKeyIdentifier and value for another time. But it also handles them correctly when we use the X509Certificate to get an access token.

Related

Verifying a RSA signature made by Crypto Node.JS in C#

I'm trying to build a web service using Express/NodeJS which signs a piece of information. The signed data is received and verified by a client written in C#. You'll have to forgive my inexperience in cryptography and its associated technologies.
First off, I generate a certificate for the C# client and a private key for the NodeJS application using OpenSSL;
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days
365
In the NodeJS application, I have the following code;
const crypto = require('crypto')
const fs = require('fs')
var pem = fs.readFileSync('./keys/key.pem');
var key = pem.toString('ascii');
var privateKey = crypto.createPrivateKey({
'key': key,
'format': 'pem',
'passphrase': '<PASSPHRASE>',
});
function sign(identifier){
var sign = crypto.createSign('RSA-SHA256');
sign.update(identifier);
var sig = sign.sign(privateKey, 'base64');
return sig;
}
exports.sign = sign;
In this case, the parameter identifier is the data to be signed. The client will receive this, and the signature generated, sig.
In the C# client I have the following snippet;
X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(pub));
using (var sha256 = SHA256.Create())
{
using (var rsa = cert.GetRSAPublicKey())
{
bool results = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(results.ToString());
}
}
The pub is the generated certificate in Base64, it is stored in a const string. The data contains the same information as identifier in the NodeJS application, but it's converted to bytes using Convert.FromBase64String(...), and likewise the signature is the data returned from sig in the NodeJS application, only converted from Base64 to byte data.
When all information is inserted, VerifyData() returns false, this leads me to believe that there's some kind of missmatch between the cryptographic configurations of the web service and the client.
Any ideas?
As pointed out in the comments, the problem was that data in the C# client was converted to from Base64 when the data in the NodeJS application read from UTF-8.
The solution was to convert the string using Encoding.UTF8.GetBytes()
Thanks for the quick response!

How to sign using new keyvault client from azure.security.keyvault

I recently started to replace the old Azure KeyVault client from the Microsoft.Azure.KeyVaultnamespace with the newer one in Azure.Security.KeyVault.
This works without any issues when getting secrets and certificates, but I am not sure how to sign anymore
How do I sign using the new keyvault client?
Did you check Azure SDK Azure.Security.KeyVault.Keys package on github?
Example code from that source:
SignResult rsaSignDataResult = rsaCryptoClient.SignData(SignatureAlgorithm.RS256, data);
Debug.WriteLine($"Signed data using the algorithm {rsaSignDataResult.Algorithm}, with key {rsaSignDataResult.KeyId}. The resulting signature is {Convert.ToBase64String(rsaSignDataResult.Signature)}");
For azure key vault, we need to create a signature from a digest using the specified key.
So, you could refer to the below code to sign some arbitrary data and verify the signatures using the CryptographyClient with both the EC and RSA keys we created.
byte[] digest = null;
using (HashAlgorithm hashAlgo = SHA256.Create())
{
digest = hashAlgo.ComputeHash(data);
}
SignResult rsaSignResult = rsaCryptoClient.Sign(SignatureAlgorithm.RS256, digest);
If you want to sign certificate you could refer to this article.

Trouble authenticating with SshUserKeyCredentials in libgit2sharp-SSH

Having some trouble authenticating with SshUserKeyCredentials using libgit2sharp-SSH:
var co = new CloneOptions();
co.CredentialsProvider = (_url, _user, _cred) => new SshUserKeyCredentials { PrivateKey="C:\\path\\to\\private_key" };
Repository.Clone("git#... .repository.git", path, co);
I found the SshUserKeyCredentials object browsing through the source code so my first question would be if it is possible to use this object to do deploy key based checkout from gitlab?
The object seems to want any combination of PrivateKey, Username, PublicKey and Passphrase. I'm currently using a PrivateKey.
The error I end up with:
{"Failed to start SSH session: Unable to exchange encryption keys"}
If this way isn't supposed to work is there an alternative way of using deploy keys to programmatically manage git from an C# environment?
I was able to figure out the following through trial-and-error and scouring the web.
You cannot have null for any field; use string.Empty.
Public and private key must be provided and in the proper format.
Private key had to be in PEM format (either use PuttyGen Conversion menu => Export OpenSSH or use openssl rsa -in id_rsa -out id_rsa.pem).
Public key had to be single line, starting with type, followed by base64 key, no comment at the end (this is the format shown in the public key text box on PuttyGen, except you have to remove the comment), e.g.
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAoblahblahblahblahblah
I added username git because that's what Bitbucket requires. Not sure you need that, but it can't be null, do string.Empty.
Example:
CredentialsHandler handler = (_url, _user, _cred) => new SshUserKeyCredentials
{
PrivateKey = #"C:\Users\blah\.ssh\keys\bitbucket.pem",
Username = "git",
Passphrase = string.Empty,
PublicKey = #"C:\Users\blah\.ssh\keys\bitbucket.pub"
}
See also:
PHP ssh2_auth_pubkey_file(): Authentication failed using public key: Invalid key data, not base64 encoded

Parse PKCS #7 SSL Certificate Chain (.p7b) without private key?

I have a PKCS #7, signed, .p7b file which contains an X509 SSL certificate and the intermediate and root CA certs it was signed with. I need to use C# to parse the .p7b file, pull out the SSL certificate, and pull some values off of it (expiry date, DN, etc).
I've tried reading it as an X509 certificate like so:
//certContent is a byte array with the p7b file contents
X509Certificate2 cert = new X509Certificate2(certContent);
That works fine with a regular .cer certificate, but throws a CryptographicException when used with a .p7b certificate. This is because the .p7b contains the entire certificate chain.
I've also tried parsing it as a SignedCms object, then iterating through the certificate chain and pulling out my SSL certificate:
SignedCms certContainer = new SignedCms();
certContainer.Decode(certContent);
foreach(X509Certificate2 cert in certConatiner.Certificates)
{
...
}
However that throws an exception on Decode saying ASN1 bad tag value met. After some searching, I believe that is because I do not have the private key which was used to create the certificate and/or sign the certificate.
Does anyone know how I can parse this .p7b certificate chain using C#?
Well, I'm an idiot. I opened up the .p7b file and realized it was just base64 on the inside. I pulled out the base64, decoded that, then parsed that as a signed CMS and all is well.
String content = Encoding.UTF8.GetString(certContent);
String base64Content = content.Replace("-----BEGIN CERTIFICATE-----", "").Replace("-----END CERTIFICATE-----", "").Replace("\r", "").Replace("\n", "");
byte[] decodedContent = Convert.FromBase64String(base64Content);
SignedCms certContainer = new SignedCms();
certContainer.Decode(decodedContent);

Loading the Jira Public Certificate in .Net from a string (how to convert ASN.1 encoded SubjectPublicKeyInfo to X509 Cert in .Net)

I am building an oauth 1.0a service that will be consumed by a gadget within Jira, it's a .Net 3.5 Application written in C#.
Jira makes requests to this service using the RSA-SHA1 signature method, which means to verify the signature of the request I need create an X509Certificate instance form their public cert.
Within the Jira application you can get the public cert by going to the consumer info screen (which also has the consumer key for Jira etc.) and it presents the public key in this format:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCObJRTGSZbAo
jRkvKmm0cwFXnKcPMfR4t/sghvLe/+QVs6TJOz5cUh5UokSqyz
VeMsL0jomP18ZcR3SPcIFT7xtOGQjLwLk7ghfYSsxjTGs9VxsC
/PQk5OQRP3v43IxFNF3M2SYhFWJZTOnqrab5AsMh2Kxdv+D69D
CINXCu5ltQIDAQAB
Looking at the Jira code which generates this key I can see it's (supposedly) PEM encoded without the BEGIN/END certificate header/footer.
RSAKeys.toPemEncoding(consumer.getPublicKey())
RSAKeys is an open source class found here:
https://studio.atlassian.com/source/browse/OAUTH/trunk/api/src/main/java/com/atlassian/oauth/util/RSAKeys.java?r=HEAD
I wish to load this public cert (key) into an X509Certificate instance within .Net, but my attempts so far have failed. Here's the code I have:
static readonly Regex stripRegex = new Regex("-----[A-Z ]*-----");
public string ConvertFromOpenSsl(string key)
{
return stripRegex.Replace(key, "").Replace("\r", "").Replace("\n", "");
}
public X509Certificate2 GetConsumerCertificate(IConsumer consumer)
{
string cert =
#"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCObJRTGSZbAo
jRkvKmm0cwFXnKcPMfR4t/sghvLe/+QVs6TJOz5cUh5UokSqyz
VeMsL0jomP18ZcR3SPcIFT7xtOGQjLwLk7ghfYSsxjTGs9VxsC
/PQk5OQRP3v43IxFNF3M2SYhFWJZTOnqrab5AsMh2Kxdv+D69D
CINXCu5ltQIDAQAB";
string converted = ConvertFromOpenSsl(cert);
var bytes = Convert.FromBase64String(converted);
var cert = new X509Certificate2(bytes); // throws here
But on the last line of code I have an exception thrown:
System.Security.Cryptography.CryptographicException: Cannot find the requested object.
at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._QueryCertBlobType(Byte[] rawData)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] data)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData)
I'm pretty sure I am missing something elementary, but I can think what it is.
UPDATE
OK, on further investigation it appears that this is a SubjectPublicKeyInfo serialization of the public key, so it's ASN.1, base 64 encoded (162 bytes unencoded), which is the default output from Java using java.security.PublicKey.getEncoded().
So given all that - is there any easy way to create an X509Certificate2 instance wrapping this public key - or is additional metadata required beyond the public key to create an x509Certificate2 instance?
Jira should provide you with a Certificate (not just a public key).
Typically the Java world will give a base64 encoded or PEM certificate. X509Certificate2 from .Net can automatically .Load a base64, PEM or binary certificate.
you can generate your XML RSA certificate via .NET using RSACryptoServiceProvider. This will give you XML (FromXmlString method), the public key then needs to be encoded, for example by using this service:
https://superdry.apphb.com/tools/online-rsa-key-converter
and then used to create application link to JIRA.
The private key in XML form you got previously, can be used for signing .NET app requests directly.
I personally used DonNetAuth library for signing, exchannging tokens, etc and it works for me. The only bug I encountered was regarding jql queries, where the signing needed a bit of tweaking to work correctly. Here is the link:
http://samondotnet.blogspot.sk/2012/12/introduction-to-dotnetauth.html
Additionally see this link:
https://answers.atlassian.com/questions/172760/is-there-any-jira-oauth-implementation-example-in-net
Hope this helps.

Categories