Generate Apple Passbook Coupon/S-MIME Signature in Visual Studio - c#

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;

Related

C# How to S/Mime in Asp.net 5 with Bouncy Castle instead of x509certificate2

I successfully used the below code to send an S/Mime e-mail on latest Windows Server, but it fails on 2012 Windows Server. I think 2012 does not support newer AES encryption.
How would I rewrite the following code to replace
System.Security.Cryptography.X509Certificates.X509Certificate2() with Bouncy Castle, also using MimeKit and Mailkit? I tried using older DES3 for 2012, but it fails with CryptoThrowHelper+WindowsCryptographicException: The specified network password is not correct. Exactly the same code runs just fine on Windows 10 on my developer machine. Ultimately I want this to run in a Linux container, plus I think Bouncy Castle is much more fun to type and say. :)
var message = new MimeMessage(); // Using MimeKit, MailKit
message.To.Add( new MailboxAddress("John Doe","jdoe#somewhere.com"));
message.From.Add( new MailboxAddress("Jane Doe","jndoe#somewhere.com"));
message.Headers.Add( "AS3-From", "PILM");
message.Headers.Add( "AS3-To", "SARS");
message.Date = DateTimeOffset.Now;
message.MessageId = "42";
message.Subject = "This is a subject";
message.Body = new TextPart("html") { Text = "This is a body" };
using (var context = new TemporarySecureMimeContext())
{ // TODO: Replace Microsoft Cryptography with BouncyCastle
var cert = new System.Security.Cryptography
.X509Certificates.X509Certificate2(
#"c:\security\smime.p12", "VeryCoolPassword",
System.Security.Cryptography.X509Certificates
.X509KeyStorageFlags.EphemeralKeySet);
var recip = new CmsRecipient(cert) {
EncryptionAlgorithms = new EncryptionAlgorithm[] {
EncryptionAlgorithm.TripleDes }
};
var recips = new CmsRecipientCollection();
recips.Add(recip);
message.Body = ApplicationPkcs7Mime.Encrypt(
context, recips, message.Body );
};
var client = new SmtpClient();
client.Connect("MySuperEmailServer", 465, true);
client.Authenticate("MySuperUserName", "VeryCoolPassword");
client.Send(message);
client.Disconnect(true);
client.Dispose();
There seems to be several parts to this question.
First, you want to know how to load a S/MIME certificate using BouncyCastle. Since, as Crypt32 pointed out, you aren't likely to need to load *.pfx (aka *.p12) files since you wouldn't have their private key, you'll likely need to load a *.cer (or *.crt) file:
using (var stream = File.OpenRead ("smime-recipient-certificate.cer")) {
var parser = new X509CertificateParser ();
var certificate = parser.ReadCertificate (stream);
}
MimeKit's CmsRecipient class will do this for you if you call the CmsRecipient .ctor that takes a fileName argument.
That said, it never hurts to know how to load it manually in case you maybe don't have it stored on disk somewhere (e.g. maybe a MemoryStream or something).
The CmsRecipient .ctor that takes an X509Certificate2 (that you are currently using) is also fine to use. MimeKit just converts the X509Certificate2 certificate into a BouncyCastle certificate internally.
That said, based on your conversation with Crypt32, it sounds like the issue you were having is with loading an X509Certificate2 from a .pfx file on Windows Server 2012? In which case, loading the certificate using BouncyCastle is probably the way to go.
If you need or want to know how to load a .pfx file using BouncyCastle, you can do that using the following logic:
// Note: BouncyCastle's X509Certificate class is named X509Certificate, not to be
// confused with System.Security.Cryptography.X509Certificates.X509Certificate.
X509Certificate LoadPfx (Stream stream, string password, out AsymmetricKeyParameter privateKey)
{
var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ());
foreach (string alias in pkcs12.Aliases) {
if (!pkcs12.IsKeyEntry (alias))
continue;
var chain = pkcs12.GetCertificateChain (alias);
if (chain.Length == 0)
continue;
var keyEntry = pkcs12.GetKey (alias);
if (!keyEntry.Key.IsPrivate)
continue;
privateKey = keyEntry.Key;
return chain[0].Certificate;
}
throw Exception ("Did not find a certificate with a private key.");
}
Note: I would probably suggest changing this:
message.MessageId = "42";
to this:
message.MessageId = MimeUtils.GenerateMessageId();
This will generate a random/unique message-id value using the correct syntax (The Message-Id header isn't a number, it's a xyz#abc.com type value).

bouncycastle How to determine first byte (recovery ID) for ECDSA signatures

I'm using BouncyCastle to sign transactions for the EOS blockchain. Similar to Bitcoin signatures, the signature needs to have a "recovery ID" as the 1st bytes. My code looks as follows:
var curve = SecNamedCurves.GetByName("secp256r1");
var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
var p = new Org.BouncyCastle.Math.BigInteger(privateKey);
var keyParameters = new ECPrivateKeyParameters(
new Org.BouncyCastle.Math.BigInteger(1,privateKey),
domain);
ISigner signer = SignerUtilities.GetSigner("SHA-256withPLAIN-ECDSA");
signer.Init(true, keyParameters);
signer.BlockUpdate(message, 0, message.Length);
var signature = signer.GenerateSignature();
This seems to work as expected but I'm unable to get the recovery ID.
Here's an example of how another library does this.
https://github.com/EOSIO/eosjs-ecc/blob/a806b93fbbccec8d38c0c02998d204ff2040a6ae/src/ecdsa.js#L199
Can someone explain how I can do that same using BouncyCastle please?
This was another post that seemed helpful, but wasn't specific to how to do this with BouncyCastle https://bitcoin.stackexchange.com/questions/83035/how-to-determine-first-byte-recovery-id-for-signatures-message-signing

Digital signature with iText7

I've encountered a problem when trying to implement digital signature with iText7. According to the documentation of iText and couple examples I need to implement IExternalSignature (here), like so: IExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm); but this is where I get the exception:
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert.PrivateKey).Private;
which basically means "Invalid key to use in the current state"(?).
Most of the examples are from older version of iText library and Java (I'm using C#) and I cant quite figute it out.
I'll be very grateful for any tips. Thanks!
EDIT:
Here's some example code just to replicate the exception:
static void Main(string[] args)
{
string output = "D:/Development/TestApp/testOutputMoje.pdf";
string input = "D:/Development/TestApp/testInput.pdf";
PdfReader reader = new PdfReader(input);
string digestAlgorithm = DigestAlgorithms.SHA256;
List<X509Certificate2> oCertChain = new List<X509Certificate2>();
//getting certificates from store
X509Store store = new X509Store(StoreName.My);
store.Open(OpenFlags.ReadOnly);
foreach (var oCert in store.Certificates)
{
oCertChain.Add(oCert);
}
store.Close();
//siginig with first certificate - just example
var cert = oCertChain[0];
//exception here:
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert.PrivateKey).Private;
X509Certificate[] bouncyCert = { Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(cert) };
StampingProperties stampProp = new StampingProperties();
stampProp.PreserveEncryption();
IExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm);
PdfSigner signer = new PdfSigner(reader, new FileStream(output, FileMode.Create), stampProp);
signer.SignDetached(signature, bouncyCert, null, null, null, 0, CryptoStandard.CADES);
reader.Close();
}
Couple more informations about the certificate (I cant show You any details unfortunately)
problem is in certificate. It has to be marked as "Exportable". I don't know how solve this case, but in my case I used own certificate this way:
var cert = new X509Certificate2();
cert.Import(File.ReadAllBytes(#"C:\temp\certificate.cer"), "password", X509KeyStorageFlags.Exportable);
This works.
You need to find the way to change your certificate flag. Maybe something with store.Open(OpenFlags.MaxAllowed), but this doesn't work.

DSA signature c#

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);

Include intermediate certificate in signature C#

I'm trying to sign a manifest file to be used in Passbook using c#. my code was doing fine until they released GM of iOS6. in this seed they want the signature to include the intemidiate certificate. Here's my code:
var dataToSign = System.IO.File.ReadAllBytes(filePathToSign);
ContentInfo contentInfo = new ContentInfo(new Oid("1.2.840.113549.1.7.2"), dataToSign);
var signerCert = new X509Certificate2(signerPfxCertPath, signerPfxCertPassword);
var signedCms = new SignedCms(contentInfo, true);
var signer = new CmsSigner(signerCert);
signer.IncludeOption = X509IncludeOption.ExcludeRoot;
signedCms.ComputeSignature(signer);
var myCmsMessage = signedCms.Encode();
return myCmsMessage;
I have the certificat einstalled on my machine but it doesn't work. any ideas what I'm missing?
As mentioned on comments (Thanks to Rudi):
Blockquote look here or here

Categories