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