I'm creating a code that verifies the signature of an xml file through the public key of the certification authority of the certificate that signed that xml. For some reason when I check with the public key of the certificate that signed it, everything goes well, but when I use the public key of the parent, it says that the signature is invalid.
I show below both the code that signs and what it verifies.
Comments:
The CA is self-signed by an AD CS.
I don't want the crl of the certificates to be validated.
private void signBtn_Click(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\urnaData\votos.xml");
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindByIssuerName, "CA das Urnas - CNE", true);
foreach (X509Certificate2 x509 in col)
{
SignXmlDoc(doc, x509);
File.WriteAllText(#"C:\urnaData\votes-signed.xml", doc.OuterXml);
}
store.Close();
}
public static void SignXmlDoc(XmlDocument doc, X509Certificate2 cert)
{
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.GetRSAPrivateKey();
Reference reference = new Reference();
reference.Uri = "";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(cert));
signedXml.KeyInfo = keyInfo;
signedXml.ComputeSignature();
XmlElement xmlSig = signedXml.GetXml();
doc.DocumentElement.AppendChild(doc.ImportNode(xmlSig, true));
}
private void testSign_Click(object sender, EventArgs e)
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindByIssuerName, "CA das Urnas - CNE", true);
foreach (X509Certificate2 x509 in col)
{
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\urnaData\votes-signed.xml");
MessageBox.Show(ValidateXMLSignature(doc, x509).ToString());
}
store.Close();
}
public static bool ValidateXMLSignature(XmlDocument doc, X509Certificate2 cert)
{
try
{
SignedXml signedXml = new SignedXml(doc);
XmlNode signatureNode = doc.GetElementsByTagName("Signature")[0];
signedXml.LoadXml((XmlElement)signatureNode);
return signedXml.CheckSignature(cert, true);
}
catch
{
return false;
}
}
For some reason when I check with the public key of the certificate that signed it, everything goes well, but when I use the public key of the parent, it says that the signature is invalid.
That sounds like everything is working correctly. The parent/CA didn't sign the document, so their public key can't verify it. All you can do is ask two different questions:
Does /this/ certificate's public key verify the signature.
Does /this/ certificate's issuance chain (see X509Chain) say that /this other/ certificate was its direct issuer? (s_parentCertBytes.SequenceEqual(chain.ChainElements[1].Certificate.RawData))
If both return true, then you have effectively written an issuer-pinning routine. But you can't do it without the public key of the actual signing certificate. (The easiest way to internalize that is to realize that the CA and the issued cert don't need to use the same algorithm family... e.g. the CA could have an ECDSA-based key and the issued cert have an RSA-based key... so there's no way you can sensibly ask if the ECDSA public key of the CA can corroborate the RSA signature.)
Related
I am creating signed SAML Request using below code and the certificates are cross checked to be same however getting invalid signature error on IDP , Please suggest
public XmlDocument SignXML(XmlDocument xml)
{
X509Certificate2 cert = new X509Certificate2(#"" + SPCertificateLocation, SPCertificatePassword);// , X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
// set key, signing algorithm, and canonicalization method
var signedXml = new SignedXml(xml.DocumentElement) { SigningKey = cert.GetRSAPrivateKey() };
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
//signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
// sign whole document using "SAML style" transforms
var reference = new Reference { Uri = string.Empty };
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
signedXml.AddReference(reference);
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data keyInfoData = new KeyInfoX509Data(cert);
keyInfo.AddClause(keyInfoData);
signedXml.KeyInfo = keyInfo;
// create signature
signedXml.ComputeSignature();
// get signature XML element and add it as a child of the root element
//signedXml.GetXml();
//XmlElement xmlSignature = signedXml.GetXml();
//AssignNameSpacePrefixToElementTree(xmlSignature, "ds");
// Add prefix "ds:" to signature
XmlElement signature = signedXml.GetXml();
SetPrefix("ds", signature);
// Load modified signature back
signedXml.LoadXml(signature);
// this is workaround for overcoming a bug in the library
signedXml.SignedInfo.References.Clear();
// Recompute the signature
signedXml.ComputeSignature();
string recomputedSignature = Convert.ToBase64String(signedXml.SignatureValue);
// Replace value of the signature with recomputed one
ReplaceSignature(signature, recomputedSignature);
// Append the signature to the XML document.
xml.DocumentElement.InsertAfter(xml.ImportNode(signature, true), xml.DocumentElement.FirstChild);
//xml.DocumentElement?.AppendChild(signedXml.GetXml());
return xml;
}
The code converts the XML correctly into signature enabled XML however it is showing unable to verify the authenticity of signature
<saml2p:AuthnRequest ID="_3a6d2566-68a1-4afc-a784-c125bd94173f" Version="2.0" ForceAuthn="false" IssueInstant="2022-05-09T08:26:33Z" Destination="https://auth.pingone.com/172e7894-5cbe-456b-b451-d2585aeab74f/saml20/idp/sso" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://localhost:20630/CASCC/SamlConsume" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:20630</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>EZBisrHSVKE2zS15roaUFG0EnP3ZobisulcQXw0fO18=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>uyniiynlIP6z/g6U4qzWKdmeD8sP7majYwqDhy5n2nTDJyE8MEdaIhEJssIDeuc+VAILueMhzRV/mC4uiAZ5kgAYDa0LYnaO3673dCvuFJPgQxKbJUWYSTnfY89x/VYXsFm6kKgR2pxyGgq4VYGK9W/vvpIi+PKkYwCKtjjb8LKin/zoCPAA7UecMS9mxPeJ/ntdEfjIGTwBcoETsFRRNdVToTdVtQJjc4LBa/5FnJbGNRKFIdRDqx+wr7IbaT+08o5yl+UI2Qogly8JxeOPolVUr94rLXKyY1/n2nkXr+5LoY/sRomoCNJ11wLsMUEmg11j4J3bB93FGtlnmpwm4Q==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEFzCCAv+gAwIBAgIUeUS7jZnkZLcxjRUQ5uhqrAI7z2gwDQYJKoZIhvcNAQELBQAwgZoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTEPMA0GA1UEBwwGQm9zdG9uMRQwEgYDVQQKDAtDb21tYW5kQ29tbTEUMBIGA1UECwwLQ29tbWFuZENvbW0xFDASBgNVBAMMC0NvbW1hbmRDb21tMSswKQYJKoZIhvcNAQkBFhxha2Fwb29yQGNvbW1hbmRjb21wYW5pZXMuY29tMB4XDTIyMDQxNTExNDk1MloXDTIzMDQxNTExNDk1MlowgZoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTEPMA0GA1UEBwwGQm9zdG9uMRQwEgYDVQQKDAtDb21tYW5kQ29tbTEUMBIGA1UECwwLQ29tbWFuZENvbW0xFDASBgNVBAMMC0NvbW1hbmRDb21tMSswKQYJKoZIhvcNAQkBFhxha2Fwb29yQGNvbW1hbmRjb21wYW5pZXMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyTHXlPSUCzHLGXkoSkuXTh2/oONTe+5TcswtFTnq6VDZnZ3m1tNs2EWcOXq6lVjSlfaqByCHUSGa0O54qMAD2WdZjTv08GQS1uYtJhjQvi58P32ost2LBEKYhtyaw8fen81OwJq+tiDHCw8xrx/z610rHGQVRrSojmqC6zLOclkQmrfpAhC2L+enovxfl6xW0FzYJETFYv/jW0ACcaZJBtFb7AfYDlCzvajUQ+dGTpZE1/0UOGdOTNjbMezGxFDyndcJ4ZLQItbG0cE3c1fEQkU06OYgPoOeXGsHM2w1R498oS/pk1ZLB1LtDUfKl7PVmjJGoytCi38Huft/95u0JQIDAQABo1MwUTAdBgNVHQ4EFgQUawg+tyAVohufNTVu7RXqPtu+B4MwHwYDVR0jBBgwFoAUawg+tyAVohufNTVu7RXqPtu+B4MwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAPom0ZUQx3OWXlO7R5Goyv7cEepq+Nikd82chqzUvwhXW5Azb3xsC+cgh0srjYlrRC9QJk7m43yQE1xa0DM4aKi/rA/l9rLJpXfjQj+97V0SA8JMrCxbcxdN+v2mN1vp8xprJ2i+nPtCBPqLM2XA0kUQ44zeygCm+L78zElYu79r3YF6ObzA9D3/Fa794zFWqI/1ggmOdI4T3kzUgFxv1YMTYZ8rgWcICVs1mSU2zkT5ScJq0NJPENR0jpzFHzirzIJUgnAcOWnCnzyCKDgpJI/+9yJNzLaR97LHk12cVfQMuBvLcchRian08sZ/J4a440h54bB9EByp+853rUpHhVQ==</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature></saml2p:AuthnRequest>
As #timothy suggested i added the Id = "#" + Id
and below is the XML generated
<saml2p:AuthnRequest ID="_35ab8f46-0ba1-4a29-a153-b02e97bf1397" Version="2.0" ForceAuthn="false" IssueInstant="2022-05-10T09:25:33Z" Destination="https://auth.pingone.com/172e7894-5cbe-456b-b451-d2585aeab74f/saml20/idp/sso" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://localhost:20630/CASCC/SamlConsume" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:20630</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><ds:Reference URI="#_35ab8f46-0ba1-4a29-a153-b02e97bf1397"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>0iGmT3qeJrHVXJveUe5AboRo65hxD25OjVpbbxWvbUo=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>InYJ2yGLSnL9Q2BVaWBfABpcpiViFgZH6XnvhYU0I67sPI/KTb6g39MZMBM4XiUDNTpjGXHcZn9c2elh+NnVAx/SPX8R0Yjq7dKiVe+Zz6BFKk0DCSA9Q2APFCk2uJZVlvKza3tIjoxrBvQuxZcNZxsMOUm8LEWnhQ3ElgN7pisHj5AiAzc/VluPIl21BG+tTolV/Ab7bg+B7YdBwK4++QEyK0R7dsYoZex+NsLf1+zel1xWg2PbZgTXOi4PXFB9RvIPXDUtfMPCSOUQRUdgbI6GeV5X+ZWCGOs0m0FnnZD7hL1WR1sWOcHUgY0DSIpgtToJintjx0f5HphidWZF9/sjP+zR9QEOtkcBMYl9JacO5tbNp49fZSVuuqN2pMxcSCE2BmPdoWw1t/CHrKFOzOfNyNQsvauP2UpufQQt+lxUePpvE7VZNuXYQ+hGv74FkBpy6NU58x3agqOP0NeMsyrT+R0AOSTCXWqzfogYIOR9t/mOFMTohKJqjPXB7H2+LrlOcmQM/+HHUf21F4KP6w2ujbFESOgCZmoyou0gBDEAtT4xbYvwnplZwSbYaKHQQhunyzesfqtdF8bY9+0jVsfj0+e7tzofoURb+d6HMtbJxDnIg4VUzmI2QHEUhNJzT1nAS2cbVieP/kGzmLQ9TKGhYdTUnWVa16oXZz+qkeo=</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIFqDCCA5ACFAQ5F+2NYAb4N/enCOaeA/V2IVPYMA0GCSqGSIb3DQEBCwUAMIGNMQswCQYDVQQGEwJVUzELMAkGA1UECAwCVVMxCzAJBgNVBAcMAk5ZMRQwEgYDVQQKDAtDb21tYW5kQ29tbTEUMBIGA1UECwwLQ29tbWFuZENvbW0xCzAJBgNVBAMMAkNDMSswKQYJKoZIhvcNAQkBFhxha2Fwb29yQGNvbW1hbmRjb21wYW5pZXMuY29tMB4XDTIyMDUwOTEyMDcyNVoXDTIzMDUwOTEyMDcyNVowgZIxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJVUzELMAkGA1UEBwwCTlkxFDASBgNVBAoMC0NvbW1hbmRDb21tMRQwEgYDVQQLDAtDb21tYW5kQ29tbTEQMA4GA1UEAwwHQ29tbWFuZDErMCkGCSqGSIb3DQEJARYcYWthcG9vckBjb21tYW5kY29tcGFuaWVzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKoMSbgGecTY5d6bNjCEnpX2YxY+QRNDT0v/zGpO23PZlt+fuaYHr+b0hP3R/3cCxVTmd36o6MNASjT/slq8aFTdk49sW/zwICNa5AtjRRBVHGOmNYz/ntbo1KIQzC1faoGg+1CiTBL/vflQN4EWMyKGg/GfyIeKLODmOzzx41U5Y/Bv22X8bMmnFgYtwBMh2c7X0hNysqUmkc81sJ5cDgcwNbU4tu+sLslxI+1iOmmDtZBfcYmn+5vU4lFy8XZIODqN0eWpOyvH+a6r6rhAe9vIPwjWv2oh1Ktw0HE3b4JIgk/Vk4YvHKeSTjeC0biue7EF1c0sexOac5KpRhaWNxY4tmHY0dxKT1I21ls7TFd1x9E9A+n5Ff+INb9O4V0OmTrJUiXHZ44vpOi2kv1zbf2lSVFXwF6fhcb1KqxP2XQUMr7Fs6P5JUuH9h1AB5Kf7pjcrza7jjWU3LFRQzwISnJZNV+g1f62VM1S3AHpn1YFaI49Kpn9k5kuqWgGPUlIJq+9tWIy19lL6D3vzYI4yzZR4sGp8NcdSlKR8FgMgfB3bU4DtP85agoEOQ5C2DKV8No3jLVEJ8qFMhn/kLzcDy9oWF7ZVQGUmYuNo0q5T9O4dXcX0alXx0PS9UKJwfw23sGfUenwLI9HZEAqK5f3UEaDnfFmhxlajIAMj2hPldODAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAL4Fr2RzIH0O4VJRgYQr1ASeSghzijkhSHpjmL3fCEVrS7WvYtgKr3gXC5tQ/8i3Immh2aqMmbmq1gk/885frCMW76e0I1ZRfSw0ktUa04WlftVLeg9lzqPEmNKwaxzSudcR+WdEnLKrm3DHXltXqXw2yuDzy1SWGeyfZ+Lq62geahBkGWn5YoQqsxbmvhFDZNtDvinUI0GsbmTEsv9oxdWGGfl0spg6y0cO5WW/8fTh6Jrpk2RjOWcx7wUChQbv/Wu4WH9S7g78/F5HSy5hQ2PTg/CVtFXt/zScj6pxXb0Uov4/PXE7zHyLPUqtOxgasov4FurDJZaxUVNE5hu9OVLiHmw0jyU0t6myON+B4+a6I2xeWDBe1EQJyK2G22yISN6o4UILD3YL3gnl/xZoe/64sKP59RU0Gxv3pyYTpCtp+DYR8xYWzN8hVG0KHlxSypkh0TwQ7a4H4x5LOXZ5Qinr1qHgjgJY9LCAYHfvX6i+pctsd+na8ZUEPKZH/ARFz1LDx0RS5RbZhsnyn7GkpJZEuUUeS+aOYkuFLlvBopcKnP8KmKXjjRmrhHaNhPhnK8cJgai377uNk87xUt2WnMankROZajIK+7M6xMTq5WgmmDPE16o6zp+mdn5I8RzglelDFtgCAf9Hz2vupH4v8fcg772fzIf277zZpl8RZc36</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature></saml2p:AuthnRequest>
However when i try to validate it using https://tools.chilkat.io/xmlDsigVerify.cshtml
I get valid signature but https://console.pingone.com/ says invalid signature so may be certificates is the one i suggest it is blocking something please suggest
My Latest code looks like
public XmlDocument SignXML(XmlDocument xml)
{
X509Certificate2 cert = new X509Certificate2(#"" + SPCertificateLocation, SPCertificatePassword);// , X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
// set key, signing algorithm, and canonicalization method
var signedXml = new SignedXml(xml.DocumentElement) { SigningKey = cert.GetRSAPrivateKey() };
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
//signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
// sign whole document using "SAML style" transforms
var reference = new Reference { Uri = "#"+_id };
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
signedXml.AddReference(reference);
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data keyInfoData = new KeyInfoX509Data(cert);
keyInfo.AddClause(keyInfoData);
signedXml.KeyInfo = keyInfo;
// create signature
signedXml.ComputeSignature();
// get signature XML element and add it as a child of the root element
//signedXml.GetXml();
//XmlElement xmlSignature = signedXml.GetXml();
//AssignNameSpacePrefixToElementTree(xmlSignature, "ds");
// Add prefix "ds:" to signature
XmlElement signature = signedXml.GetXml();
SetPrefix("ds", signature);
// Load modified signature back
signedXml.LoadXml(signature);
// this is workaround for overcoming a bug in the library
signedXml.SignedInfo.References.Clear();
//var reference2 = new Reference { Uri = "#" + _id };
//reference2.AddTransform(new XmlDsigEnvelopedSignatureTransform());
//reference2.AddTransform(new XmlDsigExcC14NTransform());
//signedXml.AddReference(reference2);
// Recompute the signature
signedXml.ComputeSignature();
string recomputedSignature = Convert.ToBase64String(signedXml.SignatureValue);
// Replace value of the signature with recomputed one
ReplaceSignature(signature, recomputedSignature);
// Append the signature to the XML document.
xml.DocumentElement.InsertAfter(xml.ImportNode(signature, true), xml.DocumentElement.FirstChild);
//xml.DocumentElement?.AppendChild(signedXml.GetXml());
return xml;
}
I was able to validate your XML with perl's XML::Sig after a few changes that I had to make to XML::Sig since it does not currently support SignedInfo without an ID.
https://tools.chilkat.io/xmlDsigVerify.cshtml also validates it fine so the issue is likely in your validation code that you did not show.
I'm using syncfusion pdf to for digitally signing pdf files. On their website, they have an example for external signing a PDF. I slightly changed the example so the certificate can be selected from the windows certificate store. The selected certificate requires a PIN, so that a dialog pop up. The function Signature_ComputeHast is plain C# code. How can I enter the PIN programmatically in the code below?
Original example: https://help.syncfusion.com/file-formats/pdf/working-with-digitalsignature#externally-sing-a-pdf-document
private static X509Certificate2 SelectCertificate()
{
// Selecte certificate from store
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection selectedCertificate = (X509Certificate2Collection)collection.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false);// (X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, true);
selectedCertificate = X509Certificate2UI.SelectFromCollection(
store.Certificates,
"Certficates",
"Select a certificate for signing the document",
X509SelectionFlag.SingleSelection);
return selectedCertificate[0];
}
void Signature_ComputeHash(object sender, PdfSignatureEventArgs arguments)
{
//Get the document bytes
byte[] documentBytes = arguments.Data;
SignedCms signedCms = new SignedCms(new ContentInfo(documentBytes), detached: true);
//Compute the signature using the specified digital ID file and the password
X509Certificate2 certificate = SelectCertificate();
var cmsSigner = new CmsSigner(certificate);
//Set the digest algorithm SHA256
cmsSigner.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");
signedCms.ComputeSignature(cmsSigner);
//Embed the encoded digital signature to the PDF document
arguments.SignedData = signedCms.Encode();
}
You will need to use the RSACryptoServiceProvider or RSACng cng (in .NET core) classes which support Hardware Security Modules to a greater level of granularity on Windows. You can create a new instance of the appropriate class with parameters to include the password as follows:
if(certificate.PrivateKey is RSACryptoServiceProvider rsa) {
if(rsa.CspKeyContainerInfo.HardwareDevice) {
CspParameters cspParams = new CspParameters(1, rsa.CspKeyContainerInfo.ProviderName,
rsa.CspKeyContainerInfo.UniqueKeyContainerName) {
KeyPassword = certPassword,
Flags = CspProviderFlags.NoPrompt
};
byte[] signedCMSBytes = new RSACryptoServiceProvider(cspParams).SignData(documentBytesDigest);
}
}
As far as I am aware you will need to create the hashed digest of documentBytes digest and put that in the in PKCS#7 along with the desired authenticated attributes yourself prior to signing. After that you will need to add any unauthenticated attributes to the CMS. Personally I do all that using Bouncy Castle. But we have to use the MS SSC to interact with the OS for access to the HSM. There is potentially a way to do it all with the SSC classes. Incidentally certPassword is a SecureString.
I am working with digital signature. We have to generate xml request and sign the request using private key. The private key to be taken from etoken which is non exportable. My findings showed that private key cannot be extracted when it is marked as non exportable. In this case, how can I sign the xml request. Please help.
Finally I got the solution. Took a while as the requirement was bit rare. This link https://www.codeproject.com/Articles/240655/Using-a-Smart-Card-Certificate-with-NET-Security-i helped me to get the solution. Please refer to below code. For SignXml() method, please refer this msdn link https://msdn.microsoft.com/en-us/library/ms229745(v=vs.110).aspx
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed);
// find cert by thumbprint
var foundCerts = store.Certificates.Find(X509FindType.Thumbprint, "12345", true);
if (foundCerts.Count == 0)
return;
var certForSigning = foundCerts[0];
store.Close();
// prepare password
var pass = new SecureString();
var passwordstring = "password";
var chararr = passwordstring.ToCharArray();
foreach (var i in chararr)
pass.AppendChar(i);
// take private key
var privateKey = certForSigning.PrivateKey as RSACryptoServiceProvider;
// make new CSP parameters based on parameters from current private key but throw in password
CspParameters cspParameters = new CspParameters(1,
privateKey.CspKeyContainerInfo.ProviderName,
privateKey.CspKeyContainerInfo.KeyContainerName,
new System.Security.AccessControl.CryptoKeySecurity(),
pass);
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(cspParameters);
XmlDocument xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(path);
// Sign the XML document.
SignXml(xmlDoc, rsaCryptoServiceProvider);
Currently, I am using the etoken (safenet), bouncy castle library and X509certificate2 to decrypt a p7m file.
I would like to decrypt the p7m byteArray through the Bouncy Castle library using a X509Ceritificate2 private key. I can retrieve the X509Ceritificate2 private key from the X509Store and the key is not null. I can utilize the private key when it is a RSACryptoServiceProvider object.
RSACryptoServiceProvider systemUserOnlyReadablePrivateKey = certificate.PrivateKey as RSACryptoServiceProvider;
However, when I tried to tranform the private key from RSACryptoServiceProvider object to other objects such as byte[] or AsymetricKeyParameter, An exception message "key not valid for use in specified state." has been shown.
AsymetricKeyParameter key = DotNetUtilities.GetKeyPair(cert.PrivateKey).Private; //Exception prompt
Since the certificates are stored in the eToken and automatically added in the X509Store when eToken plugin into computer and certificates removed when the eToken plugged out, I can not set the certificates as exportable.
Does Bouncy Castle API support decryption by using X509Ceritificate2 private key?
How can I transform the key into other object so that I can decrypt by the Bouncy Castle API.
Thanks.
The following is my source code.
byte[] p7mByte = p7mByteArray; //p7m to byte array
cmsEnvelopedData = new CmsEnvelopedDataParser(p7mByteArray);
RecipientInformationStore recipientInformationStore = cmsEnvelopedData.GetRecipientInfos();
RecipientInformation recipientInformation = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed);
var certificates = store.Certificates;
foreach (var certificate in certificates)
{
if (certificate.PrivateKey != null)
{
RecipientID recipientId = new RecipientID();
recipientId.SerialNumber = certificate.SerialNumber;
recipientId.Issuer = certificate.IssuerDN;
recipientInformation = recipientInformationStore.GetFirstRecipient(recipientId);
RSACryptoServiceProvider systemUserOnlyReadablePrivateKey = certificate.PrivateKey as RSACryptoServiceProvider;
CspParameters cspParameters = new CspParameters(systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.ProviderType, systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.ProviderName, systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.KeyContainerName)
{
Flags = CspProviderFlags.UseArchivableKey
};
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cspParameters);
csp = (RSACryptoServiceProvider)certificate.PrivateKey;
CmsTypedStream recData = null;
recData = recipientInformation.GetContentStream(DotNetUtilities.GetKeyPair(cert.PrivateKey).Private); //Exception prompt
}
}
I am using the following code to retrieve all certificates in my PC from an asp.net webapp. The certificates collection is empty, and I can't understand why.
I tried impersonating my own user account and I didn't succeed as well. What am I doing wrong?
var store = new X509Store(StoreLocation.CurrentUser); //StoreLocation.LocalMachine fails too
var certificates = store.Certificates;
foreach (var certificate in certificates)
{
var friendlyName = certificate.FriendlyName;
Console.WriteLine(friendlyName);
}
//original problem: fetch a single certificate by its subject name
X509Certificate2 clientCertificate = CertificateUtility.GetCertificate(StoreName.My, StoreLocation.CurrentUser, "CN=mypc.domainname"); //returns null :(
Add this line of code to the second line and see how it works:
store.Open(OpenFlags.ReadOnly);
and then this at the bottom :):
store.Close();
All in one ...
I have an apache server (xamp) with https. I access through https and c# (vs2010)
to a PHP upload page
Install the certificate from i.e in the personal folder certificate, for example.
To view the certicates run "certmgr.msc" , at least in win7
Listing the personal certificates
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates;
foreach (var certificate in certificates)
{
var friendlyName = certificate.FriendlyName;
var xname = certificate.GetName(); //obsolete
Console.WriteLine(friendlyName);
}
store.Close();
Find specific certificate
string certificateName = "CN=localhost"; //name found in the var xname
X509Store storex = new X509Store(StoreName.My, StoreLocation.CurrentUser);
storex.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificatesx =
storex.Certificates.Find(X509FindType.FindBySubjectName,
certificateName,
true);
X509Certificate certificatex = certificates[0];
storex.Close();
I can find certificates by ...
var certificateStore = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
certificateStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var certificateCollection = certificateStore.Certificates.Find(X509FindType.FindBySubjectName, "mycert.me.com",false);
certificateStore.Close();
var certificate = certificateCollection[0];
certificateCollection will have the certificates I care about ... if it is just one then I get first element in the collection.
Look in your certificate store(mmc/add/certificate snap-in/my user account/Certificates - Current User/Personal/Certificates) to see the subject name to make sure "CN=mypc.domainname" is whats actually on the cert.
"CN=mypc.domainname"
vs
"CN = mypc.domainname"
...etc