I have this problem, I wrote C# code for:
Generating CSR programmatically
Submit the CSR to Microsoft Certificate Services
Receive the certificate and save as pfx.
The code works great, but instead of creating CSR programmatically, when I use the CSR created using IIS, I get the above error.
What might be the reason please?
I am able to create the certificate in Microsoft Certificate services(by calling CCertRequestClass.Submit method and can see it in the issued certificates), but it is that I am not able to install it. The error happens when I call CX509EnrollmentClass.InstallResponse. Below is my CSR generation code:
private static CCspInformations CreateCSP()
{
CCspInformation csp = new CCspInformationClass();
CCspInformations csps = new CCspInformationsClass();
string cspAlgorithmName = "Microsoft Enhanced Cryptographic Provider v1.0";
// Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
csp.InitializeFromName(cspAlgorithmName);
// Add this CSP object to the CSP collection object
csps.Add(csp);
return csps;
}
private static CX509PrivateKey CreatePrivateKey(CCspInformations csps)
{
CX509PrivateKey csrPrivateKey = new CX509PrivateKeyClass();
// Provide key container name, key length and key spec to the private key object
csrPrivateKey.Length = 1024;
csrPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
csrPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
csrPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
csrPrivateKey.MachineContext = false;
// Provide the CSP collection object (in this case containing only 1 CSP object)
// to the private key object
csrPrivateKey.CspInformations = csps;
// Create the actual key pair
csrPrivateKey.Create();
return csrPrivateKey;
}
private static CX509ExtensionKeyUsage CreateExtensionKeyUsage()
{
CX509ExtensionKeyUsage extensionKeyUsage = new CX509ExtensionKeyUsageClass();
// Key Usage Extension
extensionKeyUsage.InitializeEncode(
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE
);
return extensionKeyUsage;
}
private static CX509ExtensionEnhancedKeyUsage CreateExtensionEnhancedKeyUsage()
{
CObjectIds objectIds = new CObjectIdsClass();
CObjectId objectId = new CObjectIdClass();
CX509ExtensionEnhancedKeyUsage extensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsageClass();
string clientAuthOid = "1.3.6.1.5.5.7.3.2";
string serverAuthOid = "1.3.6.1.5.5.7.3.1";
// Enhanced Key Usage Extension
objectId.InitializeFromValue(clientAuthOid); // OID for Client Authentication usage
objectIds.Add(objectId);
extensionEnhancedKeyUsage.InitializeEncode(objectIds);
return extensionEnhancedKeyUsage;
}
private static CX500DistinguishedName CreateDN(string subject)
{
CX500DistinguishedName distinguishedName = new CX500DistinguishedNameClass();
if (String.IsNullOrEmpty(subject))
{
subject = "CN=Suresh,C=IN,L=Bangalore,O=McAfee,OU=EMM,S=Karnataka";
}
// Encode the name in using the Distinguished Name object
distinguishedName.Encode(subject, X500NameFlags.XCN_CERT_NAME_STR_NONE);
return distinguishedName;
}
/// <summary>
/// Creates CSR
/// </summary>
/// <returns></returns>
public static string CreateRequest()
{
CX509CertificateRequestPkcs10 pkcs10Request = new CX509CertificateRequestPkcs10Class();
CX509Enrollment certEnroll = new CX509EnrollmentClass();
// 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
pkcs10Request.InitializeFromPrivateKey(
X509CertificateEnrollmentContext.ContextUser,
CreatePrivateKey(CreateCSP()),
string.Empty
);
pkcs10Request.X509Extensions.Add((CX509Extension)CreateExtensionKeyUsage());
pkcs10Request.X509Extensions.Add((CX509Extension)CreateExtensionEnhancedKeyUsage());
// Assing the subject name by using the Distinguished Name object initialized above
pkcs10Request.Subject = CreateDN(null);
// Create enrollment request
certEnroll.InitializeFromRequest(pkcs10Request);
return certEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
}
I also faced the same issue.
This code will work if you replace CX509CertificateRequestPkcs10 to the CX509CertificateRequestCertificate.
Related
I am trying to create a sha256 signature using a RSA Private Key but I am getting a 401 "Could not authenticate in-request, auth signature : Signature verification failed: affil-product, version: 2.0.0, env: prod
I think the issue is to do whit how it get my .pem file. I have read the Microsoft documentation and the provided Walmart example. I am following this guide. I created a non password protected key pair and uploaded the public key to Walmart. I then added my consumer ID and key version to appsettings.json {"Settings": {"consumerID": "e2ca6a2f-56f2-4465-88b3-273573b1e0c9","keyVer": "4"}}.
I am then getting this data in program.cs via the following code.
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
// Get values from the config given their key and their target type.
Settings settings = config.GetRequiredSection("Settings").Get<Settings>();
I then instantiate Walmart affiliate object allowing us to use the methods needed to access and read Walmart api
WalMartAfilAPI wallMartAfilAPI = new WalMartAfilAPI();
From there I Create a RSACryptoServiceProvider object and import the .pem and export the parameter.
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
var rsaPem = File.ReadAllText("D:\\Users\\Adam\\source\\repos\\DealsBot\\DealsBot\\DealsBot\\wallmartAfill\\WM_IO_private_key.pem");
//now we instantiate the RSA object
var rsa = RSA.Create();
//replace the private key with our .pem
rsa.ImportFromPem(rsaPem);
//Export the key information to an RSAParameters object.
// You must pass true to export the private key for signing.
// However, you do not need to export the private key
// for verification.
RSAParameters Key = rsa.ExportParameters(true);
From here I get the time stamp and call methods from the Walmart Affiliate object.
//Get current im in unix epoch milliseconds
TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
var time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
Console.WriteLine(time);
byte[] conicData = wallMartAfilAPI.Canonicalize(settings.KeyVer, settings.ConsumerID, time);
byte[] signedData = wallMartAfilAPI.HashAndSignBytes(conicData, Key);
if (wallMartAfilAPI.VerifySignedHash(conicData, signedData, Key))
{
Console.WriteLine("The data was verified");
;
Console.WriteLine(Convert.ToBase64String(signedData));
}
else
{
Here is the WalMartAfilAPI class
namespace DealsBot.wallmartAfill
{
public class WalMartAfilAPI
{
public byte[] Canonicalize(string version, string consumerId, string timestamp)
{
ASCIIEncoding ByteConverter = new ASCIIEncoding();
// Follow after the java code, which just orders the keys/values.
StringBuilder keyBuilder = new StringBuilder();
StringBuilder valueBuilder = new StringBuilder();
SortedDictionary<string, string> dictionary = new SortedDictionary<string, string>() { { "WM_CONSUMER.ID", consumerId }, { "WM_CONSUMER.INTIMESTAMP", timestamp }, { "WM_SEC.KEY_VERSION", version } };
foreach (string key in dictionary.Keys)
{
keyBuilder.Append($"{key.Trim()};");
valueBuilder.AppendLine($"{dictionary[key].Trim()}");
}
string[] conHeader = { keyBuilder.ToString(), valueBuilder.ToString() };
byte[] originalData = ByteConverter.GetBytes(conHeader[1]);
return originalData;
}
public byte[] HashAndSignBytes(byte[] DataToSign, RSAParameters Key)
{
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
RSAalg.ImportParameters(Key);
// Hash and sign the data. Pass a new instance of SHA256
// to specify the hashing algorithm.
return RSAalg.SignData(DataToSign, SHA256.Create());
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
public bool VerifySignedHash(byte[] DataToVerify, byte[] SignedData, RSAParameters Key)
{
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
RSAalg.ImportParameters(Key);
// Verify the data using the signature. Pass a new instance of SHA256
// to specify the hashing algorithm.
return RSAalg.VerifyData(DataToVerify, SHA256.Create(), SignedData);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return false;
}
}
}
}
As of today, auth signature code is available in Java (https://www.walmart.io/docs/affiliate/onboarding-guide)
The idea we provided sample code to help the customers to implement the logic at customer end by referring it
You can implement the logic in(C# .NET, Python, PHP or JS) in such a way that whenever your system invoking Walmart APIs, generate the signature on the fly and pass as input parameter
This is how all of customers implemented and consuming our APIs
Pls refer the below documentation for complete.
https://walmart.io/docs/affiliate/quick-start-guide
https://www.walmart.io/docs/affiliate/onboarding-guide
Regards,
Firdos
IO Support
I am learning digital signature and how to sign it in c#.Here is my code:
Signature.cs
public class Signature
{
static readonly string RT_OfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
static readonly string OfficeObjectID = "idOfficeObject";
static readonly string SignatureID = "idPackageSignature";
static readonly string ManifestHashAlgorithm = "http://www.w3.org/2000/09/xmldsig#sha1";
// Entry Point
public static void DigiSign(string tempfile)
{
// Open the Package
using (Package package = Package.Open(tempfile))
{
// Get the certificate
X509Certificate2 certificate = GetCertificate();
SignAllParts(package, certificate);
}
}
private static void SignAllParts(Package package, X509Certificate certificate)
{
if (package == null) throw new ArgumentNullException("SignAllParts(package)");
List<Uri> PartstobeSigned = new List<Uri>();
List<PackageRelationshipSelector> SignableReleationships = new List<PackageRelationshipSelector>();
foreach (PackageRelationship relationship in package.GetRelationshipsByType(RT_OfficeDocument))
{
// Pass the releationship of the root. This is decided based on the RT_OfficeDocument (http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument)
CreateListOfSignableItems(relationship, PartstobeSigned, SignableReleationships);
}
// Create the DigitalSignature Manager
PackageDigitalSignatureManager dsm = new PackageDigitalSignatureManager(package);
dsm.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
string signatureID = SignatureID;
string manifestHashAlgorithm = ManifestHashAlgorithm;
System.Security.Cryptography.Xml.DataObject officeObject = CreateOfficeObject(signatureID, manifestHashAlgorithm);
Reference officeObjectReference = new Reference("#" + OfficeObjectID);
try
{
dsm.Sign(PartstobeSigned, certificate, SignableReleationships, signatureID, new System.Security.Cryptography.Xml.DataObject[] { officeObject }, new Reference[] { officeObjectReference });
}
catch (CryptographicException ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
}// end:SignAllParts()
/**************************SignDocument******************************/
// This function is a helper function. The main role of this function is to
// create two lists, one with Package Parts that you want to sign, the other
// containing PacakgeRelationshipSelector objects which indicate relationships to sign.
/*******************************************************************/
static void CreateListOfSignableItems(PackageRelationship relationship, List<Uri> PartstobeSigned, List<PackageRelationshipSelector> SignableReleationships)
{
// This function adds the releation to SignableReleationships. And then it gets the part based on the releationship. Parts URI gets added to the PartstobeSigned list.
PackageRelationshipSelector selector = new PackageRelationshipSelector(relationship.SourceUri, PackageRelationshipSelectorType.Id, relationship.Id);
SignableReleationships.Add(selector);
if (relationship.TargetMode == TargetMode.Internal)
{
PackagePart part = relationship.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
if (PartstobeSigned.Contains(part.Uri) == false)
{
PartstobeSigned.Add(part.Uri);
// GetRelationships Function: Returns a Collection Of all the releationships that are owned by the part.
foreach (PackageRelationship childRelationship in part.GetRelationships())
{
CreateListOfSignableItems(childRelationship, PartstobeSigned, SignableReleationships);
}
}
}
}
/**************************SignDocument******************************/
// Once you create the list and try to sign it, Office will not validate the Signature.
// To allow Office to validate the signature, it requires a custom object which should be added to the
// signature parts. This function loads the OfficeObject.xml resource.
// Please note that GUID being passed in document.Loadxml.
// Background Information: Once you add a SignatureLine in Word, Word gives a unique GUID to it. Now while loading the
// OfficeObject.xml, we need to make sure that The this GUID should match to the ID of the signature line.
// So if you are generating a SignatureLine programmtically, then mmake sure that you generate the GUID for the
// SignatureLine and for this element.
/*******************************************************************/
static System.Security.Cryptography.Xml.DataObject CreateOfficeObject(
string signatureID, string manifestHashAlgorithm)
{
XmlDocument document = new XmlDocument();
document.LoadXml(String.Format(Properties.Resources.OfficeObject, signatureID, manifestHashAlgorithm, "{3CF6B91E-C5F6-46A4-B036-72597274FCC0}"));
System.Security.Cryptography.Xml.DataObject officeObject = new System.Security.Cryptography.Xml.DataObject();
// do not change the order of the following two lines
officeObject.LoadXml(document.DocumentElement); // resets ID
officeObject.Id = OfficeObjectID; // required ID, do not change
return officeObject;
}
/********************************************************/
static X509Certificate2 GetCertificate()
{
X509Store certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(certStore.Certificates, "Select a certificate", "Please select a certificate",
X509SelectionFlag.SingleSelection);
return certs.Count > 0 ? certs[0] : null;
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
Signature.DigiSign(#"D:\abc.docx");
}
}
And the file abc.docx after sign :
In the Additional Information of the signature , the system date/time(sign time) is diferrent from my local time and the date/time format too.I try to change my local time zone and reset date/time but it still not work.
What am i missing?
Use SecureBlackBox for .NET solve my problem !
I'm trying to create a keypair running the above code sample. I'm running this inside a activex. I have no problem to run this local, but when I install it on my server, it's not working properly, it only works if I run my IE as administrator.
System.UnauthorizedAccessException:
CertEnroll::CX509PrivateKey::Create: Access Denied. 0x80070005 (WIN32:
5)
em CERTENROLLLib.IX509PrivateKey.Create()
Any tips on how to run this without adm permission? Or there's any other way to create a key pair, send to CA and write the cert to a smartcard?
I'm following this code:
https://blogs.msdn.microsoft.com/alejacma/2008/09/05/how-to-create-a-certificate-request-with-certenroll-and-net-c/
public String CreateBase64KeyPair(string CN)
{
string msg = string.Empty;
try
{
CX509CertificateRequestPkcs10 objPkcs10 = (CX509CertificateRequestPkcs10)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestPkcs10"));
IX509PrivateKey objPrivateKey = (IX509PrivateKey)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey"));
CCspInformation objCSP = (CCspInformation)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CCspInformation"));
CCspInformations objCSPs = (CCspInformations)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CCspInformations"));
CX500DistinguishedName objDN = (CX500DistinguishedName)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName"));
CX509Enrollment objEnroll = (CX509Enrollment)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment"));
CObjectIds objObjectIds = (CObjectIds)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CObjectIds"));
CObjectId objObjectId = (CObjectId)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CObjectId"));
CX509ExtensionKeyUsage objExtensionKeyUsage = (CX509ExtensionKeyUsage)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionKeyUsage"));
CX509ExtensionEnhancedKeyUsage objX509ExtensionEnhancedKeyUsage = (CX509ExtensionEnhancedKeyUsage)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionEnhancedKeyUsage"));
// Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
objCSP.InitializeFromName(YPSIDCSP_NAME);
objCSP.GetDefaultSecurityDescriptor(true);
// Add this CSP object to the CSP collection object
objCSPs.Add(objCSP);
//Provide key container name, key length and key spec to the private key object
objPrivateKey.Length = 1024; //KEY_LEN_MY_DEFAULT
objPrivateKey.ProviderType = X509ProviderType.XCN_PROV_RSA_FULL; //XEnroll.ProviderType=1
objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; //XEnroll.KeySpec=AT_KEYEXCHANGE
objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
objPrivateKey.MachineContext = false;
// Provide the CSP collection object (in this case containing only 1 CSP object) to the private key object
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
objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, objPrivateKey, string.Empty);
// Key Usage Extension
objExtensionKeyUsage.InitializeEncode(
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE
);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
// Enhanced Key Usage Extension
objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2"); // OID for Client Authentication usage
objObjectIds.Add(objObjectId);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// Encode the name in using the Distinguished Name object
objDN.Encode("CN=" + CN.Trim(), X500NameFlags.XCN_CERT_NAME_STR_NONE);
// Assing the subject name by using the Distinguished Name object initialized above
objPkcs10.Subject = objDN;
// Create enrollment request
objEnroll.InitializeFromRequest(objPkcs10);
return objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
}
catch (Exception ex)
{
return ex.ToString();
}
}
I have to add extensions to a certificate request ( CSR ) in such a way that I respect a given structure. Namely this one
On the left is the structure I must respect for the chalenge password, on the right the structure I get when I simply generate a OID object from the challenge-password OID value, then embedding all this directly into the extension list of the PKCS10 request:
CObjectId cp_oid = new CObjectId();
// OID 1.2.840.113549.1.9.7
// cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
cp_oid.InitializeFromValue("1.2.840.113549.1.9.7");
then I create a CX509Extension object add the OID to the PKCS10 request:
CX509Extension extension = new CX509Extension();
string b64__challengePassword=System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(this.challengePassword));
extension.Initialize(cp_oid, EncodingType.XCN_CRYPT_STRING_BASE64_ANY, b64__challengePassword);
_certificateRequest.X509Extensions.Add(extension);
since the structure is clearly different from what I must obtain ( see the right part of the previous picture ) , I am now using a more sophisticated approach:
_certificateRequest = new CX509CertificateRequestPkcs10();
_certificateRequest.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, (CX509PrivateKey)_privateKey, null);
_certificateRequest.Subject = (CX500DistinguishedName)_subjectName;
CObjectIds cp_oids = new CObjectIds();
CObjectId cp_oid = new CObjectId();
// OID 1.2.840.113549.1.9.7
// cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
cp_oid.InitializeFromValue("1.2.840.113549.1.9.7");
CX509Extension _extension = new CX509Extension();
cp_oids.Add(cp_oid);
//now how do I add that oid list to the 1.2.840.113549.1.9.14 OID ?
//I try with CX509ExtensionEnhancedKeyUsage instead of a simple CX509Extension
//which one of all these is the correct extensions?
/*
* IX509ExtensionAlternativeNames Specifies one or more alternative name forms for the subject of a certificate.
IX509ExtensionAuthorityKeyIdentifier Represents an AuthorityKeyIdentifier extension.
IX509ExtensionBasicConstraints Specifies whether the certificate subject is a certification authority and, if so, the depth of the subordinate certification authority chain.
IX509ExtensionCertificatePolicies Represents a collection of policy information terms.
IX509ExtensionMSApplicationPolicies Represents a collection of object identifiers that indicate how a certificate can be used by an application.
IX509ExtensionEnhancedKeyUsage Represents a collection of object identifiers that identify the intended uses of the public key contained in a certificate.
IX509ExtensionKeyUsage Represents restrictions on the operations that can be performed by the public key contained in the certificate.
IX509Extensions Manages a collection of IX509Extension objects.
IX509ExtensionSmimeCapabilities Represents a collection that reports the decryption capabilities of an email recipient to an email sender.
IX509ExtensionSubjectKeyIdentifier Represents a SubjectKeyIdentifier extension used to identify a signing certificate.
IX509ExtensionTemplate Represents a CertificateTemplate extension that contains a version 2 template.
IX509ExtensionTemplateName Represents a CertificateTemplateName extension that contains a version 1 template.
*/
CX509ExtensionEnhancedKeyUsage eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(cp_oids);
eku.Critical = false;
CX509AttributeExtensions InitExt = new CX509AttributeExtensions();
// Add the extension objects into an IX509Extensions collection.
CX509Extensions ext1 = new CX509Extensions();
ext1.Add((CX509Extension)eku);
// Use the IX509Extensions collection//to initialize an IX509AttributeExtensions object.
CX509AttributeExtensions ext1att = new CX509AttributeExtensions();
ext1att.InitializeEncode(ext1);
//Add the IX509AttributeExtensions object to an IX509Attributes collection.
CX509Attributes att1 = new CX509Attributes();
att1.Add((CX509Attribute)ext1att);
//Use the IX509Attributes collection to initialize an ICryptAttribute object.
CCryptAttribute crypt1 = new CCryptAttribute();
crypt1.InitializeFromValues(att1);
//Initialize a CMC or PKCS #10 request object and retrieve the ICryptAttributes collection.
//Add the ICryptAttribute object to the ICryptAttributes collection for the request.
_certificateRequest.CryptAttributes.Add(crypt1);
//Console.WriteLine("-- encode");
this.status2 = this.status2 + "-- encode <BR>";
try
{
_certificateRequest.Encode();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
string rawData = _certificateRequest.get_RawData();
Console.WriteLine("data=" + rawData);
However I get the puzzling error "The file exists. (Exception from HRESULT: 0x80070050)" at the end of the process when encoding the request , I tried with different smartcards ad the key containers are OK, not full.
Is my approach toward adding this challenge-password extension correct and how can I interpret this error?
The answer to the error you are getting "The file exists. (Exception from HRESULT: 0x80070050)" is because trying to set a key on a template that already has a key. just comment this:
CX509ExtensionEnhancedKeyUsage eku = new CX509ExtensionEnhancedKeyUsage();
eku.InitializeEncode(cp_oids);
eku.Critical = false;
CX509AttributeExtensions InitExt = new CX509AttributeExtensions();
// Add the extension objects into an IX509Extensions collection.
CX509Extensions ext1= new CX509Extensions();
ext1.Add((CX509Extension)eku);
and it should work.
search for this in the in the article Working with Active Directory Certificate Service via C# for:
Seems that we finished, but if we just execute it will throw an
exception to us, said that the file exists when adding some
extensions.
it explains everything.
from the article:
The exception message could be a little bit confusing. In fact this is
because we defined something which had been defined in the certificate
template. If we dig into the source code we can see that the exception
occurred when we added the key usage extension.
And if we get back to the CA server and open the template we are
using, we can find that the key usage had been defined in the
template. This means in the code, or in the certificate request we
should not specify it again.
Hence we need to comment the code for adding the key usage, also we
need to comment the enhanced key usage part since it had been defined
in the template, too. Because we let the request supply the subject
name so here we can still specify the subject information in the
request. The method for generating request message would be like this.
Below is code to include Challenge Password into PKCS10 generated by CertEnroll:
private static byte[] getDerBytes(int tag, byte[] data)
{
if (data.Length > byte.MaxValue)
{
throw new NotSupportedException("Support for integers greater than 255 not yet implemented.");
}
var header = new byte[] { (byte)tag, (byte)data.Length };
return header.Concat(data).ToArray();
}
and
public static byte[] EncodePrintableString(string data)
{
var dataBytes = Encoding.ASCII.GetBytes(data);
return getDerBytes(0x13, dataBytes);
}
and finnally:
CObjectId cp_oid = new CObjectId();
cp_oid.InitializeFromName(CERTENROLL_OBJECTID.XCN_OID_RSA_challengePwd);
byte[] b64__challengePassword = EncodePrintableString("password");
ICryptAttribute ChallengeAttributes = new CCryptAttribute();
ChallengeAttributes.InitializeFromObjectId(cp_oid);
CX509Attribute ChallengeAttribute = new CX509Attribute();
ChallengeAttribute.Initialize(cp_oid, EncodingType.XCN_CRYPT_STRING_BASE64_ANY,
Convert.ToBase64String(b64__challengePassword));
ChallengeAttributes.Values.Add(ChallengeAttribute);
objPkcs10.CryptAttributes.Add((CCryptAttribute)ChallengeAttributes);
I have a bunch of root and intermediate certificates given as byte arrays, and I also have end user certificate. I want to build a certificate chain for given end user certificate. In .NET framework I can do it like this:
using System.Security.Cryptography.X509Certificates;
static IEnumerable<X509ChainElement>
BuildCertificateChain(byte[] primaryCertificate, IEnumerable<byte[]> additionalCertificates)
{
X509Chain chain = new X509Chain();
foreach (var cert in additionalCertificates.Select(x => new X509Certificate2(x)))
{
chain.ChainPolicy.ExtraStore.Add(cert);
}
// You can alter how the chain is built/validated.
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;
// Do the preliminary validation.
var primaryCert = new X509Certificate2(primaryCertificate);
if (!chain.Build(primaryCert))
throw new Exception("Unable to build certificate chain");
return chain.ChainElements.Cast<X509ChainElement>();
}
How to do it in BouncyCastle? I tried with code below but I get PkixCertPathBuilderException: No certificate found matching targetContraints:
using Org.BouncyCastle;
using Org.BouncyCastle.Pkix;
using Org.BouncyCastle.Utilities.Collections;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
static IEnumerable<X509Certificate> BuildCertificateChainBC(byte[] primary, IEnumerable<byte[]> additional)
{
X509CertificateParser parser = new X509CertificateParser();
PkixCertPathBuilder builder = new PkixCertPathBuilder();
// Separate root from itermediate
List<X509Certificate> intermediateCerts = new List<X509Certificate>();
HashSet rootCerts = new HashSet();
foreach (byte[] cert in additional)
{
X509Certificate x509Cert = parser.ReadCertificate(cert);
// Separate root and subordinate certificates
if (x509Cert.IssuerDN.Equivalent(x509Cert.SubjectDN))
rootCerts.Add(new TrustAnchor(x509Cert, null));
else
intermediateCerts.Add(x509Cert);
}
// Create chain for this certificate
X509CertStoreSelector holder = new X509CertStoreSelector();
holder.Certificate = parser.ReadCertificate(primary);
// WITHOUT THIS LINE BUILDER CANNOT BEGIN BUILDING THE CHAIN
intermediateCerts.Add(holder.Certificate);
PkixBuilderParameters builderParams = new PkixBuilderParameters(rootCerts, holder);
builderParams.IsRevocationEnabled = false;
X509CollectionStoreParameters intermediateStoreParameters =
new X509CollectionStoreParameters(intermediateCerts);
builderParams.AddStore(X509StoreFactory.Create(
"Certificate/Collection", intermediateStoreParameters));
PkixCertPathBuilderResult result = builder.Build(builderParams);
return result.CertPath.Certificates.Cast<X509Certificate>();
}
Edit: I added the line that fixed my problem. It's commented with all caps. Case closed.
I've done this in Java a number of times. Given that the API seems to be a straight port of the Java one I'll take a stab.
I'm pretty sure when you add the store to the builder, that collection is expected to contain all certs in the chain to be built, not just intermediate ones. So rootCerts and primary should be added.
If that doesn't solve the problem on its own I would try also specifying the desired cert a different way. You can do one of two things:
Implement your own Selector that always only matches your desired cert (primary in the example).
Instead of setting holder.Certificate, set one or more criteria on holder. For instance, setSubject, setSubjectPublicKey, setIssuer.
Those are the two most common problems I had with PkixCertPathBuilder.
The code below does not answer your question (it's a pure Java solution). I only just realized now after typing out everything that it doesn't answer your question! I forgot BouncyCastle has a C# version! Oops.
It still might help you roll your own chain builder. You probably don't need any libraries or frameworks.
Good luck!
http://juliusdavies.ca/commons-ssl/src/java/org/apache/commons/ssl/X509CertificateChainBuilder.java
/**
* #param startingPoint the X509Certificate for which we want to find
* ancestors
*
* #param certificates A pool of certificates in which we expect to find
* the startingPoint's ancestors.
*
* #return Array of X509Certificates, starting with the "startingPoint" and
* ending with highest level ancestor we could find in the supplied
* collection.
*/
public static X509Certificate[] buildPath(
X509Certificate startingPoint, Collection certificates
) throws NoSuchAlgorithmException, InvalidKeyException,
NoSuchProviderException, CertificateException {
LinkedList path = new LinkedList();
path.add(startingPoint);
boolean nodeAdded = true;
// Keep looping until an iteration happens where we don't add any nodes
// to our path.
while (nodeAdded) {
// We'll start out by assuming nothing gets added. If something
// gets added, then nodeAdded will be changed to "true".
nodeAdded = false;
X509Certificate top = (X509Certificate) path.getLast();
if (isSelfSigned(top)) {
// We're self-signed, so we're done!
break;
}
// Not self-signed. Let's see if we're signed by anyone in the
// collection.
Iterator it = certificates.iterator();
while (it.hasNext()) {
X509Certificate x509 = (X509Certificate) it.next();
if (verify(top, x509.getPublicKey())) {
// We're signed by this guy! Add him to the chain we're
// building up.
path.add(x509);
nodeAdded = true;
it.remove(); // Not interested in this guy anymore!
break;
}
// Not signed by this guy, let's try the next guy.
}
}
X509Certificate[] results = new X509Certificate[path.size()];
path.toArray(results);
return results;
}
Requires these two additional methods:
isSelfSigned():
public static boolean isSelfSigned(X509Certificate cert)
throws CertificateException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException {
return verify(cert, cert.getPublicKey());
}
And verify():
public static boolean verify(X509Certificate cert, PublicKey key)
throws CertificateException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException {
String sigAlg = cert.getSigAlgName();
String keyAlg = key.getAlgorithm();
sigAlg = sigAlg != null ? sigAlg.trim().toUpperCase() : "";
keyAlg = keyAlg != null ? keyAlg.trim().toUpperCase() : "";
if (keyAlg.length() >= 2 && sigAlg.endsWith(keyAlg)) {
try {
cert.verify(key);
return true;
} catch (SignatureException se) {
return false;
}
} else {
return false;
}
}