How to create a Certificate Signing Request on Xamarin - c#

How can we generate a certificate signing request (CSR) on Xamarin platforms.
CertificateRequest can be used for this in desktop .net however Xamarin/mono platforms do not support it, It throws a PlatformNotSupportedException.

The c# version of SandCastle supports creating Certificate Signing Requests (CSR)
byte[] CreateSigningRequest(string commonName, RSA rsa)
{
var name = new X509Name($"C=NZ, O=MyOrg, L=MyLocation, OU=MyOrgUnit, CN={commonName}");
var keys = DotNetUtilities.GetKeyPair(rsa);
Pkcs10CertificationRequest csr = new Pkcs10CertificationRequest("SHA256WITHRSA", name, keys.Public, null, keys.Private);
return csr.GetEncoded();
}

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.

Is there a 100% managed way to create an X509 certificate? [duplicate]

I am trying to use pure .net code to create a certificate request and create a certificate from the certificate request against an existing CA certificate I have available (either in the Windows Certificate store or as a separate file).
I know that I have the classes X509Certificate and X509Certificate2 available to load certificates and get access to their information, but I don't see any classes or functionality within the System.Security.Cryptography namespace that could be used to create a certificate request or to sign such a certificate request to create a new signed certificate.
And that although the documentation on the System.Security.Cryptography.Pkcs namespace says:
The System.Security.Cryptography.Pkcs namespace provides programming
elements for Public Key Cryptography Standards (PKCS), including
methods for signing data, exchanging keys, requesting certificates,
public key encryption and decryption, and other security functions.
So, how can I create a certificate request and fulfill that request to create a new X509 certificate using only pure .net classes from System.Security.Cryptography?
Note:
I don't want to use an external executable like openssl or MakeCert
I don't want to use BouncyCastle
I don't want to use Windows Certificate Enrollment API
I don't want to use the native Win32 API functions
Short answer: You can starting in .NET Framework 4.7.2.
This functionality was originally added to .NET Core 2.0 in the form of the CertificateRequest class, which can build a PKCS#10 certification signing request or an X.509 (self-signed or chained) public key certificate.
The classes for that feature were made available in .NET Framework 4.7.2.
using (RSA parent = RSA.Create(4096))
using (RSA rsa = RSA.Create(2048))
{
CertificateRequest parentReq = new CertificateRequest(
"CN=Experimental Issuing Authority",
parent,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
parentReq.CertificateExtensions.Add(
new X509BasicConstraintsExtension(true, false, 0, true));
parentReq.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(parentReq.PublicKey, false));
using (X509Certificate2 parentCert = parentReq.CreateSelfSigned(
DateTimeOffset.UtcNow.AddDays(-45),
DateTimeOffset.UtcNow.AddDays(365)))
{
CertificateRequest req = new CertificateRequest(
"CN=Valid-Looking Timestamp Authority",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, false));
req.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
false));
req.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.8")
},
true));
req.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
using (X509Certificate2 cert = req.Create(
parentCert,
DateTimeOffset.UtcNow.AddDays(-1),
DateTimeOffset.UtcNow.AddDays(90),
new byte[] { 1, 2, 3, 4 }))
{
// Do something with these certs, like export them to PFX,
// or add them to an X509Store, or whatever.
}
}
}
Longer answer if you're stuck on older versions: To accomplish your goal without adding any new P/Invokes, you would need to read and understand the following documents:
ITU-T X.680-201508, the ASN.1 language
IETF RFC 5280 or ITU-T X.509, the documents that explain the fields in X.509 certificates.
IETF RFC 2986, explains the PKCS#10 certification signing request
ITU-T X.690, explains the BER encoding family for ASN.1 (including DER) which tells you how to read and write bytes to achieve the semantic meaning from X.509 / PKCS#10.
And then you could write a DER writer/reader, and just emit the bytes for what you want.
I cant comment on the answer above, so this serves as a comment.
#Keith
if the issue is the private key used for the server certificate is missing the private key i hope this explains whats going on.
To combine the public and private keys in the answer above,
cert = RSACertificateExtensions.CopyWithPrivateKey(cert, rsa);
This will bundle the private key with the certificate for exporting to PFX with
File.WriteAllBytes("filename", cert.Export(X509ContentType.Pfx, "passphrase for export"));
for the example provided above.
The method CreateSelfSigned returns a X509Certificate2 object with the public and private key attached.
Where as when signing against a root, or subordinate
The Create method will only create the public key component in the X509Certificate2 object.
I think this is because the usual certificate methods would use the CSR to sign against and return the public key for acceptance by the client which would never expose the private key to the signing server.

Cipher selection for sslStream in .NET 4.5

I am trying to create a TLS1.1/TLS1.2 server using .Net's sslStream class. It appears that by default the only cipher suites that this stream accepts are:
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
I'd like to enable non-ECDHE versions of these (i.e. TLS_RSA_WITH_AES_128_CBC_SHA256). I googled around a bit and people talk about changing cipher suites by modifying the default SChannel settings -- either through the "SSL Cipher Suite Order" or using CNG functions: http://msdn.microsoft.com/en-us/library/windows/desktop/bb870930(v=vs.85).aspx
However, I tried this and I can't get anything to work. Using the above link's C++ code to list the enabled cipher suites with BCryptEnumContextFunctions() shows that the cipher suites I want are enabled by default. I even added TLS_RSA_WITH_AES_128_CBC_SHA256 as a top priority suite, and sslStream still refuses a TLS connection from a client that only supports that cipher (Exception: "The client and server cannot communicate, because they do not possess a common algorithm") Any idea what is going on here?
(By the way, if my client supports one of the ECDHE cipher suites, everything works great)
How are other folks implementing TLS in .Net 4.5? Should I be looking at opensource solutions? What about a wrapper for SChannel to use the CNG api more directly?
I contacted Microsoft's technical support and after using their proprietary tracing ability, it turned out that the certificate I had installed on my server did not have it's private key marked as an "exchange key". Apparently the private key counterpart of every public key in the certificate store has certain uses for which it it is allowed. In my case, the private key was only allowed to be used for signatures and was not allowed to be used for encrypting a symmetric key during the SSL/TLS handshake. This meant that my server could only support ECDHE cipher suites.
It also turns out that you can't check the enabled uses of a private key in the Certificate MMC snap-in. Making matters worse, using the sslStream class, there is also no way of determining any information for a handshake failure beyond the generic exception "The client and server cannot communicate, because they do not possess a common algorithm".
The final thing to mention is how I managed to install a server certificate with a restricted private key in the first place. It turns out that I generated it that way. I was using the CertEnroll COM interface to programmatically generate a certificate signing request which I exported, had a certificate authority sign, and installed the certificate authority's response. The C# code that I used to generate the certificate signing request accidentally created a private key that was only enabled for signature use.
From my experience, the CertEnroll interface is difficult to use and it's hard to find working examples online. So for others' reference I am including my C# code that generates a base64 encoded certificate signing request functional for SSL/TLS handshakes. In my case, the line objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; was missing.
using CERTENROLLLib;
using CERTCLILib;
public string GenerateRequest(string Subject, StoreLocation Location)
{
//code originally came from: http://blogs.msdn.com/b/alejacma/archive/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c.aspx
//modified version of it is here: http://stackoverflow.com/questions/16755634/issue-generating-a-csr-in-windows-vista-cx509certificaterequestpkcs10
//here is the standard for certificates: http://www.ietf.org/rfc/rfc3280.txt
//the PKCS#10 certificate request (http://msdn.microsoft.com/en-us/library/windows/desktop/aa377505.aspx)
CX509CertificateRequestPkcs10 objPkcs10 = new CX509CertificateRequestPkcs10();
//assymetric private key that can be used for encryption (http://msdn.microsoft.com/en-us/library/windows/desktop/aa378921.aspx)
CX509PrivateKey objPrivateKey = new CX509PrivateKey();
//access to the general information about a cryptographic provider (http://msdn.microsoft.com/en-us/library/windows/desktop/aa375967.aspx)
CCspInformation objCSP = new CCspInformation();
//collection on cryptographic providers available: http://msdn.microsoft.com/en-us/library/windows/desktop/aa375967(v=vs.85).aspx
CCspInformations objCSPs = new CCspInformations();
CX500DistinguishedName objDN = new CX500DistinguishedName();
//top level object that enables installing a certificate response http://msdn.microsoft.com/en-us/library/windows/desktop/aa377809.aspx
CX509Enrollment objEnroll = new CX509Enrollment();
CObjectIds objObjectIds = new CObjectIds();
CObjectId objObjectId = new CObjectId();
CObjectId objObjectId2 = new CObjectId();
CX509ExtensionKeyUsage objExtensionKeyUsage = new CX509ExtensionKeyUsage();
CX509ExtensionEnhancedKeyUsage objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
string csr_pem = null;
// Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
objCSPs.AddAvailableCsps();
//Provide key container name, key length and key spec to the private key object
objPrivateKey.ProviderName = providerName;
objPrivateKey.Length = KeyLength;
objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE; //Must flag as XCN_AT_KEYEXCHANGE to use this certificate for exchanging symmetric keys (needed for most SSL cipher suites)
objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
if (Location == StoreLocation.LocalMachine)
objPrivateKey.MachineContext = true;
else
objPrivateKey.MachineContext = false; //must set this to true if installing to the local machine certificate store
objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG; //must set this if we want to be able to export it later.
objPrivateKey.CspInformations = objCSPs;
// Create the actual key pair
objPrivateKey.Create();
// Initialize the PKCS#10 certificate request object based on the private key.
// Using the context, indicate that this is a user certificate request and don't
// provide a template name
if (Location == StoreLocation.LocalMachine)
objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, objPrivateKey, "");
else
objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, objPrivateKey, "");
//Set hash to sha256
CObjectId hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "SHA256");
objPkcs10.HashAlgorithm = hashobj;
// Key Usage Extension -- we only need digital signature and key encipherment for TLS:
// NOTE: in openSSL, I didn't used to request any specific extensions. Instead, I let the CA add them
objExtensionKeyUsage.InitializeEncode(
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE
);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
// Enhanced Key Usage Extension
objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // OID for Server Authentication usage (see this: http://stackoverflow.com/questions/17477279/client-authentication-1-3-6-1-5-5-7-3-2-oid-in-server-certificates)
objObjectId2.InitializeFromValue("1.3.6.1.5.5.7.3.2"); // OID for Client Authentication usage (see this: http://stackoverflow.com/questions/17477279/client-authentication-1-3-6-1-5-5-7-3-2-oid-in-server-certificates)
objObjectIds.Add(objObjectId);
objObjectIds.Add(objObjectId2);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// Encode the name in using the Distinguished Name object
// see here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379394(v=vs.85).aspx
objDN.Encode(
Subject,
X500NameFlags.XCN_CERT_NAME_STR_SEMICOLON_FLAG
);
// Assign the subject name by using the Distinguished Name object initialized above
objPkcs10.Subject = objDN;
//suppress extra attributes:
objPkcs10.SuppressDefaults = true;
// Create enrollment request
objEnroll.InitializeFromRequest(objPkcs10);
csr_pem = objEnroll.CreateRequest(
EncodingType.XCN_CRYPT_STRING_BASE64
);
csr_pem = "-----BEGIN CERTIFICATE REQUEST-----\r\n" + csr_pem + "-----END CERTIFICATE REQUEST-----";
return csr_pem;
}

Get Apple Keychain to recognize Bouncy Castle .NET created PKCS12 (.p12) store

Our organization manages a stable of iOS applications for multiple clients, which means dealing with a lot of different developer identity certificates and push notification certificates.
I have had success with the Bouncy Castle C# Crypto API in simplifying management of the certificates and private keys for push notifications, essentially eliminating the need for the Keychain for all our push notification certificates.
I would like to extend this to the developer identity certificates. The goal would be to store all the private key and certificate information in the database for each developer identity. Then when a new developer or build machine needs to be provisioned, server side code could wrap all of the certificates and private keys into one p12 archive with one password that could be imported into the target Mac's Keychain.
Unfortunately, the Mac Keychain doesn't like the p12 files I'm generating. This is annoying since I can successfully import these files into the Windows certificate manager just fine.
The code I'm using (the important parts) looks like this:
private byte[] GetP12Bytes(List<DevIdentity> identities, string password)
{
Pkcs12Store store = new Pkcs12Store();
foreach(DevIdentity ident in identities)
{
// Easiest to create a Bouncy Castle cert by converting from .NET
var dotNetCert = new X509Certificate2(ident.CertificateBytes);
// This method (not shown) parses the CN= attribute out of the cert's distinguished name
string friendlyName = GetFriendlyName(dotNetCert.Subject);
// Now reconstitute the private key from saved value strings
BigInteger modulus = new BigInteger(ident.PrivateKey.Modulus);
BigInteger publicExponent = new BigInteger(ident.PrivateKey.PublicExponent);
BigInteger privateExponent = new BigInteger(ident.PrivateKey.Exponent);
BigInteger p = new BigInteger(ident.PrivateKey.P);
BigInteger q = new BigInteger(ident.PrivateKey.Q);
BigInteger dP = new BigInteger(ident.PrivateKey.DP);
BigInteger dQ = new BigInteger(ident.PrivateKey.DQ);
BigInteger qInv = new BigInteger(ident.PrivateKey.QInv);
RsaKeyParameters kp = new RsaPrivateCrtKeyParameters(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv);
AsymmetricKeyEntry privateKey = new AsymmetricKeyEntry(kp);
// Now let's convert to a Bouncy Castle cert and wrap it for packaging
Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(dotNetCert);
X509CertificateEntry certEntry = new X509CertificateEntry(cert);
// Set the private key and certificate into the store
store.SetCertificateEntry(friendlyName, certEntry);
store.SetKeyEntry(ident.PrivateKeyName, privateKey, new X509CertificateEntry[] { certEntry });
}
using (MemoryStream ms = new MemoryStream())
{
store.Save(ms, password.ToCharArray(), new SecureRandom());
ms.Flush();
byte[] p12Bytes = ms.ToArray();
return p12Bytes;
}
}
Like I said, this works great for import on Windows, but fails with a very generic error when importing into the Mac Keychain.
There is one major difference I can see when loading a Keychain-generated p12 and my own generated p12 file, but I do not know if this is the cause.
If I load the Mac Keychain generated p12 into a Bouncy Castle PKCS12Store, and then examine the keys, on the Keychain p12, both the certificate and the private key have an attribute with the key "1.2.840.113549.1.9.21" with equivalent values (a DerOctetString with value #af8a1d6891efeb32756c12b7bdd96b5ec673e11e).
If I do the same to my generated p12 file, the private key contains the "1.2.840.113549.1.9.21" attribute, but the Certificate does not.
If I Google "1.2.840.113549.1.9.21", I find out that this OID means PKCS_12_LOCAL_KEY_ID . My only theory is that the Keychain relies on this to match up the certificate and private key, and that my generated file does not have this, so it fails.
However, I've tried adding these values to a Hashtable and then using the CertificateEntry constructor that takes the attribute hashtable. If I do that, and then save the bytes, and then reload the bytes, that attribute is again missing.
So I'm flummoxed. Maybe this attribute is a glitch in the Bouncy Castle API? Maybe there's something I'm doing wrong. Maybe the Keychain has ridiculous non-standard requirements for incoming p12 files. In any case, any help that could be provided would be greatly appreciated.
BouncyCastle's Pkcs12Store takes care of setting both the Friendly Name and Local Key ID attributes for you (or at least it does so in the 1.7 release, circa April 2011). My guess is that you must have used an older version where this didn't work.
Here's how I'm saving an iPhone Developer identity to a Pkcs12Store instance (extra stuff and security omitted):
var store = new Pkcs12Store();
// pairs is IEnumerable<Tuple<X509Certificate, AsymmetricKeyParameter>>
foreach (var pair in pairs)
{
var cn = pair.Item1.SubjectDN
.GetValueList(X509Name.CN).OfType<string>().Single();
var certEntry = new X509CertificateEntry(pair.Item1);
store.SetCertificateEntry(cn, certEntry);
var keyEntry = new AsymmetricKeyEntry(pair.Item2);
store.SetKeyEntry("Developer Name", keyEntry, new[] { certEntry });
}
store.Save(stream, string.Empty.ToArray(), new SecureRandom());
Importing the store in Keychain Access.app on OS X 10.7 correctly places the certificate and private key in the keychain and places the certificate within the private key in the UI, as with a certificate and key generated by Keychain Access itself.
On a side note, it seems that Pkcs12Store uses the public key of the certificate to generate the value of the LocalKeyId attribute shared by the certificate and key entries.
You can see the relevant section of the Pkcs12Store source here.

Categories