PDF External Signature - Trouble Hashing Document - pdf

First things first, I'm somewhat new to posting on StackOverflow, so please do tell me if I'm not providing enough information or something.
I'm interfacing with an external digital signature service. I can get a signature and add it to the original PDF using iTextSharp. It's correctly picked up by Adobe Acrobat; however, the signature is invalid because "The document has been altered or corrupted since it was signed".
I've managed to track the problem down to how I'm generating the original document's hash - as I understand it, the document's hash should be constant (ie. calls to the hashing function with the same document should always return the same hash) because said document hash is used to validate the signature. My hashing function, however, returns a different hash each time. Am I wrong in my assumption that this is a problem? If not, how would I make it have the desired behaviour?
Here goes my code:
public static string Hash(byte[] pdf, List<X509Certificate> chain, string hashAlgorithm, RCSignatureFieldsRecord fields, RCSignaturePositionRecord position)
{
IDigest messageDigest = DigestAlgorithms.GetMessageDigest(hashAlgorithm);
PdfSignatureAppearance sap = BuildAppearence(pdf, chain, fields, position);
Stream data = sap.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(data, messageDigest);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] signedAttributesHash = DigestAlgorithms.Digest(new MemoryStream(sh), messageDigest);
IDigest digest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] messageHash = DigestAlgorithms.Digest(digest, signedAttributesHash);
byte[] sha256Prefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
byte[] digestInfo = new byte[sha256Prefix.Length + messageHash.Length];
sha256Prefix.CopyTo(digestInfo, 0);
messageHash.CopyTo(digestInfo, sha256Prefix.Length);
return Convert.ToBase64String(digestInfo);
}
private static PdfSignatureAppearance BuildAppearence(byte[] pdf, List<X509Certificate> chain, RCSignatureFieldsRecord fields, RCSignaturePositionRecord position)
{
PdfReader reader = new PdfReader(pdf);
MemoryStream baos = new MemoryStream();
PdfStamper stamper = PdfStamper.CreateSignature(reader, baos, '\0', null, true);
PdfSignatureAppearance sap = stamper.SignatureAppearance;
sap.Certificate = chain[0];
sap.SignDate = DateTime.Now;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.Date = new PdfDate(sap.SignDate);
dic.Name = CertificateInfo.GetSubjectFields(chain[0]).GetField("CN");
sap.CryptoDictionary = dic;
sap.Certificate = chain[0];
sap.Acro6Layers = true;
setupSignatureFields(sap, fields);
setupVisualSignature(sap, position);
Dictionary<PdfName, int> exc = new Dictionary<PdfName, int>();
exc.Add(PdfName.CONTENTS, 8192 * 2 + 2);
sap.PreClose(exc);
return sap;
}
Any help would be greatly appreciated!
Edit
Based on mkl's response, I've refactored my code. I've opted for the second option (closing sap with an empty signature and keeping baos with the half-signed PDF). However, I'm getting a (not very descriptive) exception when calling sap.Close(dic):
The given key was not present in the dictionary.
at System.ThrowHelper.ThrowKeyNotFoundException()
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at iTextSharp.text.pdf.PdfSignatureAppearance.Close(PdfDictionary update)
Here's the updated code:
public static Tuple<string, byte[]> Hash(byte[] pdf, List<X509Certificate> chain, string hashAlgorithm, RCSignatureFieldsRecord fields, RCSignaturePositionRecord position)
{
// build signature appearence
PdfReader reader = new PdfReader(pdf);
MemoryStream baos = new MemoryStream();
PdfStamper stamper = PdfStamper.CreateSignature(reader, baos, '\0', null, true);
PdfSignatureAppearance sap = stamper.SignatureAppearance;
sap.Certificate = chain[0];
sap.SignDate = DateTime.Now;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.Date = new PdfDate(sap.SignDate);
dic.Name = CertificateInfo.GetSubjectFields(chain[0]).GetField("CN");
sap.CryptoDictionary = dic;
sap.Certificate = chain[0];
sap.Acro6Layers = true;
setupSignatureFields(sap, fields);
setupVisualSignature(sap, position);
Dictionary<PdfName, int> exc = new Dictionary<PdfName, int>();
exc.Add(PdfName.CONTENTS, 8192 * 2 + 2);
sap.PreClose(exc);
// calculate hash
IDigest messageDigest = DigestAlgorithms.GetMessageDigest(hashAlgorithm);
Stream data = sap.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(data, messageDigest);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] signedAttributesHash = DigestAlgorithms.Digest(new MemoryStream(sh), messageDigest);
IDigest digest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] messageHash = DigestAlgorithms.Digest(digest, signedAttributesHash);
byte[] sha256Prefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
byte[] digestInfo = new byte[sha256Prefix.Length + messageHash.Length];
sha256Prefix.CopyTo(digestInfo, 0);
messageHash.CopyTo(digestInfo, sha256Prefix.Length);
// close the signature appearence with an empty signature and return the hash and pdf with empty signature field
sap.Close(dic); // throws an exception
return new Tuple<string, byte[]>(Convert.ToBase64String(digestInfo), baos.ToArray());
}

as I understand it, the document's hash should be constant (ie. calls to the hashing function with the same document should always return the same hash) because said document hash is used to validate the signature. My hashing function, however, returns a different hash each time. Am I wrong in my assumption that this is a problem?
You are correctly assuming that the hash of a fixed document is constant. This after all is a major point of hashing.
You err, though, assuming that your use case is an example of this. Because you hash different documents in each run.
In BuildAppearence each time a different PDF is generated because in particular the current time is stored in it both as signing time (explicitly by your code) and as document modification time (implicitly by iText). Furthermore, a unique ID is generated and stored in the PDF (implicitly by iText). Depending on other aspects of the PDF there may yet be more entries in the PDF which change from call to call.
While it is possible to patch those implicitly set PDF entries in iText to become injectable, the clean way to implement signing would be to follow the iText signing examples and keep the originally produced version of the PDF and later inject the signature into it.

Related

AES-GCM decryption error iaik.cms.CMSException: Unable to decrypt encrypted content-encryption key: Invalid padding

I am using following code to encrypt data using AES-GCM:
// the stream to which to write the EnvelopedData
ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
EnvelopedDataOutputStream envelopedData;
// wrap EnvelopedData into a ContentInfo
ContentInfoOutputStream contentInfoStream =
new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
// create a new EnvelopedData object encrypted with AES
try {
envelopedData = new EnvelopedDataOutputStream(contentInfoStream,
(AlgorithmID)AlgorithmID.aes256_GCM.clone());
} catch (NoSuchAlgorithmException ex) {
throw new CMSException("No implementation for AES.");
}
// create the recipient infos
RecipientInfo[] recipients = new RecipientInfo[1];
// user1 is the first receiver
recipients[0] = new KeyTransRecipientInfo(signCert,
(AlgorithmID)AlgorithmID.rsaEncryption.clone());
// specify the recipients of the encrypted message
envelopedData.setRecipientInfos(recipients);
//int blockSize = 2048; // in real world we would use a block size like 2048
// write in the data to be encrypted
//byte[] buffer = new byte[blockSize];
byte[] buffer = new byte[16];
//byte[] buffer;
int bytesRead;
while ((bytesRead = inputstream.read(buffer)) != -1) {
envelopedData.write(buffer, 0, bytesRead);
}
But while trying to decrypt the same using following code:
EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
enveloped_data.setupCipher(privateSignKey, 0);
InputStream decrypted = enveloped_data.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
ByteStreams.copy(decrypted, os);
os.close();
I am getting following error:
iaik.cms.CMSException: Unable to decrypt encrypted content-encryption key: Invalid padding!
I am not sure of block size to be used, this might be a reason for the error, but not sure.
Moreover, if I compare my encrypted file with that of a correct one, I see following difference:
My encrypted data:
OBJECT IDENTIFIER 2.16.840.1.101.3.4.1.46 aes256-GCM (NIST Algorithm)
SEQUENCE (1 elem)
OCTET STRING (12 byte) FE5729470184A04A5AA30158
Correct data:
OBJECT IDENTIFIER 2.16.840.1.101.3.4.1.46 aes256-GCM (NIST Algorithm)
SEQUENCE (2 elem)
OCTET STRING (12 byte) CA985F7EAFB709DF711DCA2A
INTEGER 16
Could you please help us in getting this to work?

placing the separately signed hash to Multiple places in PDF using itextsharp

I want to place same externally signed hash (signature value) at multiple places in a PDF.
I have referred the page 'how-to-place-the-same-digital-signatures-to-multiple-places-in-pdf-using-itextsh' and tried to implement the work around provided by mkl (please refer this How to place the Same Digital signatures to Multiple places in PDF using itextsharp.net).
And it works. I ported it to get the signer bytes signed externally using web service/ api and it also works. Now due to one of the requirement I changed the way hash is being calculated.
now instead of (old one):
byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
I am trying to use (new one):
int contentEstimated=8192;
HashAlgorithm sha = new SHA256CryptoServiceProvider();
int read = 0;
byte[] buff = new byte[contentEstimated];
while ((read = data.Read(buff, 0, contentEstimated)) > 0)
{
sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);
byte[] hash = Org.BouncyCastle.Utilities.Encoders.Hex.Encode(sha.Hash);
string hashtext = Encoding.UTF8.GetString(hash, 0, hash.Length); //for writing it to file or sharing it to another api
byte[] hash1 = StringToByteArray(hashtext);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash1, null, null, CryptoStandard.CMS); or
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS); //tried both
and if I try to use it in existing implementation, the signature gets invalid with an error "Document has been altered or corrupt since it was signed". Can you please tell me where I am doing it wrong?
At most of the referred pages, they have used this hash generation method with an embed function where the calculated hash is getting embedded in the pdf,
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);
Thank you.
- Tanmay
In comments the OP clarified that he wants to adapt the solution here to using an external signing service which accepts a document hash (more exactly a SHA256 hash value of the document in Hex format) and returns a full-fledged CMS signature container.
In this case the original AllPagesSignatureContainer method Sign
public byte[] Sign(Stream data)
{
String hashAlgorithm = externalSignature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = externalSignature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}
has to be changed to not itself create a CMS container using the PdfPKCS7 sgn but merely calculate the document hash, send it to the service and use the container returned by the service:
public byte[] Sign(Stream data)
{
String hashAlgorithm = externalSignature.GetHashAlgorithm();
IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] hexHash = Org.BouncyCastle.Utilities.Encoders.Hex.Encode(hash);
string hexHashString = Encoding.UTF8.GetString(hexHash , 0, hexHash.Length);
var response = [... call service with document hash hexHashString ...];
byte[] signatureContainer = [... extract binary CMS container from response ...];
return signatureContainer;
}
The OP has not mentioned anything about the response format, so I cannot say more about the extract binary CMS container from response part. It may include selecting one attribute of a larger response structure, it may include decoding an encoded value (probably a Hex encoded string), ...

How to convert a PDF generating in response.outputStream to a Base64 encoding

I am doing a project where i need to create a admit card for student who are appearing in examination. The pdf Generation part is working fine but my problem is i have to encode my pdf to Base64 format. I am not finding any solution. My code are given as bellow
public void downloadPdf() throws IOException, DocumentException {
System.out.println("hi i ma in");
resultList=examinationDetailsService.readAdmitCardData();
for(Object[] data:resultList)
{
personalDetails=(PersonalDetails)data[0];
System.out.println("name"+personalDetails.getApplicantName());
rollNoAssign=(RollNoAssign)data[1];
System.out.println("rollno"+rollNoAssign.getRollNo());
examDateAssign=(ExamDateAssign)data[2];
}
//Converting Date
SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy");
String date = DATE_FORMAT.format(examDateAssign.getExaminationDate());
// Get the FacesContext
FacesContext facesContext = FacesContext.getCurrentInstance();
// Get HTTP response
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
// Set response headers
response.reset(); // Reset the response in the first place
response.setHeader("Content-Type", "application/pdf"); // Set only the content type
// Open response output stream
OutputStream responseOutputStream = response.getOutputStream();
Document document = new Document(PageSize.A4, 0, 0, 0, 0);
PdfWriter.getInstance(document,response.getOutputStream());
document.open();
PdfPTable maintable = new PdfPTable(1);
maintable.setWidthPercentage(90);
maintable.setSpacingBefore(0f);
maintable.setSpacingAfter(0f);
Paragraph ph1 = new Paragraph();
ph1.add(new Chunk("\n\nGOVERNMENT OF ASSAM \nOFFICE OF THE ELECTRICAL LICENSING BOARD, ASSAM\n1", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12, Font.NORMAL)));
//ph1.add(new Chunk("ST", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12, Font.NORMAL)));
Chunk superScript = new Chunk("ST", FontFactory.getFont(FontFactory.TIMES_ROMAN, 8, Font.NORMAL));
superScript.setTextRise(5f);
ph1.add(superScript);
ph1.add(new Chunk(" FLOOR, WEST END BLOCK, HOUSEFED COMPLEX,DISPUR, GUWAHATI-781006, ASSAM.", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12, Font.NORMAL)));
ph1.add(new Chunk("\n***\n", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12, Font.BOLD)));
ph1.add(new Chunk("\nADMIT CARD", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12, Font.BOLD)));
PdfPCell heading1 = new PdfPCell(ph1);
heading1.setBorder(0);
heading1.setHorizontalAlignment(Element.ALIGN_CENTER);
heading1.setVerticalAlignment(Element.ALIGN_CENTER);
maintable.addCell(heading1);
PdfPTable maintable1 = new PdfPTable(1);
maintable1.setWidthPercentage(87);
maintable1.setSpacingBefore(0f);
maintable1.setSpacingAfter(0f);
Paragraph ph2 = new Paragraph();
ph2.add(new Chunk("\n\nShri/Smti "+personalDetails.getApplicantName()+", Roll No. "+rollNoAssign.getRollNo()+" is hereby "
+ "allowed to appear in the examination for grant of Electrical Supervisor's Certificate of Competency "
+ "to be held at "+ rollNoAssign.getVenue().getName()
+ "as per schedule given below:", FontFactory.getFont(FontFactory.TIMES_ROMAN, 11, Font.NORMAL)));
ph2.add(new Chunk("\n\n Viva-voce", FontFactory.getFont(FontFactory.TIMES_ROMAN, 11, Font.BOLD)));
ph2.add(new Chunk(" test on "+date+ " at 9 AM/12.30 PM ;", FontFactory.getFont(FontFactory.TIMES_ROMAN, 11, Font.NORMAL)));
ph2.add(new Chunk(" Written", FontFactory.getFont(FontFactory.TIMES_ROMAN, 11, Font.BOLD)));
ph2.add(new Chunk(" test on __________ at 9 AM.", FontFactory.getFont(FontFactory.TIMES_ROMAN, 11, Font.NORMAL)));
ph2.add(new Chunk("\n\n\nPlease bring the followings with you while coming for the said examinations: \n"
+ "\n1.Original copy of Degree/Diploma/ITI Certificate/Supervisor's Certificate of Competency/"
+ "Workmen's Permit\n\n belonging to you and this Admit Card in original.\n"
+ "\n2.Detail experience certificate(s) relevant to the part(s) of Supervisor's examination applied for.\n"
+ "\n3.\n"
+ "\n\nNB: (a) No alteration is allowed in the entries on this Admit Card without the authority of the Board."
+ "\n (b) No expense(s) incurred by any candidate will be borne by the Board.\n\n\n", FontFactory.getFont(FontFactory.TIMES_ROMAN, 11, Font.NORMAL)));
PdfPCell heading2 = new PdfPCell(ph2);
heading2.setBorder(0);
heading2.setHorizontalAlignment(Element.ALIGN_JUSTIFIED);
heading2.setVerticalAlignment(Element.ALIGN_CENTER);
maintable1.addCell(heading2);
PdfPTable maintable2 = new PdfPTable(2);
float[] columnWidths = new float[]{55f, 45f};
maintable2.setWidths(columnWidths);
maintable2.setWidthPercentage(84);
maintable2.setSpacingBefore(0f);
maintable2.setSpacingAfter(0f);
Paragraph ph31 = new Paragraph();
ph31.add(new Chunk("Details furnished by you in the application form and/or examination process are used by the Board"
+ " for further needful, hence, if you feel any correction(s) in the same is/are required, please get those done before"
+ " leaving the examination venue. The Board shall not be under any obligation of removing the difficulties arising later"
+ " on out of incorrect/improper information furnished by you or non-furnishing of required ones.\n", FontFactory.getFont(FontFactory.COURIER, 10, Font.NORMAL)));
PdfPCell heading31 = new PdfPCell(ph31);
heading31.setBorder(15);
heading31.setHorizontalAlignment(Element.ALIGN_JUSTIFIED);
heading31.setVerticalAlignment(Element.ALIGN_LEFT);
maintable2.addCell(heading31);
Paragraph ph32 = new Paragraph();
ph32.add(new Chunk("\n\n\n(Member Secretary)\nElectrical Licensing Board,\nAssam.", FontFactory.getFont(FontFactory.TIMES_ROMAN, 12, Font.NORMAL)));
PdfPCell heading32 = new PdfPCell(ph32);
heading32.setBorder(0);
heading32.setHorizontalAlignment(Element.ALIGN_CENTER);
heading32.setVerticalAlignment(Element.ALIGN_CENTER);
maintable2.addCell(heading32);
document.add(maintable);
document.add(maintable1);
document.add(maintable2);
document.close();
/* // Read PDF contents
URL url = new URL(PDF_URL);
InputStream pdfInputStream = url.openStream();*/
// Read PDF contents and write them to the output
byte[] bytesBuffer = new byte[2048];
int bytesRead;
/* while ((bytesRead = pdfInputStream.read(bytesBuffer)) > 0) {
responseOutputStream.write(bytesBuffer, 0, bytesRead);
}
*/
Base64 encoder = new Base64();
byte[] decodedBytes = encoder.o
// Make sure that everything is out
responseOutputStream.write(decodedBytes);
responseOutputStream.flush();
// Close both streams
//pdfInputStream.close();
responseOutputStream.close();
// JSF doc:
// Signal the JavaServer Faces implementation that the HTTP response for this request has already been generated
// (such as an HTTP redirect), and that the request processing lifecycle should be terminated
// as soon as the current phase is completed.
facesContext.responseComplete();
}
Please give me any solution. i using itext
When I look at your code, I see:
PdfWriter.getInstance(document,response.getOutputStream());
Which means that you are instructing iText to write PDF bytes straight to the browser. This outputstream is closed at the moment you close the document.
I also see:
OutputStream responseOutputStream = response.getOutputStream();
I even see that you try adding stuff to this stream. This is impossible as the stream is already closed.
You need to create a ByteArrayOutputStream like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
And use this in the PdfWriter:
PdfWriter.getInstance(document, baos);
Now you can get the PDF bytes like this:
byte[] pdf = baos.toByteArray();
Now you can encode these bytes and send them to the output stream.
Encode:
Assuming that you are using org.apache.commons.codec.binary.Base64 as explained in the answer to Base64 Encoding in Java, you can do this:
byte[] base64 = Base64.encodeBase64(pdf);
(There are other ways to do this.)
Send to output stream:
response.setHeader("Expires", "0");
response.setHeader("Cache-Control",
"must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setContentType("application/pdf");
response.setContentLength(base64.length);
OutputStream os = response.getOutputStream();
os.write(base64);
os.flush();
os.close();

How to show timestamp status in PDF signature appearance using itext

The adobe reader shows pdf signature status, valid and invalid.
I would like the reader to shows the dynamic(not static)timestamp status which is included in the signature.
The itext code I use is as follows.
PdfReader reader = new PdfReader(IN_FILE);
FileOutputStream fout = new FileOutputStream(OUT_FILE);
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setCrypto(null, chain, null, PdfSignatureAppearance.SELF_SIGNED);
sap.setVisibleSignature(new Rectangle(100, 100, 300, 200), 1, "Signature");
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("adbe.pkcs7.detached"));
dic.setReason(sap.getReason());
dic.setLocation(sap.getLocation());
dic.setContact(sap.getContact());
dic.setDate(new PdfDate(sap.getSignDate()));
sap.setCryptoDictionary(dic);
int contentEstimated = 15000;
HashMap exc = new HashMap();
exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2));
sap.preClose(exc);
PDFTemplate sigLayer = PdfSignatureAppearance.getLayer(n);
is responsible for signature appearance. But I am unable to include the dynamic timestamp status.
The to-be-signed timestamp hash value is computed over the signed pdf, when the document's signature appearance has been created.
Modify the pdf adding the ts information will invalidate the signature...

Using RijndaelManaged encryption in Silverlight

I'm implementing a webservice client that is required to encrypt the request using 128-bits Rijndael. Because the RijndaelManaged class doesn't exist in Silverlight, I've followed the advice here:
This was discussed here: AesManaged and RijndaelManaged
The result is that the result I'm getting is correct (I mean, the same I'm getting using RijndaelManaged) only for the first 32 characters (128 bits), exactly the block size. I can't figure out what I'm doing wrong here. My .Net implementation (RijndaelManaged) goes like this:
private static byte[] Encrypt(byte[] PlainTextBytes, byte[] KeyBytes, string InitialVector)
{
byte[] InitialVectorBytes = Encoding.UTF8.GetBytes(InitialVector);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.ECB;
SymmetricKey.Padding = PaddingMode.PKCS7;
SymmetricKey.BlockSize = 128;
ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
MemoryStream MemStream = new MemoryStream();
CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
CryptoStream.FlushFinalBlock();
byte[] CipherTextBytes = MemStream.ToArray();
MemStream.Close();
CryptoStream.Close();
return CipherTextBytes;
}
while my Silverlight is:
private string Encrypt(byte[] PlainTextBytes, byte[] KeyBytes, string InitialVector)
{
AesManaged SymmetricKey = new AesManaged();
byte[] InitialVectorBytes = SymmetricKey.IV;
//NOTE- because Mode and Padding don't exist in AESManaged for Silverlight, I have to do the padding myself
//for an empty InitalVector (which is my case)
for (int i = 0; i < InitialVectorBytes.Length; i++) InitialVectorBytes[i] = 0;
SymmetricKey.BlockSize = 128;
ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
MemoryStream MemStream = new MemoryStream();
CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
CryptoStream.FlushFinalBlock();
byte[] CipherTextBytes = MemStream.ToArray();
MemStream.Close();
CryptoStream.Close();
return CipherTextBytes;
}
It's not clear that you're really clearing the IV. You're fetching it as a byte array and then clearing that, but I don't know for sure whether the IV property copies the internal value before returning it. I'd use this:
SymmetricKey.BlockSize = 128;
SymmetricKey.IV = new byte[SymmetricKey.BlockSize / 8];
(I'd also stop using PascalCase for parameters and local variables, by the way. The normal convention is to use camelCase for non-constant variables.)
EDIT: Sample code to demonstrate how your code isn't really changing the IV:
AesManaged aes = new AesManaged();
byte[] iv = aes.IV;
iv[0] = 1;
iv[1] = 2;
Console.WriteLine(BitConverter.ToString(iv));
Console.WriteLine(BitConverter.ToString(aes.IV));
Sample output:
01-02-01-1B-6E-05-B8-2A-C0-86-17-EF-A2-80-60-7B
D8-48-01-1B-6E-05-B8-2A-C0-86-17-EF-A2-80-60-7B
In other words, changing the values in the array isn't changing the IV really.
Your .NET version uses EBC mode for AES while the Silverlight version uses CBC. The first output block is the same in both versions because in ECB mode the initialization vector is not used and in CBC mode the initialization vector is zero. In CBC mode each block is XORed with the previous block (first block is XORed with the initialization vector) and then encrypted while in EBC mode each block is encrypted as it is.