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.
Related
Both the cert and the key file are of type string.
I tried using this:
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
rsaKey.ImportParameters(keyfile);
X509Certificate2 cert = new X509Certificate2(certfile);
cert.PrivateKey = rsaKey;
cert.Export(X509ContentType.Pkcs12, "xyz");
RestAsynchronicClient client = new RestAsynchronicClient(url, RestDataStandard.JSON, null, cert, logger);
Here I am getting conversion error in the second line. Conversion error from string to RSAParameter.
There's no automatic conversion from a file path, or file contents, to an RSAParameters; the RSACryptoServiceProvider is out of date and not recommended for new code, and the PrivateKey property on certificates is fully [Obsolete] in new versions of .NET.
With .NET 5+, this is easy:
byte[] pfxBytes;
using (X509Certificate2 cert = X509Certificate2.CreateFromPemFile(certFile, keyFile))
{
pfxBytes = cert.Export(X509ContentType.Pkcs12, pfxPwd);
}
Or, in the style closer to the code you've written:
byte[] pfxBytes;
using (X509Certificate2 cert = new X509Certificate2(certFile))
using (RSA key = RSA.Create())
{
key.ImportFromPem(File.ReadAllText(keyFile));
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(key))
{
pfxBytes = certWithKey.Export(X509ContentType.Pkcs12, pfxPwd);
}
}
Your reference snippet then goes on to ignore the PFX/PKCS12 output and pass the cert to RestAsynchronicClient. Because of some idiosyncrasies on Windows, that generally won't work if you load the cert from this style. But, if you load the PFX into a new X509Certificate2 object, that'll be in a slightly different state and everything'll be happy.
RestAsynchronicClient client = new RestAsynchronicClient(
url,
RestDataStandard.JSON,
null,
new X509Certificate2(pfxBytes, pfxPwd),
logger);
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
My project consists in signing a PDF file using PKS11 (usb smartcard).
What I need is:
1) Plug in the USB smart card (and let the OS load the certificate in CurrentUser store (Windows))
2) Call an application , no GUI needed, that sign a determined PDF with the loaded cert.
Step 2 would need the user to input the PIN of the smartcard. For now that is not a concern, I can statically set it for testing purposes.
The posts I have been reading are:
checking-for-the-accessibility-of-smart-card-private-keys-in-windows-10
find-certificate-on-smartcard-currently-on-reader
load-a-smart-card-or-other-private-certificate-in-cryptoserviceprovider-for-sign
how-do-i-sign-a-pdf-document-using-a-certificate-from-the-windows-cert-store
sign-pdf-with-itextsharp-5-3-3-and-usb-token
// Set up the PDF IO
PdfReader reader = new PdfReader(#"C:\Users\martin\Documents\tosign.pdf");
PdfStamper stamper = PdfStamper.CreateSignature(reader,
new FileStream(#"C:\Users\martin\Documents\SignedPdf.pdf", FileMode.Create),
'\0');
PdfSignatureAppearance sap = stamper.SignatureAppearance;
sap.Reason = "For no apparent reason";
sap.Location = "Place";
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2 cert = fcollection[0];
BcX509.X509CertificateParser cp = new BcX509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
cp.ReadCertificate(cert.RawData)
};
Console.WriteLine(cert);
X509Certificate2 signatureCert = new X509Certificate2(cert);
IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");
MakeSignature.SignDetached(sap, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
I get a runtime error
System.ArgumentException: 'Unknown encryption algorithm
System.Security.Cryptography.RSACng'
in the line
IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");
I read somewhere that externalSignature is to be implemented with custom code depending on the USB vendor. Though in all the posts mentioned here, it seems that line should work with no problems. I tried to change the algorithm to SHA-256. Also I have been digging Github code of IExternalSignature interface to try to get a grasp out of it.
In theory, I know the private key of the USB device will never be accessible, therefore I can not just try to sign with a "getprivatekey" method kind.
In practice, I am a MEAN stack and Python dev, I have never coded in 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;
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