I have created a page that encrypts two fields and sends it to another page where it is decrypted.
The problem is if I make my submit button send an smtp email to the user (rather than to page2.aspx itself) and they open the page in any other browser than the one that it was encrypted in, it no longer works.
For example: On page1.aspx there is an ID field, an email field, and a submit button. On submit it encrypts the ID field and sends it as a link (page2.aspx?query=ENCRYPTED DATA) where I can view the data. (This works if I stay in a specific browser)
The problem is I get a bad data error when I open the link in another browser. (Encrypted in Firefox then opening the link in Google Chrome) It throws the Bad Data server error.
Any ideas on how I can fix this issue? I want to be able to open the link from an email but allow cross browser activity using the encrypted link. Any solutions would be great. Thanks.
Here is the Encryption:
public string Encrypt(string message)
{
UTF8Encoding textConverter = new UTF8Encoding();
RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
byte[] toEncrypt = textConverter.GetBytes(message);
ICryptoTransform encryptor = rc2CSP.CreateEncryptor(ScrambleKey, ScrambleIV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
byte[] length = new byte[4];
length[0] = (byte)(message.Length & 0xFF);
length[1] = (byte)((message.Length >> 8) & 0xFF);
length[2] = (byte)((message.Length >> 16) & 0xFF);
length[3] = (byte)((message.Length >> 24) & 0xFF);
csEncrypt.Write(length, 0, 4);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
byte[] encrypted = msEncrypt.ToArray();
string b64 = Convert.ToBase64String(encrypted);
string b64mod = b64.Replace('+', '#');
return HttpUtility.UrlEncode(b64mod);
}
Here is the Email:
private void AutomatedEmail()
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString4"].ConnectionString);
DataTable dt = new DataTable();
con.Open();
SqlDataReader rd = null;
SqlCommand com = new SqlCommand("SELECT ID WHERE ID = #ID", con);
com.Parameters.AddWithValue("#ID", lblid.InnerText);
rd = com.ExecuteReader();
while (rd.Read())
{
string Query = "http://page2.aspx?query=" + Encrypt("ID=" + (rd["ID"].ToString()));
MailMessage message = new MailMessage();
message.To.Add(rd["OwnerEmail"].ToString());
message.From = new MailAddress("xxxxxxxxxxxxx");
message.Subject = "Test Email";
message.Body = "Test: " + Query;
SmtpClient smtp = new SmtpClient("xxxx");
smtp.Port = xxxx;
smtp.EnableSsl = false;
NetworkCredential netcre = new NetworkCredential("xxxx", "xxxx");
smtp.Credentials = netcre;
smtp.Send(message);
}
con.Close();
}
*****EDIT*****
Keys for Encryption/Decryption:
public byte[] ScrambleKey
{
set
{
byte[] key = value;
if (null == key)
{
key = ScrambleKey;
}
Session["ScrambleKey"] = key;
}
get
{
byte[] key = (byte[])Session["ScrambleKey"];
if (null == key)
{
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
rc2.GenerateKey();
key = rc2.Key;
Session["ScrambleKey"] = key;
}
return key;
}
}
public byte[] ScrambleIV
{
set
{
byte[] key = value;
if (null == key)
{
key = ScrambleIV;
}
Session["ScrambleIV"] = key;
}
get
{
byte[] key = (byte[])Session["ScrambleIV"];
if (null == key)
{
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
rc2.GenerateIV();
key = rc2.IV;
Session["ScrambleIV"] = key;
}
return key;
}
}
Receiving Page: Decryption:
public string Decrypt(string scrambledMessage)
{
UTF8Encoding textConverter = new UTF8Encoding();
RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
string b64mod = HttpUtility.UrlDecode(scrambledMessage);
string b64 = b64mod.Replace('#', '+');
byte[] encrypted = Convert.FromBase64String(b64);
ICryptoTransform decryptor = rc2CSP.CreateDecryptor(ScrambleKey, ScrambleIV);
MemoryStream msDecrypt = new MemoryStream(encrypted);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
byte[] fromEncrypt = new byte[encrypted.Length - 4];
byte[] length = new byte[4];
csDecrypt.Read(length, 0, 4);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
int len = (int)length[0] | (length[1] << 8) | (length[2] << 16) | (length[3] << 24);
return textConverter.GetString(fromEncrypt).Substring(0, len);
}
public NameValueCollection DecryptQueryString(string scrambledMessage)
{
string queryString = Decrypt(scrambledMessage);
NameValueCollection result = new NameValueCollection();
char[] splitChar = new char[] { '&' };
char[] equalChar = new char[] { '=' };
foreach (string s in queryString.Split(splitChar))
{
string[] keyVal = s.Split(equalChar, 2);
string key = keyVal[0];
string val = String.Empty;
if (keyVal.Length > 1) val = keyVal[1];
result.Add(key, val);
}
return result;
}
Source Error: Line 112.
Line 110: byte[] length = new byte[4];
Line 111: csDecrypt.Read(length, 0, 4);
Line 112: csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
Line 113: int len = (int)length[0] | (length[1] << 8) | (length[2] << 16) | (length[3] << 24);
Line 114: return textConverter.GetString(fromEncrypt).Substring(0, len);
Related
I have been given some c# code which implements SHA256 hashing and then AES-256-cbc. Now I have to translate the same to NodeJS. I tried several option, documentation and question/answers here, but none helped. As I am working with encryption for the first time, possibly something going wrong with encoding - but can't figure out exactly what.
Here is c# implementation:
using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;
public class HelloWorld
{
public static void Main(string[] args)
{
HelloWorld h1 = new HelloWorld();
Console.WriteLine(h1.EncryptText("Vitthal", "Vitthal"));
}
public string EncryptText(string pInput, string password)
{
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(GenerateSHA256String(pInput));
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
// method name GenerateSHA256String and code
public string GenerateSHA256String(string inputString)
{
StringBuilder stringBuilder = new StringBuilder();
try
{
SHA256 sha256 = SHA256Managed.Create();
byte[] bytes = Encoding.UTF8.GetBytes(inputString);
byte[] hash = sha256.ComputeHash(bytes);
for (int i = 0; i <= hash.Length - 1; i++)
stringBuilder.Append(hash[i].ToString("X2"));
return stringBuilder.ToString();
}
catch (Exception ex)
{
}
return stringBuilder.ToString();
}
// method name AES_Encrypt and code
private byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new System.Security.Cryptography.Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
}
And here is the NodeJS implementation using crypto:
const GenerateSHA256String = (object) => {
const buff = Buffer.from(object.toString());
const hash = createHash('sha256');
hash.update(buff);
const hashed = hash.digest('hex');
return hashed;
}
const getEncryptedChecksum = (object) => {
const payload = GenerateSHA256String(object);
console.log(Buffer.from(payload));
const passKey = Buffer.from('Vitthal');
const saltString = [1,2,3,4,5,6,7,8];
const key = pbkdf2Sync(GenerateSHA256String(passKey), Buffer.from(saltString), 1000, 100, 'sha1');
const encKey = key.subarray(0, 32);
const encIV = key.subarray(32, 48);
const cipher = createCipheriv('aes-256-cbc', encKey, encIV);
let encrypted = cipher.update(Buffer.from(payload), 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
console.log(getEncryptedChecksum('Vitthal'));
Any help in this regard is highly appreciated.
Finally solved. It was encoding issue. Some strange behaviors different between c# & nodejs. Anyways, here is the final nodejs code that works!
const GenerateSHA256String = (object, encoding) => {
const buff = Buffer.from(object.toString());
const hash = createHash('sha256');
hash.update(buff);
const hashed = hash.digest(encoding ? encoding : null);
return hashed;
}
const getEncryptedChecksum = (object) => {
const payload = GenerateSHA256String(object, 'hex');
const payBuff = Buffer.from(payload.toUpperCase());
const passKey = Buffer.from('NDSICDM');
const saltString = [1,2,3,4,5,6,7,8];
const key = pbkdf2Sync(GenerateSHA256String(passKey), Buffer.from(saltString), 1000, 64, 'sha1');
const encKey = key.subarray(0, 32);
const encIV = key.subarray(32, 48);
const cipher = createCipheriv('aes-256-cbc', encKey, encIV);
let encrypted = cipher.update(payBuff, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
I have encrypted a file in c# code using RijndaelManaged which is available in System.Security.Cryptography. This file needs to be transferred to a mobile app developed using dart/flutter and I need it to be decrypted using dart code and present it to the user. How can this be done?
Below shown is the code to do the encryption in c#:
string password = keyPhrase; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateEncryptor(key, key),
CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
Thank you
I ran into the same problem. After many hours, a solution was found. My code is based on this question1 and question2 Code on C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var m_strPassPhrase = "YYYYYYYYYYYYYYYYYYY";
var p_strSaltValue = "XXXXXXXXXXXXXXXXX";
var m_strPasswordIterations = 2;
var m_strInitVector = "ZZZZZZZZZZZZZZZZ";
var plainText = "myPassword";
var blockSize = 32;
var saltValueBytes = Encoding.ASCII.GetBytes(p_strSaltValue);
var password = new Rfc2898DeriveBytes(m_strPassPhrase, saltValueBytes, m_strPasswordIterations);
var keyBytes = password.GetBytes(blockSize);
var symmetricKey = new RijndaelManaged();
var initVectorBytes = Encoding.ASCII.GetBytes(m_strInitVector);
var encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
var memoryStream = new System.IO.MemoryStream();
var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
var cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
var cipherText = Convert.ToBase64String(cipherTextBytes);
Console.WriteLine(cipherText);
Console.WriteLine("\n end");
}
}
}
For flutter you can use pointycastle
Code on Dart(use decryptString and cryptString methods):
import 'dart:convert';
import 'package:pointycastle/block/aes_fast.dart';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:pointycastle/key_derivators/pbkdf2.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';
const KEY_SIZE = 32; // 32 byte key for AES-256
const ITERATION_COUNT = 2;
const SALT = "XXXXXXXXXXXXXXXXX";
const INITIAL_VECTOR = "ZZZZZZZZZZZZZZZZ";
const PASS_PHRASE = "YYYYYYYYYYYYYYYYYYY";
Future<String> cryptString(String text) async {
String encryptedString = "";
final mStrPassPhrase = toUtf8(PASS_PHRASE);
encryptedString =
AesHelper.encrypt(mStrPassPhrase, toUtf8(text), mode: AesHelper.CBC_MODE);
return encryptedString;
}
Future<String> decryptString(String text) async {
String decryptedString = "";
final mStrPassPhrase = toUtf8(PASS_PHRASE);
decryptedString =
AesHelper.decrypt(mStrPassPhrase, toUtf8(text), mode: AesHelper.CBC_MODE);
return decryptedString;
}
///MARK: AesHelper class
class AesHelper {
static const CBC_MODE = 'CBC';
static const CFB_MODE = 'CFB';
static Uint8List deriveKey(dynamic password,
{String salt = '',
int iterationCount = ITERATION_COUNT,
int derivedKeyLength = KEY_SIZE}) {
if (password == null || password.isEmpty) {
throw new ArgumentError('password must not be empty');
}
if (password is String) {
password = createUint8ListFromString(password);
}
Uint8List saltBytes = createUint8ListFromString(salt);
Pbkdf2Parameters params =
new Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
KeyDerivator keyDerivator =
new PBKDF2KeyDerivator(new HMac(new SHA1Digest(), 64));
keyDerivator.init(params);
return keyDerivator.process(password);
}
static Uint8List pad(Uint8List src, int blockSize) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = blockSize - (src.length % blockSize);
var out = new Uint8List(src.length + padLength)..setAll(0, src);
pad.addPadding(out, src.length);
return out;
}
static Uint8List unpad(Uint8List src) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = pad.padCount(src);
int len = src.length - padLength;
return new Uint8List(len)..setRange(0, len, src);
}
static String encrypt(String password, String plaintext,
{String mode = CBC_MODE}) {
String salt = toASCII(SALT);
Uint8List derivedKey = deriveKey(password, salt: salt);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var ivStr = toASCII(INITIAL_VECTOR);
Uint8List iv =
createUint8ListFromString(ivStr);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(true, params);
Uint8List textBytes = createUint8ListFromString(plaintext);
Uint8List paddedText = pad(textBytes, aes.blockSize);
Uint8List cipherBytes = _processBlocks(cipher, paddedText);
return base64.encode(cipherBytes);
}
static String decrypt(String password, String ciphertext,
{String mode = CBC_MODE}) {
String salt = toASCII(SALT);
Uint8List derivedKey = deriveKey(password, salt: salt);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var ivStr = toASCII(INITIAL_VECTOR);
Uint8List iv = createUint8ListFromString(ivStr);
Uint8List cipherBytesFromEncode = base64.decode(ciphertext);
Uint8List cipherIvBytes =
new Uint8List(cipherBytesFromEncode.length + iv.length)
..setAll(0, iv)
..setAll(iv.length, cipherBytesFromEncode);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(false, params);
int cipherLen = cipherIvBytes.length - aes.blockSize;
Uint8List cipherBytes = new Uint8List(cipherLen)
..setRange(0, cipherLen, cipherIvBytes, aes.blockSize);
Uint8List paddedText = _processBlocks(cipher, cipherBytes);
Uint8List textBytes = unpad(paddedText);
return new String.fromCharCodes(textBytes);
}
static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
var out = new Uint8List(inp.lengthInBytes);
for (var offset = 0; offset < inp.lengthInBytes;) {
var len = cipher.processBlock(inp, offset, out, offset);
offset += len;
}
return out;
}
}
///MARK: HELPERS
Uint8List createUint8ListFromString(String s) {
Uint8List ret = Uint8List.fromList(s.codeUnits);
return ret;
}
String toUtf8(value) {
var encoded = utf8.encode(value);
var decoded = utf8.decode(encoded);
return decoded;
}
String toASCII(value) {
var encoded = ascii.encode(value);
var decoded = ascii.decode(encoded);
return decoded;
}
The default mode of Rijndael in .Net is 128 bit block size - compatible with AES. Unless you are using a non-standard block size, prefer .Net's AesManaged.
You haven't specified which padding or mode you are using. The .Net default seems to be CBC, so we'll assume that. It's not clear whether it defaults to a certain padding mode.
(Note that you are using the key both as the IV and the key. The IV should be unique for each invocation of the encryption routine. TLDR - the way you are using AesManaged is insecure - don't use this code in real life.)
Also, you are decoding the key from a string. The key length of AES must be exactly 128 or 256 bits (or one of the more unusual ones). Unless you have chosen your string well, it is unlikely to UTF-8 encode to an exact key length. Also, by using a string you are only using bytes in the key that happen to be characters. Typically, to use a string as a password you would convert it to a key using a key derivation algorithm (e.g. PBKDF2) rather than just UTF-8 encoding it.
With all that said, if your password is exactly 16 (or 32 long) and your file is an exact multiple of 16 bytes (if it is not, you need to decide how to pad it) you should be able to decrypt it like this:
import 'dart:convert';
import 'dart:io';
import 'package:pointycastle/export.dart';
main() async {
var key = utf8.encode('abcdefghijklmnop');
var cipher = CBCBlockCipher(AESFastEngine())
..init(false, ParametersWithIV<KeyParameter>(KeyParameter(key), key));
var cipherText = await File('encryptedFile').readAsBytes();
var plainText = cipher.process(cipherText);
await File('decryptedFile').writeAsBytes(plainText, flush: true);
}
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 am trying to manually decrypt data which was encrypted with MachineKey.Protect(). I am using AES and SHA1 algoritms.
// unencrypted input in HEX: 010203
// AES key in HEX: CCA0DC9874B3F9E679E0A576F77EDF9B121CAB2F9A363A4EAF99976F7B51FA89
// want to decrypt this: A738E5F98920E37AB14C5F4332D4C7F0EC683680AAA0D34B806E75DECF04B7A3DB651E688B563F77BA107FB15990C88FB8023386
Here is an example.
// Some input data
var input = new byte[] { 1, 2, 3 };
// this just works fine
var protectedData = System.Web.Security.MachineKey.Protect(input, "ApplicationCookie", "v1");
// protectedData in hex: A738E5F98920E37AB14C5F4332D4C7F0EC683680AAA0D34B806E75DECF04B7A3DB651E688B563F77BA107FB15990C88FB8023386
// works
var unprotectedInput = System.Web.Security.MachineKey.Unprotect(protectedData, "ApplicationCookie", "v1");
// now lets do it manually
// in web.config machineKey is configured: AES and SHA1
var algorithm = new AesManaged();
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
var validationAlgorithm = new HMACSHA1();
// this is the key from web.config
var key = HexToBinary("CCA0DC9874B3F9E679E0A576F77EDF9B121CAB2F9A363A4EAF99976F7B51FA89");
using (SymmetricAlgorithm encryptionAlgorithm = algorithm)
{
encryptionAlgorithm.Key = key;
int offset = encryptionAlgorithm.BlockSize / 8; //16
int buffer1Count = validationAlgorithm.HashSize / 8; // 20
int count = checked(protectedData.Length - offset - buffer1Count); // 16
byte[] numArray = new byte[offset];
Buffer.BlockCopy((Array)protectedData, 0, (Array)numArray, 0, numArray.Length);
encryptionAlgorithm.IV = numArray; // in HEX: A738E5F98920E37AB14C5F4332D4C7F0
using (MemoryStream memoryStream = new MemoryStream())
{
using (ICryptoTransform decryptor = encryptionAlgorithm.CreateDecryptor())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(protectedData, offset, count);
// Padding is invalid and cannot be removed.
cryptoStream.FlushFinalBlock();
var result = memoryStream.ToArray();
}
}
}
}
I am getting an exception (padding is not right).
I do not know what else to try...
This is the code for MachineKey.Protect, https://msdn.microsoft.com/cs-cz/library/system.web.security.machinekey.protect(v=vs.110).aspx
public byte[] Protect(byte[] clearData)
{
// this is AESManaged
using (SymmetricAlgorithm encryptionAlgorithm = this._cryptoAlgorithmFactory.GetEncryptionAlgorithm())
{
// this is our key
encryptionAlgorithm.Key = this._encryptionKey.GetKeyMaterial();
if (this._predictableIV)
encryptionAlgorithm.IV = CryptoUtil.CreatePredictableIV(clearData, encryptionAlgorithm.BlockSize);
else
encryptionAlgorithm.GenerateIV();
byte[] iv = encryptionAlgorithm.IV;
using (MemoryStream memoryStream = new MemoryStream())
{
memoryStream.Write(iv, 0, iv.Length);
using (ICryptoTransform encryptor = encryptionAlgorithm.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(clearData, 0, clearData.Length);
cryptoStream.FlushFinalBlock();
using (KeyedHashAlgorithm validationAlgorithm = this._cryptoAlgorithmFactory.GetValidationAlgorithm())
{
validationAlgorithm.Key = this._validationKey.GetKeyMaterial();
byte[] hash = validationAlgorithm.ComputeHash(memoryStream.GetBuffer(), 0, checked ((int) memoryStream.Length));
memoryStream.Write(hash, 0, hash.Length);
return memoryStream.ToArray();
}
}
}
}
}
}
The decryption key is wrong.
MachineKey.Protect/UnProtect() modifies the key before using it.
It is doing something like this:
public static byte[] DeriveKey(byte[] key, int keySize, string primaryPurpose, params string[] specificPurposes)
{
var secureUtf8Encoding = new UTF8Encoding(false, true);
var hash = new HMACSHA512(key);
using (hash)
{
var label = secureUtf8Encoding.GetBytes(primaryPurpose);
byte[] context;
using (var memoryStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(memoryStream, secureUtf8Encoding))
{
foreach (var str in specificPurposes)
binaryWriter.Write(str);
context = memoryStream.ToArray();
}
}
return DeriveKeyImpl(hash, label, context, keySize);
}
}
public static byte[] DeriveKeyImpl(HMAC hmac, byte[] label, byte[] context, int keyLengthInBits)
{
int count1 = label != null ? label.Length : 0;
int count2 = context != null ? context.Length : 0;
byte[] buffer = new byte[checked(4 + count1 + 1 + count2 + 4)];
if (count1 != 0)
Buffer.BlockCopy((Array)label, 0, (Array)buffer, 4, count1);
if (count2 != 0)
Buffer.BlockCopy((Array)context, 0, (Array)buffer, checked(5 + count1), count2);
WriteUInt32ToByteArrayBigEndian(checked((uint)keyLengthInBits), buffer, checked(5 + count1 + count2));
int dstOffset = 0;
int val1 = keyLengthInBits / 8;
byte[] numArray = new byte[val1];
uint num = 1;
while (val1 > 0)
{
WriteUInt32ToByteArrayBigEndian(num, buffer, 0);
byte[] hash = hmac.ComputeHash(buffer);
int count3 = Math.Min(val1, hash.Length);
Buffer.BlockCopy((Array)hash, 0, (Array)numArray, dstOffset, count3);
checked { dstOffset += count3; }
checked { val1 -= count3; }
checked { ++num; }
}
return numArray;
}
It is very important to specify the right purpose. For standard MachineKey.Protect primary reason is "User.MachineKey.Protect". The key size for this example is 256 (in bits).
I need tyhe dot net equivalent of the following code. Matter is, I am encrypting using Ruby on client side, here is the code.
The encoded string will be passed to a C# web service. That has to decrypt the string.
If someone can provide the dot net equivalent of this code, then it will be helpful.
require 'rubygems'
require 'ezcrypto'
require 'crypt/rijndael'
plaintext = '24.9195N 17.821E'
aes_key = Crypt::Rijndael.new('0123456789abcdef0123456789abcdef')
aes_cyphertext = aes_key.encrypt_string(plaintext)
print "\n"
print aes_cyphertext +"\n"
print Base64.encode64(aes_cyphertext)
print "\n"
print aes_key.decrypt_string(aes_cyphertext)
print "\n"
It's going to be something like this code shown below as a unit test. The first part does the encryption - the second half does the decryption.
Paste the code into a new MSTest unit test (Create New Test Project or add to an existing one).
The key and the iv are what you'll need to set accordingly.
//needed to convert from hex string
public static byte[] FromHexString(string hexString)
{
int NumberChars = hexString.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
return bytes;
}
[TestMethod]
public void Test()
{
string toEncryptString = "24.9195N 17.821E";
//initialise key and IV (note - all zero IV is not recommended!)
byte[] key = FromHexString("0123456789abcdef0123456789abcdef");
byte[] iv = FromHexString("00000000000000000000000000000000");
byte[] toEncrypt = System.Text.Encoding.UTF8.GetBytes(toEncryptString);
byte[] cipherBytes = null;
string cipherText = null;
//encrypt
using (System.Security.Cryptography.Rijndael r = new RijndaelManaged())
{
r.Key = key;
r.IV = iv;
using(System.Security.Cryptography.ICryptoTransform transform
= r.CreateEncryptor())
{
using (var mStream = new System.IO.MemoryStream())
{
using (var cStream =
new CryptoStream(mStream, transform, CryptoStreamMode.Write))
{
cStream.Write(toEncrypt, 0, toEncrypt.Length);
cStream.FlushFinalBlock();
cipherBytes = mStream.ToArray();
cipherText = Convert.ToBase64String(cipherBytes);
}
}
}
}
//decrypt
byte[] toDecrypt = Convert.FromBase64String(cipherText);
string decryptedString = null;
using (System.Security.Cryptography.Rijndael r = new RijndaelManaged())
{
r.Key = key;
r.IV = iv;
using(System.Security.Cryptography.ICryptoTransform transform2
= r.CreateDecryptor()) // <-- difference here
{
using (var mStream2 = new System.IO.MemoryStream())
{
using (var cStream2 =
new CryptoStream(mStream2, transform2, CryptoStreamMode.Write))
{
cStream2.Write(toDecrypt, 0, toDecrypt.Length);
cStream2.FlushFinalBlock();
decryptedString =
System.Text.Encoding.UTF8.GetString(mStream2.ToArray());
}
}
}
}
Assert.AreEqual(toEncryptString, decryptedString);
}