The following question and answer on StackOverflow show how to generate a PDF that cannot be opened without the appropriate password.
Password protected PDF using C#
I would like to use this framework similarly, but slightly altered to allow my users to "open" the PDF without needing the password, but only allow them to EDIT the PDF if they have the password.
Is that possible with iTextSharp?
if this matters, I am working in C# 4.0 within a WF 4.0 custom activity.
Yes, there are two passwords that you can pass to PdfEncryptor.Encrypt(), userPassword and ownerPassword. Just pass null to the userPassword and people will be able to open it without specify a password.
string WorkingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string InputFile = Path.Combine(WorkingFolder, "Test.pdf");
string OutputFile = Path.Combine(WorkingFolder, "Test_enc.pdf");
using (Stream input = new FileStream(InputFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (Stream output = new FileStream(OutputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
PdfReader reader = new PdfReader(input);
PdfEncryptor.Encrypt(reader, output, true, null, "secret", PdfWriter.ALLOW_SCREENREADERS);
}
}
Another implementation:
public static void Common_PassWordProtectPDF_Static_WithoutEmail(FileInfo[] filteredfiles, string strAgentName, string strAgentCode, string strpassword, string strEmailID, string sourcefolder, string strdestfolder, string strdestinationFileName)
{
foreach (FileInfo file in filteredfiles)
{
//string sourcePdf = Convert.ToString(ConfigurationManager.AppSettings["SourceFolder"]) + "\\" + file.Name;
//string strdestPdf = Convert.ToString(ConfigurationManager.AppSettings["DestinationFolder"]) + file.Name;
string sourcePdf = sourcefolder + "\\" + file.Name;
string strdestPdf = strdestfolder + strdestinationFileName;
using (Stream input = new FileStream(sourcePdf, FileMode.Open, FileAccess.Read, FileShare.Read))
{
//sourcePdf unsecured PDF file
//destPdf secured PDF file
using (Stream output = new FileStream(strdestPdf, FileMode.Create, FileAccess.Write, FileShare.None))
{
PdfReader pdfReader = new PdfReader(input);
X509Store store = new X509Store("My");
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = new X509Certificate2();
RSACryptoServiceProvider csp = null;
AcroFields fields = pdfReader.AcroFields;
foreach (X509Certificate2 mCert in store.Certificates)
{
//TODO's
string strresult = mCert.GetName();
bool str123 = false;
if (strresult.Contains("Certificate name") == true)
{
csp = (RSACryptoServiceProvider)mCert.PrivateKey;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(file.Name);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
byte[] signature = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
if (Verify(file.Name, signature, mCert))
{
char s = pdfReader.PdfVersion;
//var pdfStamper = PdfStamper.(pdfReader, output, s, #"\0", true);
//csp.SignData(signature, true);
pdfReader.Appendable = false;
Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
cp.ReadCertificate(mCert.RawData)};
IExternalSignature externalSignature = new X509Certificate2Signature(mCert, "SHA-1");
// var signedPdf = new FileStream(output, FileMode.Create);
// var signedPdf = PdfEncryptor.Encrypt(pdfReader, output, true, strpassword, strpassword, PdfWriter.ALLOW_PRINTING);
//char s = pdfReader.PdfVersion;
var pdfStamper = PdfStamper.CreateSignature(pdfReader, output, s, #"\", false);
PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;
byte[] USER = Encoding.ASCII.GetBytes("userpwd");
byte[] OWNER = Encoding.ASCII.GetBytes(strpassword);
Rectangle cropBox = pdfReader.GetCropBox(1);
float width = 108;
float height = 32;
// signatureAppearance.SignatureGraphic = Image.GetInstance("C:\\logo.png");
//signatureAppearance.Layer4Text = "document certified by";
//signatureAppearance.Reason = "Because I can";
//signatureAppearance.Location = "My location";
//signatureAppearance.SetVisibleSignature(new Rectangle(100, 100, 250, 150), pdfReader.NumberOfPages, "Signature");
Rectangle rect = new Rectangle(600, 100, 300, 150);
Chunk c = new Chunk("A chunk represents an isolated string. ");
rect.Chunks.Add(c);
//signatureAppearance.SetVisibleSignature(new Rectangle(100, 100, 600, 150), pdfReader.NumberOfPages, "Signature");
signatureAppearance.SetVisibleSignature(rect, pdfReader.NumberOfPages, "Signature");
// signatureAppearance.SetVisibleSignature(new Rectangle(cropBox.GetLeft(0), cropBox.GetBottom(0), cropBox.GetLeft(width), cropBox.GetLeft(height)), pdfReader.NumberOfPages, "Signature");
signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
pdfStamper.SetEncryption(USER, OWNER, PdfWriter.AllowPrinting, PdfWriter.ENCRYPTION_AES_128);
MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
pdfStamper.Close();
// PdfEncryptor.Encrypt(pdfReader, output, true, strpassword, strpassword, PdfWriter.SIGNATURE_EXISTS);
}
else
{
Console.WriteLine("ERROR: Signature not valid!");
}
}
}
string Password = strpassword;
}
}
}
public static byte[] Sign(string text, string certSubject)
{
// Access Personal (MY) certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("No valid cert was found");
}
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
static bool Verify(string text, byte[] signature, X509Certificate2 cert)
{
// Load the certificate we’ll use to verify the signature from a file
// X509Certificate2 cert = new X509Certificate2(certPath);
// Note:
// If we want to use the client cert in an ASP.NET app, we may use something like this instead:
// X509Certificate2 cert = new X509Certificate2(Request.ClientCertificate.Certificate);
// Get its associated CSP and public key
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
Related
Kindly help me to resolve this issue. It is showing error shows cannot access the closed stream.
I am using memory stream from RLDC and watermarked first then the process of digital sign begin.
I think there is an issue in the last 6 lines or maybe I am not closing the variables properly.
What am I doing wrong here?
public static byte[] AddWatermark(byte[] bytes, BaseFont baseFont, string watermarkText)
{
using (var ms = new MemoryStream(10 * 1024))
{
using (var reader = new PdfReader(bytes))
using (var stamper = new PdfStamper(reader, ms))
{
var pages = reader.NumberOfPages;
for (var i = 1; i <= pages; i++)
{
var dc = stamper.GetOverContent(i);
AddWaterMarkText(dc, watermarkText, baseFont, 100, 45, BaseColor.GRAY, reader.GetPageSizeWithRotation(i));
// Keypair Generator
RsaKeyPairGenerator kpGenerator = new RsaKeyPairGenerator();
kpGenerator.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
// Create a keypair
AsymmetricCipherKeyPair kp = kpGenerator.GenerateKeyPair();
// Certificate Generator
X509V3CertificateGenerator cGenerator = new X509V3CertificateGenerator();
cGenerator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
cGenerator.SetSubjectDN(new X509Name("CN=" + "abc.com"));
cGenerator.SetIssuerDN(new X509Name("CN=" + "Aalok"));
cGenerator.SetNotBefore(DateTime.Now);
cGenerator.SetNotAfter(DateTime.Now.Add(new TimeSpan(365, 0, 0, 0))); // Expire in 1 year
cGenerator.SetSignatureAlgorithm(HashType.SHA256withRSA.ToString()); // See the Appendix Below for info on the hash types supported by Bouncy Castle C#
cGenerator.SetPublicKey(kp.Public); // Only the public key should be used here!
Org.BouncyCastle.X509.X509Certificate cert = cGenerator.Generate(kp.Private); // Create a self-signed cert
byte[] encoded = cert.GetEncoded();
try
{
FileStream outStream = new FileStream("abc.der", FileMode.Create, FileAccess.ReadWrite);
{
outStream.Write(encoded, 0, encoded.Length);
}
}
catch (FileNotFoundException)
{
Console.WriteLine("File not found");
}
// Create the PKCS12 store
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
// Add a Certificate entry
X509CertificateEntry certEntry = new X509CertificateEntry(cert);
store.SetCertificateEntry(cert.SubjectDN.ToString(), certEntry); // use DN as the Alias.
// Add a key entry
AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(kp.Private);
store.SetKeyEntry(cert.SubjectDN.ToString() + "_key", keyEntry, new X509CertificateEntry[] { certEntry }); // Note that we only have 1 cert in the 'chain'
// Save to the file system
try
{
var filestream = new FileStream(#"abc.pfx", FileMode.Create, FileAccess.ReadWrite);
{
store.Save(filestream, "123".ToCharArray(), new SecureRandom());
}
}
catch (FileNotFoundException)
{
Console.WriteLine("File not found");
}
String alias = "";
ICollection<Org.BouncyCastle.X509.X509Certificate> chain = new List<Org.BouncyCastle.X509.X509Certificate>();
// searching for private key
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
foreach (X509CertificateEntry c in store.GetCertificateChain(alias).ToList())
{
chain.Add(c.Certificate);
}
RsaPrivateCrtKeyParameters parameters = pk.Key as RsaPrivateCrtKeyParameters;
PdfReader read = new PdfReader(bytes);
// PdfStamper.CreateSignature(read, ms, '\0');
PdfStamper stamp = PdfStamper.CreateSignature(read, ms, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamp.SignatureAppearance;
appearance.Reason = "My reason for signing";
appearance.Location = "The middle of nowhere";
// appearance.SignDate = DateTime.Now;
appearance.SetVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(parameters, DigestAlgorithms.SHA256);
MakeSignature.SignDetached(appearance, pks, chain, null, null, null, 0, CryptoStandard.CMS);
stamp.Close();
read.Close();
break;
}
//stamper.Close();
reader.Close();
ms.Close();
}
return ms.ToArray();
}
}
we are trying to encrypt the code with RSA/ECB/PKCs1 code with .cert publickey but we are failing and getting an error "not a valid RSAKey"
public string RsaEncryptWithPublic(string clearText, string publicKey)
{
publicKey = GetCertificate().PublicKey.Key.ToXmlString(false);
var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
var encryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(publicKey))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
The following question and answer on StackOverflow show how to generate a PDF that cannot be opened without the appropriate password.
Password protected PDF using C#
I would like to use this framework similarly, but slightly altered to allow my users to "open" the PDF without needing the password, but only allow them to EDIT the PDF if they have the password.
Is that possible with iTextSharp?
if this matters, I am working in C# 4.0 within a WF 4.0 custom activity.
Yes, there are two passwords that you can pass to PdfEncryptor.Encrypt(), userPassword and ownerPassword. Just pass null to the userPassword and people will be able to open it without specify a password.
string WorkingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string InputFile = Path.Combine(WorkingFolder, "Test.pdf");
string OutputFile = Path.Combine(WorkingFolder, "Test_enc.pdf");
using (Stream input = new FileStream(InputFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (Stream output = new FileStream(OutputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
PdfReader reader = new PdfReader(input);
PdfEncryptor.Encrypt(reader, output, true, null, "secret", PdfWriter.ALLOW_SCREENREADERS);
}
}
Another implementation:
public static void Common_PassWordProtectPDF_Static_WithoutEmail(FileInfo[] filteredfiles, string strAgentName, string strAgentCode, string strpassword, string strEmailID, string sourcefolder, string strdestfolder, string strdestinationFileName)
{
foreach (FileInfo file in filteredfiles)
{
//string sourcePdf = Convert.ToString(ConfigurationManager.AppSettings["SourceFolder"]) + "\\" + file.Name;
//string strdestPdf = Convert.ToString(ConfigurationManager.AppSettings["DestinationFolder"]) + file.Name;
string sourcePdf = sourcefolder + "\\" + file.Name;
string strdestPdf = strdestfolder + strdestinationFileName;
using (Stream input = new FileStream(sourcePdf, FileMode.Open, FileAccess.Read, FileShare.Read))
{
//sourcePdf unsecured PDF file
//destPdf secured PDF file
using (Stream output = new FileStream(strdestPdf, FileMode.Create, FileAccess.Write, FileShare.None))
{
PdfReader pdfReader = new PdfReader(input);
X509Store store = new X509Store("My");
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = new X509Certificate2();
RSACryptoServiceProvider csp = null;
AcroFields fields = pdfReader.AcroFields;
foreach (X509Certificate2 mCert in store.Certificates)
{
//TODO's
string strresult = mCert.GetName();
bool str123 = false;
if (strresult.Contains("Certificate name") == true)
{
csp = (RSACryptoServiceProvider)mCert.PrivateKey;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(file.Name);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
byte[] signature = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
if (Verify(file.Name, signature, mCert))
{
char s = pdfReader.PdfVersion;
//var pdfStamper = PdfStamper.(pdfReader, output, s, #"\0", true);
//csp.SignData(signature, true);
pdfReader.Appendable = false;
Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
cp.ReadCertificate(mCert.RawData)};
IExternalSignature externalSignature = new X509Certificate2Signature(mCert, "SHA-1");
// var signedPdf = new FileStream(output, FileMode.Create);
// var signedPdf = PdfEncryptor.Encrypt(pdfReader, output, true, strpassword, strpassword, PdfWriter.ALLOW_PRINTING);
//char s = pdfReader.PdfVersion;
var pdfStamper = PdfStamper.CreateSignature(pdfReader, output, s, #"\", false);
PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;
byte[] USER = Encoding.ASCII.GetBytes("userpwd");
byte[] OWNER = Encoding.ASCII.GetBytes(strpassword);
Rectangle cropBox = pdfReader.GetCropBox(1);
float width = 108;
float height = 32;
// signatureAppearance.SignatureGraphic = Image.GetInstance("C:\\logo.png");
//signatureAppearance.Layer4Text = "document certified by";
//signatureAppearance.Reason = "Because I can";
//signatureAppearance.Location = "My location";
//signatureAppearance.SetVisibleSignature(new Rectangle(100, 100, 250, 150), pdfReader.NumberOfPages, "Signature");
Rectangle rect = new Rectangle(600, 100, 300, 150);
Chunk c = new Chunk("A chunk represents an isolated string. ");
rect.Chunks.Add(c);
//signatureAppearance.SetVisibleSignature(new Rectangle(100, 100, 600, 150), pdfReader.NumberOfPages, "Signature");
signatureAppearance.SetVisibleSignature(rect, pdfReader.NumberOfPages, "Signature");
// signatureAppearance.SetVisibleSignature(new Rectangle(cropBox.GetLeft(0), cropBox.GetBottom(0), cropBox.GetLeft(width), cropBox.GetLeft(height)), pdfReader.NumberOfPages, "Signature");
signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
pdfStamper.SetEncryption(USER, OWNER, PdfWriter.AllowPrinting, PdfWriter.ENCRYPTION_AES_128);
MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
pdfStamper.Close();
// PdfEncryptor.Encrypt(pdfReader, output, true, strpassword, strpassword, PdfWriter.SIGNATURE_EXISTS);
}
else
{
Console.WriteLine("ERROR: Signature not valid!");
}
}
}
string Password = strpassword;
}
}
}
public static byte[] Sign(string text, string certSubject)
{
// Access Personal (MY) certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("No valid cert was found");
}
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
static bool Verify(string text, byte[] signature, X509Certificate2 cert)
{
// Load the certificate we’ll use to verify the signature from a file
// X509Certificate2 cert = new X509Certificate2(certPath);
// Note:
// If we want to use the client cert in an ASP.NET app, we may use something like this instead:
// X509Certificate2 cert = new X509Certificate2(Request.ClientCertificate.Certificate);
// Get its associated CSP and public key
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
I am trying to sign a PDF Document using two web services in two servers. But it is showing "Document has been altered or corrupt since it was signed" in Adobe Reader. Can anybody suggest what is wrong in following code.
PROCEDURE
1. Web service (WS) on Server A, Generate hash from PDF and sent to WS on Server B for signing.
2. WS on Server B signs hash.
3. WS on Server A receives signed hash and Embed in PDF document.
CODE
GENERATE HASH
private PDFHashData generateHash(byte[] content, string userName)
{
PdfReader reader = new PdfReader(content);
MemoryStream ms = new MemoryStream();
PdfStamper stamper = PdfStamper.CreateSignature(reader, ms, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.SetVisibleSignature(new Rectangle(500, 150, 400, 200), 1, signatureFieldName);
appearance.SignDate = DateTime.Now;
appearance.Reason = Reason;
appearance.Location = Location;
appearance.Contact = Contact;
StringBuilder buf = new StringBuilder();
buf.Append("Digitally signed by");
buf.Append("\n");
buf.Append(userName);
buf.Append("\n");
buf.Append("Date: " + appearance.SignDate);
appearance.Layer2Text = buf.ToString();
appearance.Acro6Layers = true;
appearance.CertificationLevel = 0;
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
byte[] hash = SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
StringBuilder hex = new StringBuilder(hash.Length * 2);
foreach (byte b in hash)
hex.AppendFormat("{0:x2}", b);
PDFHashData phData= new PDFHashData();
phData.Hash = hex.ToString();
phData.Content = Convert.ToBase64String(ms.ToArray());
return phData;
}
SIGN HASH
byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
private Stream getCertificate()
{
// Base 64 byte - PFX file with private key
return new MemoryStream(Convert.FromBase64String("..................................AgIEAA=="));
}
protected void Page_Load(object sender, EventArgs e)
{
Stream stream = Request.InputStream;
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
byte[] hash = StringToByteArray(Encoding.UTF8.GetString(buffer));
Pkcs12Store store = new Pkcs12Store(getCertificate(), "*******".ToCharArray());
String alias = "";
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
X509CertificateEntry[] chain = store.GetCertificateChain(alias);
List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
foreach (X509CertificateEntry en in chain)
{
c.Add(en.Certificate);
}
PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA1");
String hashAlgorithm = signature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, c, hashAlgorithm, false);
DateTime signingTime = DateTime.Now;
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
Response.Write(Convert.ToBase64String(sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS)));
}
EMBED SIGNATURE TO PDF
private byte[] signPDF(byte[] content, string userName, byte[] pk)
{
PdfReader reader = new PdfReader(content);
MemoryStream os = new MemoryStream();
IExternalSignatureContainer external = new MyExternalSignatureContainer(pk);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
return os.ToArray();
}
For those who are interested I am posting the answer.
I ended up using itextsharp 5.5.10. Code is given below,
Initialize PDF object
public PDFSigning(byte[] Content, string UserName)
{
content = Content;
reader = new PdfReader(content);
ms = new MemoryStream();
stamper = PdfStamper.CreateSignature(reader, ms, '\0');
appearance = stamper.SignatureAppearance;
userName = UserName;
}
private Stream getCertificate()
{
return new MemoryStream(Convert.FromBase64String("************************=="));
}
Generate hash
private string generateHash()
{
appearance.SetVisibleSignature(new Rectangle(500, 150, 400, 200), 1, signatureFieldName);
appearance.SignDate = DateTime.Now;
appearance.Reason = Reason;
appearance.Location = Location;
appearance.Contact = Contact;
StringBuilder buf = new StringBuilder();
buf.Append("Digitally signed by");
buf.Append("\n");
buf.Append(userName);
buf.Append("\n");
buf.Append("Date: " + appearance.SignDate);
appearance.Layer2Text = buf.ToString();
appearance.Acro6Layers = true;
appearance.CertificationLevel = 0;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
{
Date = new PdfDate(appearance.SignDate),
Name = userName
};
dic.Reason = appearance.Reason;
dic.Location = appearance.Location;
dic.Contact = appearance.Contact;
appearance.CryptoDictionary = dic;
Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
exclusionSizes.Add(PdfName.CONTENTS, (csize * 2) + 2);
appearance.PreClose(exclusionSizes);
HashAlgorithm sha = new SHA256CryptoServiceProvider();
Stream s = appearance.GetRangeStream();
int read = 0;
byte[] buff = new byte[0x2000];
while ((read = s.Read(buff, 0, 0x2000)) > 0)
{
sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);
StringBuilder hex = new StringBuilder(sha.Hash.Length * 2);
foreach (byte b in sha.Hash)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
Sign hash
public byte[] SignMsg(string hexhash)
{
byte[] hash = hexToByteArray(hexhash);
Pkcs12Store store = new Pkcs12Store(getCertificate(), "*********".ToCharArray());
String alias = "";
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
X509CertificateEntry[] chain = store.GetCertificateChain(alias);
List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
foreach (X509CertificateEntry en in chain)
{
c.Add(en.Certificate);
}
PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA256");
String hashAlgorithm = signature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, c, hashAlgorithm, false);
DateTime signingTime = DateTime.Now;
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}
Sign PDF
private byte[] signPDF(byte[] pk)
{
byte[] paddedSig = new byte[csize];
System.Array.Copy(pk, 0, paddedSig, 0, pk.Length);
PdfDictionary dic2 = new PdfDictionary();
dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));
appearance.Close(dic2);
//System.IO.File.WriteAllBytes(System.Web.HttpContext.Current.Server.MapPath("~/temp.pdf"), ms.ToArray());
return ms.ToArray();
}
I was told not to use RSA to encrypt simple text but to use AES. I found a simple piece of code to implement AES:
public static class Crypto
{
#region Settings
private static int _iterations = 2;
private static int _keySize = 256;
private static string _hash = "SHA1";
private static string _salt = "aselrias38490a32"; // Random
private static string _vector = "8947az34awl34kjq"; // Random
#endregion
public static string Encrypt(string value, string password)
{
return Encrypt<AesManaged>(value, password);
}
public static string Encrypt<T>(string value, string password)
where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
byte[] valueBytes = Encoding.UTF8.GetBytes(value);
byte[] encrypted;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes =
new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);
cipher.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = cipher.CreateEncryptor(keyBytes, vectorBytes))
{
using (MemoryStream to = new MemoryStream())
{
using (CryptoStream writer = new CryptoStream(to, encryptor, CryptoStreamMode.Write))
{
writer.Write(valueBytes, 0, valueBytes.Length);
writer.FlushFinalBlock();
encrypted = to.ToArray();
}
}
}
cipher.Clear();
}
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string value, string password)
{
return Decrypt<AesManaged>(value, password);
}
public static string Decrypt<T>(string value, string password) where T : SymmetricAlgorithm, new()
{
byte[] vectorBytes = Encoding.ASCII.GetBytes(_vector);
byte[] saltBytes = Encoding.ASCII.GetBytes(_salt);
byte[] valueBytes = Convert.FromBase64String(value);
byte[] decrypted;
int decryptedByteCount = 0;
using (T cipher = new T())
{
PasswordDeriveBytes _passwordBytes = new PasswordDeriveBytes(password, saltBytes, _hash, _iterations);
byte[] keyBytes = _passwordBytes.GetBytes(_keySize/8);
cipher.Mode = CipherMode.CBC;
try
{
using (ICryptoTransform decryptor = cipher.CreateDecryptor(keyBytes, vectorBytes))
{
using (MemoryStream from = new MemoryStream(valueBytes))
{
using (CryptoStream reader = new CryptoStream(from, decryptor, CryptoStreamMode.Read))
{
decrypted = new byte[valueBytes.Length];
decryptedByteCount = reader.Read(decrypted, 0, decrypted.Length);
}
}
}
}
catch (Exception ex)
{
return String.Empty;
}
cipher.Clear();
}
return Encoding.UTF8.GetString(decrypted, 0, decryptedByteCount);
}
}
However, this is based on a string coming back and then used to decrypt in the same program. I need to encrypt the following data in a WinForms program and the decrypt in a whole separate Windows Service program:
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Convert XML doc to byte stream
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
// byte[] fileBytes = Encoding.Default.GetBytes(xmlDoc.OuterXml);
string encrypted = Crypto.Encrypt(xmlDoc.OuterXml, "testpass");
How can I do it? Please show sample code.
EDIT: Kevin, I have implemented your algorithm but the problem is I want to generate the key once and save it for use in the other program to decrypt but I need to pass the byte[] to the encrypt function. So I tried converting using System.Text.Encoding.ASCII.GetBytes(key); and it doesn't do it correctly. I have the wrong number of bytes for byte[] for the key.
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Read file to a string
string contents = File.ReadAllText(fileName);
string key = String.Empty;
byte[] aesKey;
using (var aes = Aes.Create())
{
// aesKey = aes.Key;
key = Convert.ToBase64String(aes.Key);
}
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = System.Text.Encoding.UTF8.GetBytes(sKey);
string encyptedText = EncryptDecrpt.EncryptStringToBase64String(contents, aesKey);
File.WriteAllText(fileName, encyptedText);
EDIT2: Here's both parts as they stand now. The encrypting side:
private void SaveForm()
{
try
{
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
// doc.Save(fileName);
// Read file to a string
// string contents = File.ReadAllText(fileName);
string key = String.Empty;
byte[] aesKey;
//using (var aes = Aes.Create())
//{
// aesKey = aes.Key;
// key = Convert.ToBase64String(aes.Key);
//}
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = Convert.FromBase64String(sKey);
string encyptedText = EncryptDecrpt.EncryptStringToBase64String(doc.ToString(), aesKey);
File.WriteAllText(fileName, encyptedText);
//doc.Save(fileName);
The Windows Service side that tries to decrypt:
try
{
string path = AppDomain.CurrentDomain.BaseDirectory;
eventLog1.WriteEntry(path);
string fileName = System.IO.Path.Combine(path, "alphaService.xml");
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
Byte[] keyBytes = Convert.FromBase64String(sKey);
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);
eventLog1.WriteEntry(xmlStr);
using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
{
reader.ReadToFollowing("DatabaseServerName");
DatabaseServerName = reader.ReadElementContentAsString();
reader.ReadToFollowing("DatabaseUserName");
DatabaseUserName = reader.ReadElementContentAsString();
reader.ReadToFollowing("DatabasePassword");
DatabasePassword = reader.ReadElementContentAsString();
reader.ReadToFollowing("RegistrationCode");
RegistrationCode = reader.ReadElementContentAsString();
}
eventLog1.WriteEntry("Configuration data loaded successfully");
}
catch (Exception ex)
{
eventLog1.WriteEntry("Unable to load configuration data. " + ex.Message);
}
The algorithm I wrote below uses a random Initialization Vector that it puts at the beginning of the encrypted value so you can encrypt the same value twice and not get the same encrypted output. This is fairly normal and lets you only pass a single "secret" back and forth.
You will need to share your secret key by some out of bounds process because both encryption and decryption need to know the key. That is a seperate topic of key exchange that is documented in other places. Here is an SO link to get you started if you need some help on it.
Also if you are "making up" random values I recommend that you don't. Use something to help you like the following which generates random bytes and then converts them into a base64 string which is easier for human usage or some types of key exchange. Note that this is just an example of how you could generate random key's... in practice this may be based on some user input that is recreatable or you use the users hash value to lookup your random key that you generate. In any event here is the code for the key...
byte[] key;
string base64Key;
using (var aes = Aes.Create())
{
// key as byte[]
key = aes.Key;
// key as base64string - which one you use depends on how you store your keys
base64Key= Convert.ToBase64String(aes.Key);
}
Usage is as follows...
// you get the base64 encoded key from somewhere
var base64Key = "+CffHxKmykUvCrrCILd4rZDBcrIoe3w89jnPNXYi0rU=";
// convert it to byte[] or alternatively you could store your key as a byte[]
// but that depends on how you set things up.
var key = Convert.FromBase64String(base64Key);
var plainText = "EncryptThis";
var encryptedText = EncryptStringToBase64String(plainText, key);
var decryptedText = DecryptStringFromBase64String(encryptedText, key);
Here are the encryption methods... EncryptStringToBase64String and DecryptStringFromBase64String.
EDIT: Great point owlstead about using Aes.BlockSize for the IV size. I've also cleaned up the arguement checks.
private const int KeySize = 256; // in bits
static string EncryptStringToBase64String(string plainText, byte[] Key)
{
// Check arguments.
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
byte[] returnValue;
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.GenerateIV();
aes.Mode = CipherMode.CBC;
var iv = aes.IV;
if (string.IsNullOrEmpty(plainText))
return Convert.ToBase64String(iv);
var encryptor = aes.CreateEncryptor(Key, iv);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
// this is just our encrypted data
var encrypted = msEncrypt.ToArray();
returnValue = new byte[encrypted.Length + iv.Length];
// append our IV so our decrypt can get it
Array.Copy(iv, returnValue, iv.Length);
// append our encrypted data
Array.Copy(encrypted, 0, returnValue, iv.Length, encrypted.Length);
}
}
}
// return encrypted bytes converted to Base64String
return Convert.ToBase64String(returnValue);
}
static string DecryptStringFromBase64String(string cipherText, byte[] Key)
{
// Check arguments.
if (string.IsNullOrEmpty(cipherText))
return string.Empty;
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
string plaintext = null;
// this is all of the bytes
var allBytes = Convert.FromBase64String(cipherText);
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.Mode = CipherMode.CBC;
// get our IV that we pre-pended to the data
byte[] iv = new byte[aes.BlockSize/8];
if (allBytes.Length < iv.Length)
throw new ArgumentException("Message was less than IV size.");
Array.Copy(allBytes, iv, iv.Length);
// get the data we need to decrypt
byte[] cipherBytes = new byte[allBytes.Length - iv.Length];
Array.Copy(allBytes, iv.Length, cipherBytes, 0, cipherBytes.Length);
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(Key, iv);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherBytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
EDIT 2: Never convert actual binary data (like a random key) into a string using a TextEncoding. If data starts life as a string and you convert into binary using an encoding then and ONLY then can you convert it from binary into a string using the proper encoding. Otherwise you will have code that works sometimes which is a recipe for torturing yourself.
// This is base64 not UTF8, unicode, ASCII or anything else!!!
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
aesKey = Convert.FromBase64String(sKey);
Edit 3:
Why use File.WriteAllText to write the file but use File.ReadAllBytes when you read it? You can write it and read it as text and use ASCII encoding since base64 is guaranteed to be ASCII. Also Decrypt returns a decrypted string which you are not storing or using. The decrypted string is what you need to parse because it's your xml.
You can use this for saving the file...
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
In your decrypt you should do this...
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedStr , keyBytes);
EDIT 4: I've attempted to duplicate your exception and I can't make it happen... here is my test code that I'm running in a console app and it works.
public static void EncryptMethod()
{
var fileName = #"c:/text.xml";
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", "txtServerName.Text"),
new XElement("DatabaseUserName", "txtDatabaseUserName.Text"),
new XElement("DatabasePassword", "txtDatabasePassword.Text"),
new XElement("ServiceAccount", "txtAccount.Text"),
new XElement("ServicePassword", "txtServicePassword.Text"),
new XElement("RegistrationCode", "txtRegistrationCode.Text"));
doc.Add(xml);
var sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
var aesKey = Convert.FromBase64String(sKey);
string encyptedText = EncryptStringToBase64String(doc.ToString(), aesKey);
File.WriteAllText(fileName, encyptedText);
}
public static void DecryptMethod()
{
var fileName = #"c:/text.xml";
string sKey = "LvtZELDrB394hbSOi3SurLWAvC8adNpZiJmQDJHdfJU=";
Byte[] keyBytes = Convert.FromBase64String(sKey);
var encryptedText = File.ReadAllText(fileName, new ASCIIEncoding());
string xmlStr = DecryptStringFromBase64String(encryptedText, keyBytes);
using (XmlReader reader = XmlReader.Create(new StringReader(xmlStr)))
{
reader.ReadToFollowing("DatabaseServerName");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("DatabaseUserName");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("DatabasePassword");
Console.WriteLine(reader.ReadElementContentAsString());
reader.ReadToFollowing("RegistrationCode");
Console.WriteLine(reader.ReadElementContentAsString());
}
}
Usage from the console app...
EncryptMethod();
DecryptMethod();