I have the following code to sign my data before sending it (http):
internal static byte[] Encode(byte[] arMessage, string signerCert, string signerPassword)
{
X509Certificate2 cert = new X509Certificate2(signerCert, signerPassword);
//debug data
var msg = System.Text.ASCIIEncoding.ASCII.GetString(arMessage);
//--
ContentInfo contentInfo = new ContentInfo(arMessage);
SignedCms signedCms = new SignedCms(contentInfo, true); // <- true detaches the signature
CmsSigner cmsSigner = new CmsSigner(cert);
signedCms.ComputeSignature(cmsSigner);
byte[] signature = signedCms.Encode();
return signature;
}
I can see the signature after doing the following:
string sig = Convert.ToBase64String(bSignature) + MESSAGE_SEPARATOR;
//this will be included in the message:
bSignature = System.Text.ASCIIEncoding.ASCII.GetBytes(sig);
//debug data, see the signature:
string deb8 = System.Text.ASCIIEncoding.ASCII.GetString(bSignature);
//--
For example:
MIICvgYJKoZIhvcNAQcCoIICrzCCAqsCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCAbgwggG0MIIBXqADAgECAhAGicJ2MhB7tUZtG3QcdWDwMA0GCSqGSIb3DQEBBAUAMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTExMDUwMzEyMTQ0NVoXDTM5MTIzMTIzNTk1OVowDzENMAsGA1UEAxMEVGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuOm4jGHUzLPNww6sx7cZJJxjLIDL/rVVLOtDd1NeA4DZZM1+lKwLpDp3FrV1CA4NP7g3weTg6Y0Jb6X7CSQHnfHRzU8LLrHgp/D9NJ39/RhsKggUteMa1FUUas0fMDMELtTO07ejNfYAhDYQiXmeg+pIMpUfeUhMicyV2xrPzG8CAwEAAaNLMEkwRwYDVR0BBEAwPoAQEuQJLQYdHU8AjWEh3BZkY6EYMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5ghAGN2wAqgBkihHPuNSqXDX0MA0GCSqGSIb3DQEBBAUAA0EAVIjI0My490lY6vKrcgorZZ8MBo3MSk9HuDRBE0SRwSQjnvPsAD0ol1ZjvzjFKA/0Vgvp1lyYquCTSL8R/uWf+TGBzzCBzAIBATAqMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5AhAGicJ2MhB7tUZtG3QcdWDwMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEgYCuXG2CJ0G6UaO1AVMpo5ul3JkJ9EvJbdwb6sXOqOonq5qKbaGJsx0sgwoBfg3lLMP3UI9JVi4dTGG4KTzA1k/0Qt0owmEPKrDh3zVZr2hK/iHsW75sGjSgYT4hxbn6ziQ2IFoN2qCbxdXrMZ+TD0pf7o/ABdorjlvQ5USwlPKjZQ==
This is what I have in the received message.
So the question is: how to validate the signature of the received message on the recipient (has the .cer file provided)?
Thanks in advance
Edit 1:
I tried to follow the Daniel Hilgarth's logic but it didn't worked.
A few times I met the "ASN Bad tag value" exception.
To make it easier, I hardcoded the message being used to generate the signature
So, on the receiver I have 2 things: the original message and the signature generated for it:
//Signature from the message (string in ASCII)
var signatureKey = GetSignatureFromSignatureMessage(signatureMessage, boundary);
//Original sent message (the arMessage itself used in Encode method above, converted to string from byte)
var messageOriginal =
"Content-Type: application/EDIFACT\r\nContent-Transfer-Encoding: binary\r\n\r\nSome short text.\r\nVery short.";
I need to check if the signature corresponds this message.
So I am trying to do something like this:
//contentInfo from the original message.
ContentInfo contentInfo = new ContentInfo(System.Text.ASCIIEncoding.ASCII.GetBytes(messageOriginal));
//SingedCms from the contentInfo above
SignedCms signedCms = new SignedCms(contentInfo, true);
//Here, I believe, I am attaching the signature I have to the Cms
signedCms.Decode(System.Text.ASCIIEncoding.ASCII.GetBytes(signatureKey));
//checking?
signedCms.CheckSignature(true);
And I get exceptions on decode part.
Any advices?
Edit 2:
Solution:
The direction given by Daniel Hilgarth is right.
My problem was that the sender encoded the key few times:
Base64 byte array -> Base64String -> ASCII byte array -> ASCII string -> Send_message
The receiver was receiving the data in ASCII already, doing:
ASCII String -> Byte array.
I had to convert everything back to base64 byte array to make it work.
//Signature from the message (ASCII String)
var signatureKey = GetSignatureFromSignatureMessage(signatureMessage, boundary);
//Original Byte Array (Base64)
var sigKeyBase = Convert.FromBase64String(signatureKey);
//Original sent message
var messageOriginal =
"Content-Type: application/EDIFACT\r\nContent-Transfer-Encoding: binary\r\n\r\nSome short text.\r\nVery short.";
var messageOriginalByteASCII = System.Text.ASCIIEncoding.ASCII.GetBytes(messageOriginal);
ContentInfo contentInfo = new ContentInfo(messageOriginalByteASCII);
SignedCms signedCms = new SignedCms(contentInfo, true);
signedCms.Decode(sigKeyBase);
signedCms.CheckSignature(true);
In this case it passes Check.
P.S. Too bad ChekSignature doesn't return true or false. I'd be more comfortable imho. :(
Hm... maybe using SignedCms.CheckSignature?! Use it in conjunction with SignedCms.Decode. Basically, just use the reverse way you use to sign the document. An example with detached signature is available from the MSDN page:
// Create a ContentInfo object from the inner content obtained
// independently from encodedMessage.
ContentInfo contentInfo = new ContentInfo(innerContent);
// Create a new, detached SignedCms message.
SignedCms signedCms = new SignedCms(contentInfo, true);
// encodedMessage is the encoded message received from
// the sender.
signedCms.Decode(encodedMessage);
// Verify the signature without validating the
// certificate.
signedCms.CheckSignature(true);
Related
I need to sign a lot of documents and this code works. The problem is that every time I see the form for entering the password.
How to transfer the password (PIN) in the form at the signature by means of the certificate which is on a flash drive?
INFO: can't export the private key from a flash drive (the key market is not exported)
static public byte[] SignToken(Byte[] tokenToSignBytes, X509Certificate2 signerCert)
{
// Place message in a ContentInfo object.
// This is required to build a SignedCms object.
ContentInfo contentInfo = new ContentInfo(tokenToSignBytes);
// Instantiate SignedCms object with the ContentInfo above.
// Has default SubjectIdentifierType IssuerAndSerialNumber.
// Has default Detached property value false, so message is
// included in the encoded SignedCms.
SignedCms signedCms = new SignedCms(contentInfo, true);
// Formulate a CmsSigner object for the signer.
CmsSigner cmsSigner = new CmsSigner(signerCert);
Console.WriteLine(cmsSigner);
// Sign the CMS/PKCS #7 message.
Console.Write("Computing signature with signer subject name {0} ... ", signerCert.SubjectName.Name);
signedCms.ComputeSignature(cmsSigner, false);
//signedCms.ComputeSignature(cmsSigner, true); // it's not working
Console.WriteLine("Done.");
// Encode the CMS/PKCS #7 message.
return signedCms.Encode();
}
Instead of passing the PIN, I would refactor the code to define cmsSigner variable outside and only once. Then pass this parameter to SignToken method. Then you will be prompted for PIN only once when access the cmsSigner variable for the first time. Then you reuse the instance and no prompts should appear when you call SignToken method multiple times using same cmsSigner instance.
CertificateI have a Certificate
This is the text i have to verify:
B5080F731EE89EC82FD2E8B22E9_I_CANNOT_SHOW_THE_REAL_TEXT
This is the signed:
MIIBUwYJKoZIhvcNAQcCoIIBRDCCAUACAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHATGCAR8wggEbAgEBMG8wZDELMAkGA1UEBhMCREUxHDAaBgNVBAoTE1NBUCBUcnVzdCBDb21tdW5pdHkxEzARBgNVBAsTClNBUCBXZWIgQVMxFDASBgNVBAsTC0kwMDIwMjEyMzYwMQwwCgYDVQQDEwNFMTUCByASBQYIEQgwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE4MDYyNzE5MzcyNVowIwYJKoZIhvcNAQkEMRYEFDgpp0877pKaChyIGVw5sPeD0W03MAkGByqGSM44BAMEMDAuAhUA4PH8bdBPHHtuPHvhJxjei%2BFrJYUCFQCnZ6IABDiRlctS9E9N3IQK60JLIg%3D%3D
CanĀ“t find a way to do verify the signature with c#. When i use the "normal" DSACryptoServiceProvider I always get the error saying the signature size should be 40 bytes.
I just need to know were to go. wath to use
I know is DSA.
I know the signature is around 500bytes
this is the code i'm trying:
DSACryptoServiceProvider csp = (DSACryptoServiceProvider)CurrentCer.csp.PublicKey.Key;
SHA1Managed sha1 = new SHA1Managed();
byte[] data = Encoding.UTF8.GetBytes(ToSign);
byte[] hash = sha1.ComputeHash(data);
var base64EncodedBytes = System.Convert.FromBase64String(signature);
result = csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), base64EncodedBytes);
DSASignatureDeformatter verifier = new DSASignatureDeformatter(csp);
verifier.SetHashAlgorithm("SHA1");
bool valid = verifier.VerifySignature(hash, base64EncodedBytes);
Your data isn't a signature, per se. It's a query-string-encoded base64-encoded representation of a CMS Signed-Data with detached content, and it happens to have been signed with DSA.
str = Uri.UnescapeDataString(str);
byte[] signatureMessage = Convert.FromBase64String(str);
ContentInfo content = new ContentInfo(yourDataHere);
SignedCms signedCms = new SignedCms(content, detached: true);
signedCms.Decode(signatureMessage);
SignerInfoCollection signers = signedCms.SignerInfos;
if (signers.Count != 1 || signers[0].Certificate != null)
{
// Reject it, this isn't what you're looking for.
// At least, based on the sample you gave.
//
// You could, for Count == 1, accept Certificate == null or
// Certificate.RawData.SequenceEqual(CurrentCer.RawData),
// if you're so inclined.
}
// This throws if the signature doesn't check out.
signedCms.CheckSignature(new X509Certificate2Collection(CurrentCer), verifySignatureOnly: true);
Dears
Please help me to understand non detached signature validation process.
I've used sample code (slightly modified) from SignedCms.ComputeSignature and CheckSignature documentation.
Here it is
[TestMethod]
public void CheckSignaturePlain()
{
var x509 = new X509Helper(LogManager.GetLogger("x509"));
X509Certificate2 certificate = x509.GetCertificate("XML_SIGN_TEST_CERT");
var str = "quick brown fox jumps over lazy dog";
var data = Encoding.ASCII.GetBytes(str);
detached = false; // tests are passed for detached signatures only
using (var sha256 = new SHA256CryptoServiceProvider()) // One thing though, Microsoft recommends that you avoid using any managed classes. You should change SHA256Managed to SHA256CryptoServiceProvider. https://blogs.msdn.microsoft.com/winsdk/2015/11/14/using-sha256-with-the-signedxml-class/
{
var hash = sha256.ComputeHash(data);
var signature = Sign(hash, certificate);
SignCheck(hash, signature);
try
{
data = data.Skip(1).ToArray(); // lets change the data to get broken signature
var hash2 = sha256.ComputeHash(data);
SignCheck(hash2, signature);
Assert.Fail("signature verification should fail");
}
catch (CryptographicException ce)
{
TestContext.WriteLine(ce.Message);
}
}
}
byte[] Sign(byte[] hash, X509Certificate2 cert)
{
ContentInfo contentInfo = new ContentInfo(hash);
SignedCms signedCms = new SignedCms(contentInfo, detached);
CmsSigner cmsSigner = new CmsSigner(cert);
cmsSigner.IncludeOption = X509IncludeOption.WholeChain;
signedCms.ComputeSignature(cmsSigner);
byte[] cmsMessage = signedCms.Encode();
return cmsMessage;
}
bool detached;
void SignCheck(byte[] hash, byte[] signature)
{
var contentInfo2 = new ContentInfo(hash);
var signedCms = new SignedCms(contentInfo2, detached);
signedCms.Decode(signature);
signedCms.CheckSignature(true);
}
However it works properly (from my point of view) only for detached signatures.
If detached = false is set then signature verification does not fail for changed data.
It happens because data signed were included to the signature object and SignedCms.CheckSignature ignores hash that was calculated from changed data.
Is it possible to use non-detached signature and get signedCms.CheckSignature to take into account hash that was calculated from changed data?
Or I should extract signed data from non-detached signature, calculate hash over data and compare them manually?
I'd like to use non-detached signature. Signed data from the signature (hash actually) should be used as message identifier on abstraction layer that has no idea how to calculate hash for different kind of objects.
In non-detached mode the content is within the payload, so during Decode the ContentInfo you provided is discarded and the one from within the payload is used instead.
if (!this.Detached) {
Oid contentType = PkcsUtils.GetContentType(m_safeCryptMsgHandle);
byte[] content = PkcsUtils.GetContent(m_safeCryptMsgHandle);
m_contentInfo = new ContentInfo(contentType, content);
}
(from SignedCms.Decode on referencesource)
In detached signature mode the SignedCms data represents a signature (and metadata) over some data you have to provide. Using standard cryptographic signature behaviors it takes the input data, hashes it, and performs a private key operation on the hash to generate the signature.
In embedded (non-detached) signature mode the SignedCms data is a single blob which contains the data as well as the signature (and metadata). So sending the CMS object in non-detached mode replaces sending your original data (because the original data is now embedded).
Unless the thing you want to transport is actually just the hash, you should feed SignedCms the actual data value, since it hashes it itself as part of computing the signature.
I am trying to create a Apple wallet passes in my system, after reading Different S/MIME signature between OpenSSL and C# and Apple Passbook coupons from C#, the system can create .pkasss automatically now.
And my problem is signature cannot create successfully in actual. If I using iPhone and try to open the .pkpass file, it can't be open!! I find out that is the problem is coming form signature, if I using mac to create a signature in terminal, it create a 3326bytes size signature; my code only can create a 3002 bytes file, which means the signature must be miss something.
Does Mac OS X method have a big difference between Windows OS method?
Has anyone faced this problem before? Does anyone know why the signatures are different?
Does anyone know how t fix it?
This is my source code:
var cert = new X509Certificate2(assetsFolder + p12File, p12Password);
var buffer = File.ReadAllBytes(Path.Combine(assetsFolder, "manifest.json"));
var cont = new ContentInfo(buffer);
var cms = new SignedCms(cont, true);
var signer = new CmsSigner(cert)
{
IncludeOption = X509IncludeOption.ExcludeRoot,
SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber,
};
cms.ComputeSignature(signer, true);
var myCmsMessage = cms.Encode();
File.WriteAllBytes(Path.Combine(assetsFolder, "signature"), myCmsMessage);
Big Thanks!!!
----------------------------------UPDATE---------------------------------
I found the ans of signature!!!
The setting of OID and SignerIdentifierType will affert the signature
Here is my solution:
byte[] buffer = File.ReadAllBytes(Path.Combine(assetsFolder, "manifest.json"));
X509Certificate2 cert = new X509Certificate2(assetsFolder + p12File, p12Password);
var oid = new Oid("1.2.840.113549.1.7.2");
ContentInfo contentInfo = new ContentInfo(oid, buffer);
SignedCms signedCms = new SignedCms(contentInfo, true);
var cmsSigner = new CmsSigner(cert);
cmsSigner.IncludeOption = X509IncludeOption.ExcludeRoot;
cmsSigner.SignedAttributes.Add(new Pkcs9SigningTime(DateTime.Now));
cmsSigner.SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier;
signedCms.ComputeSignature(cmsSigner);
byte[] myCmsMessage = signedCms.Encode();
return myCmsMessage;
I am pretty much new to cryptographic. So far I am successful to generate an enveloped CMS file. But after it, I need to encode a CMS Signed and encrypted data file using ASN.1 DER format but don't know how to? please provide me some suggestion.
below I paste my code for more light;
var content = File.ReadAllBytes(#"E:\Miscellaneous\Pacs.zip");
var contentInfo1 = new ContentInfo(content);
var signedCms = new SignedCms(contentInfo1);
var cmsSigner = new CmsSigner(GetSignerCert()) { IncludeOption = X509IncludeOption.None };
signedCms.ComputeSignature(cmsSigner);
var contentInfo = new ContentInfo(signedCms.Encode());
// RSA_DES_EDE3_CBC.
var envelopedCms = new EnvelopedCms(contentInfo);
var recip1 = new CmsRecipient(
SubjectIdentifierType.IssuerAndSerialNumber,
GetRecipientCert());
envelopedCms.Encrypt(recip1);
File.WriteAllBytes(#"E:\signedfile", envelopedCms.Encode());
// need code to encode signedfile using ASN.1 DER
EnvelopedCms.Encode() is specified to create bytes, but the only way to do that is to use an encoding method. It is about 99% certain that that encoding is ASN.1 BER or DER. So it is extremely likely that you've already accomplished your goal.