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.
Related
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.
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?
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), ...
I found this code on a forum:
Public Shared Sub Encryptor(wme As String, password As String)
Try
Dim key As Byte() = New Byte(31) {}
Encoding.Default.GetBytes(password).CopyTo(key, 0)
Dim aes As New RijndaelManaged() With
{
.Mode = CipherMode.CBC,
.KeySize = 256,
.BlockSize = 256,
.Padding = PaddingMode.Zeros
}
Dim buffer As Byte() = File.ReadAllBytes(wme)
Using matrizStream As New MemoryStream
Using cStream As New CryptoStream(matrizStream, aes.CreateEncryptor(key, key), CryptoStreamMode.Write)
cStream.Write(buffer, 0, buffer.Length)
Dim appendBuffer As Byte() = matrizStream.ToArray()
Dim finalBuffer As Byte() = New Byte(appendBuffer.Length - 1) {}
appendBuffer.CopyTo(finalBuffer, 0)
File.WriteAllBytes(wme, finalBuffer)
End Using
End Using
File.Move(wme, wme)
Catch
End Try
End Sub
wme is the file path. Password is the password for the encryption. I was woundering if this was a strong encryption method or its some bad encryption method.
Rijndael with a BlockSize of 256-bits is not AES, AES only supports a block size if 128-bits. Not 31 bytes, there would be some unknown padding applied.
For AES use: .BlockSize = 128.
You need to supply an Initialization Vector (IV). The IV will also be the same as the block size, 128-bits (16-bytes), make it exactly that size. Use a random IV and prefix the encrypted data with it so it will be available for decryption.
Also AES supports three key sizes, 128, 192 and 256 bits, make the keys exactly the correct size.
Further, null padding is a bad idea and does not support binary data, instead the usual padding is PKCS#7 (sometimes specified as PKCS#5).
I need some help in understanding, how to decrypt SalesforceMarketingCloud encrypted String into java.
I should admit that I have few things missing in my questions but this is what ever I have been provided so far
Algorithm – AES256, 256 Bit
Initilization vector – 16 bytes
Salt – 8 Bytes
Passphrase – “presharedpassphrase123”
As per my understanding the Input String has been encrypted using
EncryptSymmetric(#Clear, "AES", #null, #Passphrase, #null, #Salt, #null, #IV)
But when I try to decrypt it at Java side, it fails for various reason.
My best shot has been getting a garbage String as output so far when I used combination like below
NoPadding, CBS, and hashing my passphrase(which is 22 char long) to 32 bit using SHA-256
Can anyone please help, any Java code sample which you successfully used in past.
private String decryptWithKey(String myKey, byte[] strToDecrypt) throws Exception
{
MessageDigest sha = null;
try {
key = myKey.getBytes(CHAR_SCHEME);
sha = MessageDigest.getInstance("SHA-256");
key = sha.digest(key);
key = Arrays.copyOf(key, 32);
secretKey = new SecretKeySpec(key, ALGO);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
byte[] ivByte = new byte[cipher.getBlockSize()];
ivByte = hexStringToByteArray("0716A494177F29F102AF33AFD0253BA1");;
System.out.println(new String(ivByte));
// IvParameterSpec ivParamsSpec = new IvParameterSpec(IV);
IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParamsSpec);
setDecryptedString(new String(cipher.doFinal(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: "+e.toString());
throw e;
}
return decryptedString;
}