I have posted about this already but no luck since then I have more information I thought I would try again I really hope someone can help. Basically I am reading an XML file and verifying the fact that it has been signed. This code works perfectly when run as an adminitrator but not as network service, the final line resolves to 'true' but when not run as admin doesnt.
NOTE: this is not a problem with reading the XML file this opens fine. The problem is with one of the objects in memory. I 'think' the problem is to do with access control lists on the CryptoKeyRights object.
I have used the following (in the below code) to try and grant everyone access to the CspParams object:
CryptoKeyRights rightsForall = CryptoKeyRights.FullControl;
CryptoKeyAccessRule everyone = new CryptoKeyAccessRule(#"Everyone", CryptoKeyRights.FullControl, AccessControlType.Allow);
cspParams.CryptoKeySecurity = new CryptoKeySecurity();
cspParams.CryptoKeySecurity.AddAccessRule(everyone);
The above code
The code is:
// Verify the signature of an XML file against an asymmetric
// algorithm and return the result.XmlDocument Doc, RSA Key
public static Boolean VerifyLicenceFile(string xmlLicFilePathArg)
{
bool isVerified = false;
try
{
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
// Create a new XML document.
XmlDocument xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(xmlLicFilePathArg);
// Check arguments.
if (xmlDoc == null)
throw new ArgumentException("Doc");
if (rsaKey == null)
throw new ArgumentException("Key");
// Create a new SignedXml object and pass it
// the XML document class.
SignedXml signedXml = new SignedXml(xmlDoc);
// Find the "Signature" node and create a new
// XmlNodeList object.
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");
// Throw an exception if no signature was found.
if (nodeList.Count <= 0)
{
throw new CryptographicException("Verification failed: No Signature was found in the document.");
}
// This example only supports one signature for
// the entire XML document. Throw an exception
// if more than one signature was found.
if (nodeList.Count >= 2)
{
throw new CryptographicException("Verification failed: More that one signature was found for the document.");
}
// Load the first <signature> node.
signedXml.LoadXml((XmlElement)nodeList[0]);
// Check the signature and return the result.
isVerified = signedXml.CheckSignature(rsaKey);
}
catch (Exception ex)
{
}
return isVerified;
}
This sounds more like permissions on the root CA, or the signing cert. So what I'd check is where the certificates in the chain are in the certificate store - if they're in the User store (which would explain it working under Administrator) or the machine store (where they should work for everyone)
The anser for this was not to use the machine to store the keys in...export them and load them independantly...
Is it possible to sign an xml document without having to use KeyContainerName?
This is a problem with the Mandatory Profiles and Temporary profiles.
These profiles are not full users, and do not have their own key stores. You need to use an ephemeral key, or avoid triggering keystore access.
See http://blogs.msdn.com/b/alejacma/archive/2007/10/23/rsacryptoserviceprovider-fails-when-used-with-mandatory-profiles.aspx for details.
You can try setting RSACryptoServiceProvider.UseMachineKeyStore = true.
This might avoid using the user profile's keystore.
If you are using .net 4.0 you can use the new CspParameters.flags CreateEphemeralKey to indicate that the key is independent of the keystore. i.e. it is an in-memory key, not read or saved to the keychain.
Related
I would like to remove a digital signature from a VBA signed excel macro file. However when I look at EPPlus's library I see that the "Signature" property is read-only, and setting the Certificate as null doesn't seem to remove it, only invalidates the signature in the file:
using (ExcelPackage xlPackage = new ExcelPackage(fiNew))
{
xlPackage.Workbook.VbaProject.Signature.Certificate = null;
xlPackage.Save();
}
Calling the dispose method doesn't work either, errors out on the save. Does anybody know how to do this in EPPlus?
Looking at the source just provide a certificate without a private key - see line 137.
internal void Save(ExcelVbaProject proj)
{
if (Certificate == null)
{
return;
}
if (Certificate.HasPrivateKey==false) //No signature. Remove any Signature part
You could for example just use the first certificate in the Trusted Root Certificate Authorities, which have no keys, as long as you are not running on a root certificate authority or somebody improted a PFX by accident... so we filter for that too:
Here is some code to read from the Trusted Root Certificate Authorities store:
using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) {
store.Open(OpenFlags.ReadOnly);
var someCertWithoutPrivateKey =
store.Certificates
.Cast<X509Certificate2>()
.Where(c => !c.HasPrivateKey)
.FirstOrDefault();
}
We are trying to use MimeKit to validate the digitally signed emails(.p7m) signature. When I call signature.Verify(); it is throwing the error message:
{"Failed to verify digital signature: non-empty set
required\r\nParameter name: value"}.
But the same mail was verified successfully by Limilabs.Mail.
I am using the below code to verify the signature.
if (message.Body is MultipartSigned)
{
var signed = (MultipartSigned)message.Body;
foreach (var signature in signed.Verify())
{
try
{
bool valid = signature.Verify();
// If valid is true, then it signifies that the signed content
// has not been modified since this particular signer signed the
// content.
// However, if it is false, then it indicates that the signed
// content has been modified.
}
catch (DigitalSignatureVerifyException)
{
// There was an error verifying the signature.
}
}
}
Can anyone can help me on this why I am getting the error?
The problem here is that MimeKit, by default, uses the DefaultSecureMimeContext backend for S/MIME when the developer hasn't explicitly provided a context for use in the MultipartSigned.Verify() method invocation and also hasn't registered an alternative S/MIME context using CryptographyContext.Register().
Since the DefaultSecureMimeContext starts off with an empty database of S/MIME certificates, it has no trusted anchors (aka Root Certificate Authority certificates) and thus throws the exception you are seeing when it goes to build a certificate chain for the S/MIME signer when verifying the signature.
You can fix this either by importing some Root Certificate Authority certificates (preferably including the one needed in order to build the certificate chain for said signer) -or- by using the WindowsSecureMimeContext:
if (message.Body is MultipartSigned)
{
var signed = (MultipartSigned)message.Body;
using (var ctx = new WindowsSecureMimeContext ()) {
foreach (var signature in signed.Verify(ctx))
{
try
{
bool valid = signature.Verify();
// If valid is true, then it signifies that the signed content
// has not been modified since this particular signer signed the
// content.
// However, if it is false, then it indicates that the signed
// content has been modified.
}
catch (DigitalSignatureVerifyException)
{
// There was an error verifying the signature.
}
}
}
}
I've written an ASP.NET Core 2.0 website in C# and have Facebook authentication enabled, so it requires HTTPS. I'm using the native Kestrel web server to host the site and have a listener set to take the PFX certificate per MS' documentation. I can't seem to find a way for Kestrel to recognize the private key after recall from Key Vault. I know it's present, as I wrote two debug statements that indicate it is, in fact present.
This is the function that I'm using to retrieve the secret, which is working.
public static async Task<X509Certificate2> GetKeyVaultCert()
{
X509Certificate2 pfx;
try
{
var kvClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
var secret = await kvClient
.GetSecretAsync("https://macscampvault.vault.azure.net/secrets/letsencrypt").ConfigureAwait(false);
byte[] bytes;
if(secret.ContentType == "application/x-pkcs12")
bytes = Convert.FromBase64String(secret.Value);
else
{
bytes = new byte[0];
Console.WriteLine("secret is not PFX!!");
throw new ArgumentException("This is not a PFX string!!");
}
var password = new SecureString();
var coll = new X509Certificate2Collection();
coll.Import(bytes, null, X509KeyStorageFlags.Exportable);
pfx = coll[0];
// File output added in case I end up needing to write cert to container
// File.WriteAllBytes(Directory.GetCurrentDirectory().ToString() + "/Macs.pfx", bytes);
Console.WriteLine(pfx.HasPrivateKey);
Console.WriteLine(pfx.GetRSAPrivateKey());
}
catch (Exception ex)
{
Console.WriteLine($"There was a problem during the key vault operation\n{ex.Message}");
throw;
}
return pfx;
}
The debug statements after the assignment call pfx = coll[0]; tell me that this private key exists, but when I try to connect to the website using lynx https://localhost I receive the following exception:
System.NotSupportedException: The server mode SSL must use a certificate with the associated private key.
So, how do I use the private key? Here's a gist to the file in question.
I already was helped by How to serialize and deserialize a PFX certificate in Azure Key Vault? but after following it, I got to this state.
In your gist you have the following code:
var keyVaultCert = GetKeyVaultCert().Result ??
throw new ArgumentNullException("GetKeyVaultCert().Result");
pfx = new X509Certificate2(keyVaultCert.RawData);
The second line there removes the private key, because the RawData property just returns the DER encoded X.509 object.
keyVaultCert is already an X509Certificate2 with a private key, you probably want to just use it.
pfx = GetKeyVaultCert().Result ?? throw etc;
I would like to verify the signature in a SignedXml against the certificates in the machine store. This code is used to verify the signature:
internal bool VerifySignature(XmlDocument xml)
{
var signedXml = new SignedXml(xml);
var nsMgr = new XmlNamespaceManager(xml.NameTable);
nsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
signedXml.LoadXml((XmlElement)xml.SelectSingleNode("//ds:Signature", nsMgr));
return signedXml.CheckSignature();
}
The signature verifies fine, but only against itself and not against the certificates installed on the machine. Is there a way to check it against the root certificates in the local certificate store as well?
If anyone is interested, I used the CheckSignature(X509Certificate2, Boolean) method. I got the certificate from the Signature object and checked it like this:
var x509data = signedXml.Signature.KeyInfo.OfType<KeyInfoX509Data>().First();
var verified = false;
if(x509data != null)
{
var cert = x509data.Certificates[0] as X509Certificate2;
verified = cert != null && signedXml.CheckSignature(cert, false);
}
return verified;
You can use the overload of the CheckSignature method which takes an AsymmetricAlgorithm.
Pass along the public key of your certificate. You can fetch this via X509Store.
The hosting server just wont execute:
SignedXml.ComputeSignature();
I thought fromXML and toXML methods required full trust. But this came as a surprise. Now it is impossible to digitally sign any document.
On searching the net I found this:
Using RSA Public Key Encryption in a Shared Web Hosting Environment
Anyone used this before or any other way out?
I was eventually able to develop the online activation system using Bounty Castle security API's.
There is not a direct method available, but the base API can be used to generate a digital signature.
I know this post is old but maybe someone will find it useful:
The solution works with ASP .NET 3.5 in medium trust:
private XmlDocument GetSignedDoc(XmlDocument doc)
{
X509Certificate2 certificate = null;
try
{
certificate = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + licenceFile, licenceFilePass, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
if (certificate == null)
throw new Exception("The certificate i
s null!!!");
}
catch (Exception ex)
{
exception += "X509Certificate2 fail! Did not get certificate " + AppDomain.CurrentDomain.BaseDirectory + licenceFile;
exception += FormatException(ex);
goto SetError;
}
RSACryptoServiceProvider myRSASigner = null;
try
{
myRSASigner = (RSACryptoServiceProvider)certificate.PrivateKey;
if (myRSASigner == null)
{
throw new Exception("No valid cert was found");
}
doc = SignXmlFile(doc, myRSASigner);
catch (Exception ex)
{
exception += "SignXmlFile failed";
exception += FormatException(ex);
goto SetError;
}
}
private static XmlDocument SignXmlFile(XmlDocument doc, RSACryptoServiceProvider myRSA)
{
byte[] sign_this = Encoding.UTF8.GetBytes(doc.InnerXml);
byte[] signature = myRSA.SignData(sign_this, new SHA1CryptoServiceProvider());
string base64_string = Convert.ToBase64String(signature);
XmlElement Signature = doc.CreateElement("Signature");
Signature.AppendChild(doc.CreateTextNode(base64_string));
doc.DocumentElement.AppendChild(doc.ImportNode(Signature, true));
return doc;
}
The authors of the article are basically reinventing the wheel by getting different pieces together in order to get some working code. While their approach should work and you could invent some similar approach yourself (take some code here and there and try to make it work), they confirm (in the history) that there were bugs that were fixed and I assume there can be more bugs there.
JFYI: we offer XML security components which work in limited environments because we have all code written ourselves and included in our assemblies.