I have an issue attempting to register a second device through DPS to an IoT Hub using x509 certificates. My root certificate authority is present and validated on both the DPS and IoT Hub (generated through openssl). As for the client side certificate, I'm generating it once the application starts (if to doesn't already exist) in the below code. What's bothering me is every single device gets enrolled into Azure DPS correctly but only the FIRST device gets authorized and registered. Is it possibly something I'm doing during my client side certificate creation that is messing it up? Also the error is found in this line during device registration to the IoT Hub:
DeviceRegistrationResult result = await provisioningDeviceClient.RegisterAsync().ConfigureAwait(false);
Added error:
2019/12/16 09:37:38.309|ERROR| Error found attempting to start service The device failed to register # the IoT Hub : The device failes to provision correctly: AMQP transport exception | Tidel.DeviceAgent.DeviceAgent |
CLIENT SIDE CERTIFICATE GENERATION
X509Certificate2 caRootCertificate;
X509Store caStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
caStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection signerCollection = (X509Certificate2Collection)caStore.Certificates.Find(X509FindType.FindByIssuerName, "CERTNAME", true);
caStore.Close();
if (signerCollection.Count != 0)
{
caRootCertificate = signerCollection[0];
using (var rsa = RSA.Create())
{
rsa.KeySize = 2048;
var clientCertificateRequest = new CertificateRequest($"CN={_writableOptions.Value.RegistrationId}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
clientCertificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
var issuerSubjectKey = caRootCertificate.Extensions["Subject Key Identifier"].RawData;
var segment = new ArraySegment<byte>(issuerSubjectKey, 2, issuerSubjectKey.Length - 2);
var authorityKeyIdentifier = new byte[segment.Count + 4];
authorityKeyIdentifier[0] = 0x30;
authorityKeyIdentifier[1] = 0x16;
authorityKeyIdentifier[2] = 0x80;
authorityKeyIdentifier[3] = 0x14;
segment.CopyTo(authorityKeyIdentifier, 4);
clientCertificateRequest.CertificateExtensions.Add(new X509Extension("2.5.29.35", authorityKeyIdentifier, false));
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName(_writableOptions.Value.RegistrationId);
var sanExtension = sanBuilder.Build();
clientCertificateRequest.CertificateExtensions.Add(sanExtension);
clientCertificateRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.2") }, false));
clientCertificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(clientCertificateRequest.PublicKey, false));
var notBefore = DateTimeOffset.UtcNow.AddDays(-1);
if (notBefore < caRootCertificate.NotBefore)
{
notBefore = new DateTimeOffset(caRootCertificate.NotBefore);
}
var notAfter = DateTimeOffset.UtcNow.AddDays(365);
if (notAfter > caRootCertificate.NotAfter)
{
notAfter = new DateTimeOffset(caRootCertificate.NotAfter);
}
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var unixTime = Convert.ToInt64((DateTime.UtcNow - epoch).TotalSeconds);
var serial = BitConverter.GetBytes(unixTime);
using (var cert = clientCertificateRequest.Create(caRootCertificate, notBefore, notAfter, serial))
{
X509Certificate2 client = cert.CopyWithPrivateKey(rsa);
return await Task.FromResult(client);
}
}
}
else
{
throw new FileNotFoundException($"Could not find a root certificate.");
}
DEVICE ENROLLMENT TO DPS
Attestation attestation = X509Attestation.CreateFromClientCertificates(new X509Certificate2(certificate.Export(X509ContentType.Cert)));
IndividualEnrollment individualEnrollment = new IndividualEnrollment(_writableOptions.Value.RegistrationId, attestation)
{
DeviceId = _writableOptions.Value.DeviceId,
ProvisioningStatus = ProvisioningStatus.Enabled
};
individualEnrollmentResult = await _provisioningServiceClient.CreateOrUpdateIndividualEnrollmentAsync(individualEnrollment).ConfigureAwait(false);
DEVICE REGISTRATION TO IOT HUB
using (var certificatePassword = new X509Certificate2(certificate.GetRawCertData(), _writableOptions.Value.CertPass))
{
using (var security = new SecurityProviderX509Certificate(certificatePassword))
{
using (var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly))
{
ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.Create(_writableOptions.Value.AzureEndpoint, _writableOptions.Value.IdScope, security, transport);
DeviceRegistrationResult result = await provisioningDeviceClient.RegisterAsync().ConfigureAwait(false);
IAuthenticationMethod authenticationMethod = new DeviceAuthenticationWithX509Certificate(result.DeviceId, certificate);
DeviceClient deviceClient = DeviceClient.Create(result.AssignedHub, authenticationMethod, TransportType.Amqp_Tcp_Only);
return await Task.FromResult(deviceClient);
}
}
}
I figured out the issue. When the certificate was generated in the store, I was using FindByIssuerName to locate the certificate.
X509Certificate2Collection signerCollection = (X509Certificate2Collection)caStore.Certificates.Find(X509FindType.FindByIssuerName, "CERTNAME", true);
After investigating further, there were TWO certificates with the exact same name in the store. The issue: MMC snap-in was only showing one certificate. After looking around, it was suggested somewhere to run a storerepair command on the store. After running the store repair command, I could then see both certificates in MMC and was able to remove the offending certificate preventing the valid one from being detected.
Windows Version: Windows Embedded 8.1 Industry Pro
Related
I create a self sign certificate in debug with .net core and store it into the certificate store (my/root). When the app runs under windows the certificate cain is okay but under linux (ubuntu) the certificate chain check fails.
Certificate creation code is:
public static X509Certificate2 BuildSelfSignedServerCertificate()
{
using RSA rsa = RSA.Create(2048);
var request = new CertificateRequest("CN=My_Certificate",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
request.CertificateExtensions
.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection {
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.2") }, false));
var certificate = request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)),
new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
LogFileLevelInformation("Created new self sign certificate.");
return certificate;
}
Then I add the certificate at my and root stores:
public static void CreateCert()
{
var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadWrite);
bool result = certificateStore.Certificates.OfType<X509Certificate2>()
.Any(cert => cert.Subject.Contains("My_Certificate"));
if (result)
return;
var cert = BuildSelfSignedServerCertificate();
certificateStore.Add(cert);
certificateStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser, OpenFlags.MaxAllowed);
certificateStore.Open(OpenFlags.MaxAllowed);
certificateStore.Add(cert);
LogFileLevelInformation("New certificate added to store.");
}
then later I verify the chain:
private X509Certificate2 VerifyCertificateChain(X509Certificate2 certificate)
{
var chain = new X509Chain();
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
var chainBuilt = chain.Build(certificate);
if (chainBuilt)
{
LogDebugLevelInformation("The certificate chain is validated.")
return certificate;
}
return null;
}
the certificate check under windows with
var chainBuilt = chain.Build(certificate);
returns with result true, but when the same code runs under linux it returns false.
the certificate is available on linux under:
/root/.dotnet/corefx/cryptography/x509stores/root
and
/root/.dotnet/corefx/cryptography/x509stores/my
chain.StatusCode = "unable to get local issuer certificate"
Any idea why it is failing under linux?
I have some code that is supposed to add an ssl certifacate to a port. It does say it has succesfully added a certificate, but when i host the website and then type in the localhost url (localhost:{port}) the page keeps loading. I don't understand why it doesn't work.
I already tried to host the website without ssl and that worked fine so that's not the problem.
public static class Certificate
{
public static void Standard(int port)
{
var certSubjectName = "HelloMyNameIsHank";
var expiresIn = TimeSpan.FromHours(1);
var cert = GenerateCert(certSubjectName, expiresIn);
Console.WriteLine("Generated certificate, {0}Thumbprint: {1}{0}", Environment.NewLine, cert.Thumbprint);
RegisterSslOnPort(port, cert.Thumbprint);
Console.WriteLine($"Registerd SSL on port: {port}");
}
private static void RegisterSslOnPort(int port, string certThumbprint)
{
var appId = Guid.NewGuid();
string arguments = $"http add sslcert ipport=0.0.0.0:{port} certhash={certThumbprint} appid={{{appId}}}";
ProcessStartInfo procStartInfo = new ProcessStartInfo("netsh", arguments);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
var process = Process.Start(procStartInfo);
while (!process.StandardOutput.EndOfStream)
{
string line = process.StandardOutput.ReadLine();
Console.WriteLine(line);
}
process.WaitForExit();
}
public static X509Certificate2 GenerateCert(string certName, TimeSpan expiresIn)
{
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
var existingCert = store.Certificates.Find(X509FindType.FindBySubjectName, certName, false);
if (existingCert.Count > 0)
{
store.Close();
return existingCert[0];
}
else
{
var cert = CreateSelfSignedCertificate(certName, expiresIn);
store.Add(cert);
store.Close();
return cert;
}
}
public static X509Certificate2 CreateSelfSignedCertificate(string subjectName, TimeSpan expiresIn)
{
// create DN for subject and issuer
var dn = new CX500DistinguishedName();
dn.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
// create a new private key for the certificate
CX509PrivateKey privateKey = new CX509PrivateKey();
privateKey.ProviderName = "Microsoft Base Cryptographic Provider v1.0";
privateKey.MachineContext = true;
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; // use is not limited
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
privateKey.Create();
// Use the stronger SHA512 hashing algorithm
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA512");
// add extended key usage if you want - look at MSDN for a list of possible OIDs
var oid = new CObjectId();
oid.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // SSL server
var oidlist = new CObjectIds();
oidlist.Add(oid);
var eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(oidlist);
// Create the self signing request
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
cert.Subject = dn;
cert.Issuer = dn; // the issuer and the subject are the same
cert.NotBefore = DateTime.Now;
// this cert expires immediately. Change to whatever makes sense for you
cert.NotAfter = DateTime.Now.Add(expiresIn);
cert.X509Extensions.Add((CX509Extension)eku); // add the EKU
cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
cert.Encode(); // encode the certificate
// Do the final enrollment process
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert); // load the certificate
enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name
string csr = enroll.CreateRequest(); // Output the request in base64
// and install it back as the response
enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password
// output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
// instantiate the target class with the PKCS#12 data (and the empty password)
return new System.Security.Cryptography.X509Certificates.X509Certificate2(
System.Convert.FromBase64String(base64encoded), "",
// mark the private key as exportable (this is usually what you want to do)
System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable
);
}
}
I want to be able to host my website with https. If anyone knows why this doesn't work please tell me.
I'm using httplistener for hosting. I don't want to use ASP.NET and/or IIS.
I'm not to familiar with ssl certificates so I would also really appreciate some code snippets even if it's just pseudo-code.
I want to consume a web service (https://realtime-listings-api.webservices.zpg.co.uk/sandbox/v1/listing/list) and as per there documentation (https://realtime-listings.webservices.zpg.co.uk/docs/latest/documentation.html)
I have to send .crt and .pem file for authentication.
I am able to load .crt file but for .pem I am getting error that Cannot find the requested object . I have tried different method to load PEM file.
I have followed following threads but still not able to load X509Certificate from .pem file.
My code is as below
var webAddr = "https://realtime-listings-api.webservices.zpg.co.uk/sandbox/v1/listing/list";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json; profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.1/schemas/listing/list.json";
httpWebRequest.Method = "POST";
httpWebRequest.ClientCertificates.Add(X509Certificate.CreateFromCertFile(#"E:\ProcessZooplaData\zpg_realtime_listings_14810206-20261204.crt"));
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Now till here everything is Okay now If I try to load .pem file then I am getting error
var pem = System.IO.File.ReadAllText(#"E:\\ProcessZooplaData\\private.pem");
byte[] certBuffer = GetBytesFromPEM(pem, "RSA PRIVATE KEY");
var certificate = new X509Certificate(certBuffer);
httpWebRequest.ClientCertificates.Add(certificate);
byte[] GetBytesFromPEM(string pemString, string section)
{
var header = String.Format("-----BEGIN {0}-----", section);
var footer = String.Format("-----END {0}-----", section);
var start = pemString.IndexOf(header, StringComparison.Ordinal);
if (start < 0)
return null;
start += header.Length;
var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if (end < 0)
return null;
return Convert.FromBase64String(pemString.Substring(start, end));
}
I am getting error here that Cannot find the requested object .
Rest of code is as below
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"branch_reference\":\"test\"}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
//return result;
}
I have tried following threads for reference
how to get private key from PEM file?
http://pages.infinit.net/ctech/20040812-0816.html
If your certificate has already been loaded into your cert store, then you can do the following:
var requestHandler = new WebRequestHandler();
var store = new X509Store("My", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, certificateName, true);
if (certificates.Count > 0)
requestHandler.ClientCertificates.Add(certificates[0]);
else
throw new Exception(string.Format("Can't find certificate {0}", certificateName));
using (var client = new HttpClient(requestHandler))
{
do work!
}
which should add the cert from the store to the connection.
Then you'll need to open up the cert store in the Certificates (run mmc, add Certificates to the console for the local computer store), browse to your certificate, right click it and select All Tasks > Manage Private Keys and grant the user account your application will run under "read" access to the cert's private key and you won't need to open any files to do this.
Of course, if you're doing this on a shared hosting environment where you don't get access to the certs or can't install them into the cert store, that's a different problem.
I'm using this code to connect to Google OAuth2.0 and I'm using the C# libraries.
using (var tImpersonationContext = new ImpersonationContext(ConfigurationManager.AppSettings["ImpersonationDomain"], ConfigurationManager.AppSettings["ImpersonationUser"], ConfigurationManager.AppSettings["ImpersonationPassword"]))
{
string[] tScopes = new string[] { AnalyticsService.Scope.Analytics };
var tKeyFilePath = Path.Combine(System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]), String.Format("GoogleCertificates\\{0}", this.OAuthKeyName));
var tServiceAccountEmail = this.OAuthServiceAccountEmail;
//loading the Key file
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.Exportable);
var tCredential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(tServiceAccountEmail)
{
Scopes = tScopes
}.FromCertificate(tCertificate));
if (tCredential.RequestAccessTokenAsync(CancellationToken.None).Result)
{
this.SessionToken = tCredential.Token.AccessToken;
}
}
In my dev enviroment everything works just perfect but when I move the app to my Azure Server I got this error:
The system cannot find the file specified.
When I try to execute this line:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.Exportable);
I'm sure that the path is correct, in fact if I copy and paste the path in the File Explorer the wizard to import the certificate starts.
I'm sure that the impersonation is working because the line is excecuted.
I have tried to change it to:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.MachineKeySet);
Or this:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.UserProtected | X509KeyStorageFlags.DefaultKeySet);
And nothing is working, in fact everything I have tried from other questions of this web site are not working.
If it help is a Windows App that I pretend to use in a task scheduled, to get data from Google Analytics.
Does someone have any idea why is not working in the Azure Server??
Thanks in advance.
UPDATE
I have tried to install the certificate and I have change the code to this:
var tCertificate = new X509Certificate2(tKeyFilePath, this.OAuthKeyPassword, X509KeyStorageFlags.MachineKeySet);
But now I get this error:
Key not valid for use in specified state.
I have change all the code an the aproach and now is working.
First you have to instal your certificate in the MMC manager inside trusted root certificates authorities under certificates and it have to be set as a local machine.
And then you just have to use this code:
X509Store tCertStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
tCertStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection tCertCollection = tCertStore.Certificates.Find(X509FindType.FindByThumbprint, this.OAuthKeyFingerprint, false);
// Get the first cert with the thumbprint
if (tCertCollection.Count > 0)
{
// header
var tHeader = new { typ = "JWT", alg = "RS256" };
// claimset
var tTimes = GetExpiryAndIssueDate();
var tClaimset = new
{
iss = this.OAuthServiceAccountEmail,
scope = "https://www.googleapis.com/auth/analytics",
aud = "https://www.googleapis.com/oauth2/v3/token",
iat = tTimes[0],
exp = tTimes[1],
};
JavaScriptSerializer ser = new JavaScriptSerializer();
// encoded header
var tHeaderSerialized = ser.Serialize(tHeader);
var tHeaderBytes = Encoding.UTF8.GetBytes(tHeaderSerialized);
var tHeaderEncoded = Convert.ToBase64String(tHeaderBytes);
// encoded claimset
var tClaimsetSerialized = ser.Serialize(tClaimset);
var tClaimsetBytes = Encoding.UTF8.GetBytes(tClaimsetSerialized);
var tClaimsetEncoded = Convert.ToBase64String(tClaimsetBytes);
// input
var tInput = tHeaderEncoded + "." + tClaimsetEncoded;
var tInputBytes = Encoding.UTF8.GetBytes(tInput);
X509Certificate2 tCertificate = tCertCollection[0];
// signiture
var tRSA = tCertificate.PrivateKey as RSACryptoServiceProvider;
var tCspParam = new CspParameters
{
KeyContainerName = tRSA.CspKeyContainerInfo.KeyContainerName,
KeyNumber = tRSA.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2,
Flags = CspProviderFlags.UseMachineKeyStore
};
var tAesCsp = new RSACryptoServiceProvider(1024, tCspParam) { PersistKeyInCsp = true };
var tSignatureBytes = tAesCsp.SignData(tInputBytes, "SHA256");
var tSignatureEncoded = Convert.ToBase64String(tSignatureBytes);
// jwt
var tJWT = tHeaderEncoded + "." + tClaimsetEncoded + "." + tSignatureEncoded;
HttpClient tClient = new HttpClient();
Dictionary<string, string> tPost = new Dictionary<string, string>
{
{"assertion", tJWT},
{"grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"}
};
FormUrlEncodedContent tContent = new FormUrlEncodedContent(tPost);
var tURI = "https://www.googleapis.com/oauth2/v3/token";
HttpResponseMessage tResult = tClient.PostAsync(tURI, tContent).Result;
TokenResponse tTokenObject = JsonConvert.DeserializeObject<TokenResponse>(tResult.Content.ReadAsStringAsync().Result);
this.SessionToken = tTokenObject.access_token;
}
tCertStore.Close();
I hope this help someone.
Happy coding.
I working with programmatically working with certificates and communicating with a Certificate Authority. I have been working with the CertClient and CertEnroll COM objects in C# on Windows 2008R2.
I can generate a request and get back a cert from the CA. I started with this example:
http://blogs.msdn.com/b/alejacma/archive/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c.aspx
I am having two issues that I can not figure out. First is, how can I get access to the private key that was used to generate the cert? The methods that are part of the IX509PrivateKey interface don't seem to work on my test env. The request I give the CA is different from the private key, correct?
The second issue is I can't seem to figure out to supply an enrollment agent cert when requesting a cert. The older versions of this API had a method, SetSignerCertificate, that was used. I can't find an equivalent in the new API.
An example using an Enrollment Agent certificate is found on MSDN under Create Enroll on Behalf of Another User Request. The certificate will be installed into the certificate store you specify, and you can utilize the private key as you would any other installed certificate.
Full example:
// Add references to CERTENROLL (CertEnroll 1.0 Type Library)
// and CERTCLILib (CertCli 1.0 Type Library)
using CERTCLILib;
using CERTENROLLLib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using X509KeyUsageFlags = CERTENROLLLib.X509KeyUsageFlags;
namespace TestSubmitEnrollment
{
class Program
{
static void Main(string[] args)
{
string requesterName = #"DOMAIN\otherUser";
string caName = #"CA1.DOMAIN.LOCAL\DOMAIN-CA1-CA";
string template = "User";
// signerCertificate's private key must be accessible to this process
var signerCertificate = FindCertificateByThumbprint("3f817d138f32a9a8df2aa6e43b8aed76eb93a932");
// create a new private key for the certificate
CX509PrivateKey privateKey = new CX509PrivateKey();
// http://blogs.technet.com/b/pki/archive/2009/08/05/how-to-create-a-web-server-ssl-certificate-manually.aspx
privateKey.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
privateKey.MachineContext = false;
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_NONE;
privateKey.Create();
// PKCS 10 Request
// we use v1 to avoid compat issues on w2k8
IX509CertificateRequestPkcs10 req = (IX509CertificateRequestPkcs10)new CX509CertificateRequestPkcs10();
req.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, privateKey, template);
// PKCS 7 Wrapper
var signer = new CSignerCertificate();
signer.Initialize(false, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_BASE64_ANY,
Convert.ToBase64String(signerCertificate.GetRawCertData()));
var wrapper = new CX509CertificateRequestPkcs7();
wrapper.InitializeFromInnerRequest(req);
wrapper.RequesterName = requesterName;
wrapper.SignerCertificate = signer;
// get CSR
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(wrapper);
var csr = enroll.CreateRequest();
//File.WriteAllText("csr.p7b", csr);
// submit
const int CR_IN_BASE64 = 1, CR_OUT_BASE64 = 1;
const int CR_IN_PKCS7 = 0x300;
ICertRequest2 liveCsr = new CCertRequest();
var disposition = (RequestDisposition)liveCsr.Submit(CR_IN_BASE64 | CR_IN_PKCS7, csr, null, caName);
if (disposition == RequestDisposition.CR_DISP_ISSUED)
{
string resp = liveCsr.GetCertificate(CR_OUT_BASE64);
//File.WriteAllText("resp.cer", resp);
// install the response
var install = new CX509Enrollment();
install.Initialize(X509CertificateEnrollmentContext.ContextUser);
install.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedRoot,
resp, EncodingType.XCN_CRYPT_STRING_BASE64_ANY, null);
}
else
{
Console.WriteLine("disp: " + disposition.ToString());
}
Console.WriteLine("done");
Console.ReadLine();
}
private enum RequestDisposition
{
CR_DISP_INCOMPLETE = 0,
CR_DISP_ERROR = 0x1,
CR_DISP_DENIED = 0x2,
CR_DISP_ISSUED = 0x3,
CR_DISP_ISSUED_OUT_OF_BAND = 0x4,
CR_DISP_UNDER_SUBMISSION = 0x5,
CR_DISP_REVOKED = 0x6,
CCP_DISP_INVALID_SERIALNBR = 0x7,
CCP_DISP_CONFIG = 0x8,
CCP_DISP_DB_FAILED = 0x9
}
private static X509Certificate2 FindCertificateByThumbprint(string sslCertThumbprint)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, sslCertThumbprint, true);
if (certs.Count > 0)
{
return certs[0];
}
else
{
throw new KeyNotFoundException();
}
}
finally
{
store.Close();
}
}
// we re-declare this to account for backcompat to 2k8
[ComImport, Guid("884E2042-217D-11DA-B2A4-000E7BBB2B09")]
class CX509CertificateRequestPkcs10
{
}
}
}
The request I give the CA is different from the private key, correct?
You only pass the public key to the CA.
I can't seem to figure out to supply an enrollment agent cert when requesting a cert.
You Need to wrap the PKCS10 in a CMS/CMC. Have a look here https://www.rfc-editor.org/rfc/rfc5272