End-to-end encrypted static website content - encryption

I have a really simple static webpage which just consists of a single html file that gets served.
I want to encrypt some of its content so it can only be read by certain persons (the ones that know a certain passphrase). Please note I don't want to encrypt the source code. Just a "secret" content inside of it.
For instance:
<p>
This is a secret message
</p>
I know I can add authentication at a server level, but I don't want my html file to be even readable by the server, meaning I want end-to-end encryption of the "secret" parts.
This might be a good job for asymmetric encryption. I could use the others public keys to encrypt the message so only them will be able to read it (I even thought of mutual SSL authentication).
The thing is, this might be too difficult/not possible to do for the intended recipients as they're non-technical people. I just want them to use a passphrase to see the "secret" content, not installing any certificates or similar stuff. So maybe just symmetric encryption is a better fit.
Do you have any ideas on how to achieve this? Is AES or a similar algorithm a good idea for this?
I'm open to different approaches.
Thanks

See the example below, where a static HTML page contains some encrypted ciphertext embedded in the page source. The user can enter an AES256 key to decrypt the ciphertext, and upon decryption (assuming the correct key was entered) the plaintext is displayed.
As you can see, all of the crypto is done client-side (in browser) in javascript using the window.crypto.subtle.decrypt() function from the Web Crypto API.
The key to decrypt the ciphertext is:
ae35b7091804f9a011fd7e2ec030cf78e0aadf225d86a7238cfd73a451cbdb86
<html lang="en">
<head>
<meta charset="utf-8">
<title>AES Decryption</title>
</head>
<body>
<h2>AES Decryption</h2>
<p>Enter key: <input id=txtkeyhex size=64 maxlength=64 value=''> (32 bytes, hex-encoded)</p>
<p><button id=btndecrypt onclick=javascript:fdecrypt();>Decrypt</button></p>
<p> </p>
<p><div id=divdecryptresult style="font-family:courier;"></p>
<script>
async function fdecrypt() {
var ciphertexthex='13d9858cf68ca674d91bbbd23e2907b5d079e0718c14506f95d44cbd79d8808c76b863a431138f2ed2e7277b166f898f56c3311baffa5013fea2fe93bc1e33ceaa5a97b885d4e724422bf8987b416ece84cacf511136d5ee545826f7f15ee93f';
var ivhex='6bf8762dc343afc9a7f02d1a6af75d28';
if(!ishex(txtkeyhex.value)) {
alert('decryption key must be hex-encoded.');
return;
}
if(txtkeyhex.value.length!=64) {
alert('invalid length for decryption key. key must be 32 bytes in length. (32 bytes = 64 hexadecimal characters)');
return;
}
var ciphertextbytes=hextobytes(ciphertexthex);
var keybytes=hextobytes(txtkeyhex.value);
var ivbytes=hextobytes(ivhex);
var key=await window.crypto.subtle.importKey('raw', keybytes, {name: 'AES-CBC', length: 256}, false, ['decrypt'])
.catch(function(err){
alert('unable to import key.');
console.error(err);
});
if(!key) { return; }
var plaintextbytes=await window.crypto.subtle.decrypt({name: "AES-CBC", iv: ivbytes}, key, ciphertextbytes)
.catch(function(err){
alert('decryption failed. check inputs.');
console.error(err);
});
if(!plaintextbytes) { return; }
var plaintext=new TextDecoder("utf-8").decode(plaintextbytes);
divdecryptresult.innerText=plaintext;
}
function ishex(s) {
var regex=/[0-9A-Fa-f]/g;
return regex.test(s);
}
function hextobytes(hexstring) {
var result=new Uint8Array(hexstring.length/2);
for(var i=0; i<hexstring.length/2; i++) {
result[i]=parseInt(hexstring.substring(i*2, i*2+2), 16);
}
return result;
}
</script>
</body>
</html>

Related

How to hash a password using an existing key in PCLCrypto?

I'm creating a mobile application that connects to a web service. It needs to login to the system using a password. The password is stored on the server as a MD5 hashed password that was hashed using this method:
Byte[] Initial = <Key goes here>
MD5CryptoServiceProvider Provider = new MD5CryptoServiceProvider();
List<Byte> Encoding = new List<Byte>(Initial);
Encoding.AddRange(ASCIIEncoding.ASCII.GetBytes(inputString));
return Convert.ToBase64String(Provider.ComputeHash(Encoding.ToArray()));
The key and code to hash the password on the web application cannot be used in my Xamarin PCL project because it's not possible to use the 'MD5CryptoServiceProvider' in Xamarin PCL.
I need to create an equivalent method in the xamarin application to hash the password, before it's compared to the web service version.
To do this I have chose PCLCrypto but I can't seem to find anywhere to include the same key that was used to originally encrypt the password.
This is the Xamarin code:
Byte[] Initial = <the same key as was used to originally cache it>
// step 1, calculate MD5 hash from input
var hasher = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Md5);
byte[] inputBytes = Encoding.UTF8.GetBytes(inputString);
byte[] hash = hasher.HashData(inputBytes);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
Thanks in advance for your help.
I have a solution but i use plateform specific code.
In xamarin.forms part you implement an interface like for example:
public interface SpecificCodes
{
Boolean comparePasswords(String userPassword,String DBPassword);
}
In xamarin.android you create a class which implements interface
[assembly:Dependency(typeof(MyProject.Droid.Code.SpecificPartsAndroid))]
namespace MyProject.Droid.Code
{
...
public class SpecificPartsAndroid: SpecificCodes
{
public Boolean comparePasswords(String userPassword, String DBPassword)
{
byte[] dig = hash(new Java.Lang.String(userPassword).GetBytes("UTF-8"),"MD5");
byte[] res=Base64.Decode(DBPassword,Base64Flags.Default);
return String.Compare(dig.ToString(),res.ToString())==0?true:false;
}
private static byte[] hash (byte[] toHash, String algorithm)
{
MessageDigest md = MessageDigest.GetInstance(algorithm);
return md.Digest(toHash);
}
}
}
In xamarin.ios do the same
And then to call your function use the code below in Xamarin.forms
DependencyService.Get<SpecificCodes>().comparePasswords(pwdUser, pwdHashed);
Hope it helps
Just using a hash function is not sufficient and just adding a salt does little to improve the security. Instead iIterate over an HMAC with a random salt for about a 100ms duration and save the salt with the hash. Use functions such as PBKDF2, password_hash, Bcrypt and similar functions. The point is to make the attacker spend a lot of time finding passwords by brute force.
Protecting your users is important, please use secure password methods.
See How to securely hash passwords, The Theory on Security Stackexchange.
See OWASP (Open Web Application Security Project) Password Storage Cheat Sheet.
See Modern, Secure, Salted Password Hashing Made Simple
See Toward Better Password Requirements by Jim Fenton:

Inline image rendered twice by OSX mail app

My .NET 4.5 web application uses class SmtpClient to create and send e-mail messages to various recipients.
Each e-mail message consists of:
an HTML message body
an embedded inline image (JPeg or PNG or GIF)
an attachment (PDF)
Sample code is below. It works fine, but there is one gripe from OSX users. Apple's standard mail app renders the image twice; once inlined in the message body, and again following the message body, next to the preview of the PDF attachment.
I tinkered with the following properties; none of which would help.
SmtpClient's DeliveryFormat
MailMessage's IsBodyHtml and BodyTransferEncoding
Attachment's MimeType, Inline, DispositionType, ContentId, FileName, Size, CreationDate, ModificationDate
If I compose a similar e-mail message in MS Outlook and send it off to the Apple user, the image is rendered once, inlined in the message body; exactly as I would like it to be. So apparently it is possible.
After reading this, I inspected the raw MIME data, and noticed Outlook uses multipart/related to group together the message body and the images.
My question:
How do I mimic Outlook's behavior with the classes found in System.Net.Mail?
Things I would rather not do:
Employ external images instead of embedded ones (many e-mail clients initially block these to protect recipient's privacy).
Use third party libraries (to avoid legal hassle). The SmtpDirect class I found here seems to solve the problem (though I got a server exception in return), but it is hard for me to accept a complete rewrite of MS's SmtpClient implementation is necessary for such a subtle change.
Send the e-mail message to a pickup folder, manipulate the resulting .eml file, push the file to our Exchange server.
Minimal code to reproduce the problem:
using System.IO;
using System.Net.Mail;
using System.Net.Mime;
namespace SendMail
{
class Program
{
const string body = "Body text <img src=\"cid:ampersand.gif\" /> image.";
static Attachment CreateGif()
{
var att = new Attachment(new MemoryStream(Resource1.ampersand), "ampersand.gif")
{
ContentId = "ampersand.gif",
ContentType = new ContentType(MediaTypeNames.Image.Gif)
};
att.ContentDisposition.Inline = true;
return att;
}
static Attachment CreatePdf()
{
var att = new Attachment(new MemoryStream(Resource1.Hello), "Hello.pdf")
{
ContentId = "Hello.pdf",
ContentType = new ContentType(MediaTypeNames.Application.Pdf)
};
att.ContentDisposition.Inline = false;
return att;
}
static MailMessage CreateMessage()
{
var msg = new MailMessage(Resource1.from, Resource1.to, "The subject", body)
{
IsBodyHtml = true
};
msg.Attachments.Add(CreateGif());
msg.Attachments.Add(CreatePdf());
return msg;
}
static void Main(string[] args)
{
new SmtpClient(Resource1.host).Send(CreateMessage());
}
}
}
To actually build and run it, you will need an additional resource file Resource1.resx with the two attachments (ampersand and Hello) and three strings host (the SMTP server), from and to (both of which are e-mail addresses).
(I found this solution myself before I got to posting the question, but decided to publish anyway; it may help out others. I am still open for alternative solutions!)
I managed to get the desired effect by using class AlternateView.
static MailMessage CreateMessage()
{
var client = new SmtpClient(Resource1.host);
var msg = new MailMessage(Resource1.from, Resource1.to, "The subject", "Alternative message body in plain text.");
var view = AlternateView.CreateAlternateViewFromString(body, System.Text.Encoding.UTF8, MediaTypeNames.Text.Html);
var res = new LinkedResource(new MemoryStream(Resource1.ampersand), new ContentType(MediaTypeNames.Image.Gif))
{
ContentId = "ampersand.gif"
};
view.LinkedResources.Add(res);
msg.AlternateViews.Add(view);
msg.Attachments.Add(CreatePdf());
return msg;
}
As a side effect, the message now also contains a plain text version of the body (for paranoid web clients that reject HTML). Though it is a bit of a burden ("Alternative message body in plain text" needs improvement), it does give you more control as to how the message is rendered under different security settings.

How x509chain works in C#? What is purpose of using it?

I have implement client certificate based security in C#. Everything is clear and working good for me. I was finding perfect way to verify client certificate on server side, through which i came to x509Chain. But i am not clear about How it exactly works? How can we configure it? Is it secure way to verify certificate?
Any help will be Appreciated! Thanks!
Note: By theoretical point of view, i have read my documents on that but i am not sure for it's functionality. Please provide practical example and guide for that.
A bit of theory I wrote some time ago: http://social.technet.microsoft.com/wiki/contents/articles/3147.certificate-chaining-engine-cce.aspx
This article describes what is certificate chaining engine (CCE) and how it works in Windows in general. It is based on RFC5280 and Microsoft-specific implementation of certificate chaining engine. .NET uses native CryptoAPI functions, so X509Chain behaves in the same way as in native CryptoAPI.
The X509Chain does not work reliably for scenarios where you do not have the root certificate in the trusted CA store on the machine.
Others will advocate using bouncy castle. I wanted to avoid bringing in another library just for this task, so I wrote my own.
As see in RFC3280 Section 4.1 the certificate is a ASN1 encoded structure, and at it's base level is comprised of only 3 elements.
The "TBS" (to be signed) certificate
The signature algorithm
and the signature value
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
C# actually has a handy tool for parsing ASN1, the System.Formats.Asn1.AsnDecoder.
Using this, we can extract these 3 elements from the certificate to verify the chain.
The first step was extracting the certificate signature, since the X509Certificate2 class does not expose this information and it is necessary for the purpose of certificate validation.
Example code to extract the signature value part:
public static byte[] Signature(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
var offsetSpan = certificateSpan[(tbsOffset + tbsLength)..];
AsnDecoder.ReadSequence(
offsetSpan,
encodingRules,
out var algOffset,
out var algLength,
out _
);
return AsnDecoder.ReadBitString(
offsetSpan[(algOffset + algLength)..],
encodingRules,
out _,
out _
);
}
The next step is to extract the TBS certificate. This is the original data which was signed.
example code to extract the TBS certificate data:
public static ReadOnlySpan<byte> TbsCertificate(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
// include ASN1 4 byte header to get WHOLE TBS Cert
return certificateSpan.Slice(tbsOffset - 4, tbsLength + 4);
}
You may notice that when extracting the TBS certiifcate I needed to include the ASN1 header in the data, this is because the signature of the TBS Certificate INCLUDES this data (this annoyed me for a while).
For the first time in history, the Microsoft does not impede us with their API design, and we are able to obtain the Signature Algorithm directly from the X509Certificate2 object. Then we just need to decide to what extend we are going to implement different hash algorithms.
var signature = signed.Signature();
var tbs = signed.TbsCertificate();
var alg = signed.SignatureAlgorithm;
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad
switch (alg)
{
case { Value: var value } when value?.StartsWith("1.2.840.113549.1.1.") ?? false:
return signedBy.GetRSAPublicKey()?.VerifyData(
tbs,
signature,
value switch {
"1.2.840.113549.1.1.11" => HashAlgorithmName.SHA256,
"1.2.840.113549.1.1.12" => HashAlgorithmName.SHA384,
"1.2.840.113549.1.1.13" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
RSASignaturePadding.Pkcs1
) ?? false;
case { Value: var value } when value?.StartsWith("1.2.840.10045.4.3.") ?? false:
return signedBy.GetECDsaPublicKey()?.VerifyData(
tbs,
signature,
value switch
{
"1.2.840.10045.4.3.2" => HashAlgorithmName.SHA256,
"1.2.840.10045.4.3.3" => HashAlgorithmName.SHA384,
"1.2.840.10045.4.3.4" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
DSASignatureFormat.Rfc3279DerSequence
) ?? false;
default: throw new UnsupportedSignatureAlgorithm(alg);
}
As shown in the code above, https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad is a good resource to see the mapping of algorithms and OIDs.
Another thing you should be aware of is that there are some articles out there that claim that for elliptical curve algorithms, microsoft expects a R,S formatted key instead of a DER formatted key. I tried to convert the key to this format but it ultimately didn't work. What I discovered was that it was necessary to use the DSASignatureFormat.Rfc3279DerSequence parameter.
Additional certificate checks, like "not before" and "not after", or CRL and OCSP checks can be done in addition to the chain verification.

Node.js Password Encryption Same As In Asp.Net

I have created a wesite in asp.net and use ms-sql database to save the records. Now want to convert it in node.js application. And want to use same sql database. In asp.net application I have encrypt the password for registered user. Below is code.
public static string CreateHash(string unHashed)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] data = System.Text.Encoding.ASCII.GetBytes(unHashed);
data = x.ComputeHash(data);
return System.Text.Encoding.ASCII.GetString(data);
}
public static bool MatchHash(string HashData, string HashUser)
{
HashUser = CreateHash(HashUser);
if (HashUser == HashData)
return true;
else
return false;
}
Now problem is that how I use same encryption in node.js. So when node application is ready old user can also make login. It only possible if node app also use same encryption that I have use in asp.net.
For node I have created all environment and use mssql module for database communication. Please help me fix that. Thanks!!
First of all MD5 shall no longer be used if you are serious about security.
Based on your comment and code, I fear there is a 'data loss' in the initial ASP.net code.
Let us have a look at CreateHash function again, I've added comments:
public static string CreateHash(string unHashed)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
// Convert unHashed string to bytes using ASCII coding
byte[] data = System.Text.Encoding.ASCII.GetBytes(unHashed);
// Compute MD5 hash from bytes
data = x.ComputeHash(data);
// Decode MD5 resulting bytes as ASCII
return System.Text.Encoding.ASCII.GetString(data);
}
The last line confuses me, it is decoding bytes received from MD5 function as if they were ASCII, but that is incorrect assumption. And the resulting encoded string as you gave in comment contains lots of "?'s".
Next node.js code will do similar except encode the string using hex rather than ascii:
var crypto = require('crypto')
function createHash(data) {
return crypto.createHash('md5').update(data, 'ascii').digest('hex')
}
To emulate "bytes to ascii" you could try .digest('binary') instead of hex. If it does not give what you expect, then you have to make a separate 'conversion' step from hex to ascii. (I am not experienced enough to give you elegant solution to the later one)

Protecting my self from cross-site scripting

I have implemented a Request.QueryString["somestr"].ToString();
I suppress cross site scripting by doing HttpUtility.HtmlEncode(Request.QueryString["somestr"].ToString();
I still have an issue where a user can do:
myfriendlydomain.com/?somestr=';alert(WOO XSS SUCCEDED);test='
How can I prevent this from happening?
As requested:
//Code Behind
if(request.querystring["somestr"] != null)
{
AffiliatesEmail = HttpUtility.HtmlEncode(Request.QueryString["somestr"].ToString();
}
//Front End
<script type="text/javascript">
//<![CDATA[
/*** Do not change ***/
var SomeVAR = {};
SomeVAR.Tracking.Sale.orderRef = '<%= AffiliatesEmail %>';
//]]>
</script>
<script src="https://www.somethirdparty.com/somejscript.js" type="text/javascript" defer="defer"> </script>
This is our implementation. Anything afterwards I do not believe is relevant.
You can use the JavaScriptStringEncode() Method to scrub the string and encode it to prevent this from happening.
Another way is to use the AntiXSS library.
By knowing the context in which you are using the AffiliatesEmail string, it helps to know how thorough you have to be in validating and sanitising the string.
Let's say for example, that we know AffiliatesEmail was only valid if it were numeric. That way, you'd be protected if you rejected any Request.QueryString["somestr"] which didn't validate as a number.
Now, I suspect that AffiliatesEmail is in fact supposed to be a valid email address.
Using that knowledge, we can now validate it as an email address and reject everything else:
using System.Net.Mail;
try
{
MailAddress ma = new MailAddress(AffiliatesEmail);
}
catch (FormatException fe)
{
//Email isn't valid, so don't output it to the client!!!
}
The code above simply validates whether the string is an email address (as defined by .NET) - if it's not, then we don't need to worry about what it is, because we simply don't trust it.
So don't get too hung up on santising everthing that gets put in the querystring - by simply knowing the bounds of what is acceptable, you can avoid complex regexes and XSS-cleaning routines.
You need to validate every querystring input to make sure you have valid data coming in. I wouldn't write the value directly out to a page, either.

Resources