2

I have been working on trying to implement an encryption mechanism for passing secure information on my website. My host charges extra for SSL, and I am not ready for the extra monetary commitment.

I tried to use pidCrypt to encrypt the values on the client side via javascript. Then, I have tried several techniques for unencrypting on the PHP side. For some reason, the data just gets garbled.

Can someone point out what I am doing wrong? Or, should I use a different javascript library for the encryption? Any advice?

Here's the javascript code that pulls the text to encrypt from an input on the page and the public key from a hidden text area on the page.

$(document).ready(function() {
  $('button').click(function() {
    var dataToSend = new Object();

    var input = $('input[name=textToEncrypt]').val();
    var public_key = $('textarea[name=publicKey]').val();
    var params = certParser(public_key);
        var key = pidCryptUtil.decodeBase64(params.b64);
    //new RSA instance
    var rsa = new pidCrypt.RSA();
    //RSA encryption
    //ASN1 parsing
    var asn = pidCrypt.ASN1.decode(pidCryptUtil.toByteArray(key));
    var tree = asn.toHexTree();
    //setting the public key for encryption
    rsa.setPublicKeyFromASN(tree);
    var t = new Date();  // timer
    crypted = rsa.encrypt(input);
    dataToSend.unencrypted = input;
    dataToSend.textToDecrypt = pidCryptUtil.fragment(pidCryptUtil.encodeBase64(pidCryptUtil.convertFromHex(crypted)),64);
    $('body').append(dataToSend.textToDecrypt);


    $.getJSON('engine.php', dataToSend, function(data) {
      var items = [];

      $.each(data, function(key, val) {
         items.push('<li id="' + key + '">' + key + ': ' + val + '</li>');
      });

      $('<ul/>', {
        'class': 'my-new-list',
        html: items.join('')
      }).appendTo('body');
    });


  });
});

This is my engine.php code that is supposed to decrypt the value. Notice that I have tried several different ways from different examples.

<?php
   require_once 'private/keys.php';



function EncryptData($source)
{
  /*
   * NOTE:  Here you use the $pub_key value (converted, I guess)
   */
  $key = $DEkeys->pubKey;
  openssl_public_encrypt($source,$crypttext,$key);
  return(base64_encode($crypttext));
}

function DecryptData($source)
{
  /*
   * NOTE:  Here you use the returned resource value
   */
  $decoded_source = base64_decode($source);
  openssl_private_decrypt($decoded_source,$newsource,$DEkeys->privKey);
  return($newsource);
}

function EncryptData2($source)
{
  $fp=fopen("/pathtokey/public.pem","r");
  $pub_key=fread($fp,8192);
  fclose($fp);
  openssl_get_publickey($pub_key);
  /*
   * NOTE:  Here you use the $pub_key value (converted, I guess)
   */
  openssl_public_encrypt($source,$crypttext,$pub_key);
  return(base64_encode($crypttext));
}

function DecryptData2($source)
{
  #print("number : $number");
  $fp=fopen("/pathtokey/private.pem","r");
  $priv_key=fread($fp,8192);
  fclose($fp);
  // $passphrase is required if your key is encoded (suggested)
  $res = openssl_get_privatekey($priv_key);
  /*
   * NOTE:  Here you use the returned resource value
   */
  $decoded_source = base64_decode($source);
  openssl_private_decrypt($decoded_source,$newsource,$res);
  return($newsource);
}

$out = new stdClass;

$out->hello = 'hello, world!';

if(!empty($_GET["textToDecrypt"])) {
   $out->raw = $_GET['textToDecrypt'];
   $out->unencrypted = $_GET['unencrypted'];
     if($DEkeys->privKey == false) {
       $out->error = 'Could not read private key';
     }
     $out->success = openssl_private_decrypt(base64_decode($out->raw), $decrypted, $DEkeys->privKey);
     $out->decrypted = $decrypted;
     $out->dec2 = DecryptData2($out->raw);
     $out->test1 = EncryptData2('testing');
     $out->test2 = DecryptData2($out->test1);
} else {
   $out->nondata = $_GET['textToDecrypt'];
}


echo json_encode($out);

When I enter "test" for the value to decrypt, the PHP shows: - decrypted: dGVzdA== - dec2: dGVzdA==

So, neither the openssl_private_decrypt() nor the DecryptData2() functions will correctly decrypt the values. The EncryptData2() and DecryptData2() will work together though.

Is it possible I am missing something small? Any advice?

Edit: Here are the commands I used to create the keys --

This creates the private key:

 openssl genrsa -out private.pem 1024

This creates the public key:

 openssl rsa -in private.pem -pubout > public.pem
K-G
  • 2,799
  • 4
  • 26
  • 42
digitaleagle
  • 854
  • 1
  • 9
  • 15
  • Crypto rule #1: Don't roll your own crypto. – Alex Howansky Aug 30 '11 at 17:13
  • @Alex Irrelevant- He's using established encryption routines here, no rolling his own. digitaleagle You sure the public/private keys match and are in a usable format for both systems? (byte arrays vs decimal vs hex vs base64 etc) – Rudu Aug 30 '11 at 17:26
  • Don't do it. Dump your host and get one that supports SSL. Here's why: http://stackoverflow.com/questions/3604582/which-attacks-are-possible-concerning-my-security-layer-concept/3604619#3604619 – NullUserException Aug 30 '11 at 18:10
  • @Rudu- I am pretty sure the keys match. I was attempting to run that out by using EncryptData() and DecryptData() functions together. They can encrypt and decrypt "test" successfully. Maybe I will try to regenerate new keys and include how I did that tonight. – digitaleagle Aug 30 '11 at 21:25
  • @NullUserException - I am seriously considering that, but I have 3 months or so left on my contract. Currently, I am with Lunarpages. I noticed that GoDaddy has a plan that includes SSL that I want to look into. I just wanted something to reduce the risk until that time. – digitaleagle Aug 30 '11 at 21:28
  • Anyone who happens upon this nowadays can checkout StartSSL for free basic SSL certificates. DON'T IMPLEMENT TLS YOURSELF! – Ronnie Overby Aug 28 '15 at 19:02
  • Don't forget that purchasing the certificate is only half the battle. From what I had seen, most of the hosts have an installation charge to install the certificate. And, you have to have a static IP address, which costs more per month. One really good option that many hosts have is a shared SSL account. You have to use the host's domain name to access the website, but it is good for administration kinds of things on the site, which is all I needed it for. – digitaleagle Aug 29 '15 at 21:45

4 Answers4

2

Try the following simple example. I used it only to encrypt a password but you could use it for the whole form as well.

It is using a open source javascript library https://github.com/ziyan/javascript-rsa

HTML/JAVASCRIPT:

<script language="JavaScript" type="text/javascript" src="jsbn.js"></script>
<script language="JavaScript" type="text/javascript" src="rsa.js"></script>

<script language="JavaScript">

    function encryptData(){

        //Don't forget to escape the lines:
        var pem="-----BEGIN PUBLIC KEY-----\
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfmlc2EgrdhvakQApmLCDOgP0n\
NERInBheMh7J/r5aU8PUAIpGXET/8+kOGI1dSYjoux80AuHvkWp1EeHfMwC/SZ9t\
6rF4sYqV5Lj9t32ELbh2VNbE/7QEVZnXRi5GdhozBZtS1gJHM2/Q+iToyh5dfTaA\
U8bTnLEPMNC1h3qcUQIDAQAB\
-----END PUBLIC KEY-----";

        var key = RSA.getPublicKey(pem);

        element=document.getElementById('password');
        element.value=RSA.encrypt(element.value, key);
    }
</script>

<form method='POST' id='txtAuth' onsubmit='encryptData()'>
    <input type='text' name='username'/>
    <input type='password' name='password' id='password' placeholder="password"/>
    <input name='submit' type='submit' value='Submit'>
</form>

PHP:

<?php

if (isset($_POST['password'])) {

    //Load private key:
    $private = "-----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQDfmlc2EgrdhvakQApmLCDOgP0nNERInBheMh7J/r5aU8PUAIpG
    XET/8+kOGI1dSYjoux80AuHvkWp1EeHfMwC/SZ9t6rF4sYqV5Lj9t32ELbh2VNbE
    /7QEVZnXRi5GdhozBZtS1gJHM2/Q+iToyh5dfTaAU8bTnLEPMNC1h3qcUQIDAQAB
    AoGAcbh6UFqewgnpGKIlZ89bpAsANVckv1T8I7QT6qGvyBrABut7Z8t3oEE5r1yX
    UPGcOtkoRniM1h276ex9VtoGr09sUn7duoLiEsp8aip7p7SB3X6XXWJ9K733co6C
    dpXotfO0zMnv8l3O9h4pHrrBkmWDBEKbUeuE9Zz7uy6mFAECQQDygylLjzX+2rvm
    FYd5ejSaLEeK17AiuT29LNPRHWLu6a0zl923299FCyHLasFgbeuLRCW0LMCs2SKE
    Y+cIWMSRAkEA7AnzWjby8j8efjvUwIWh/L5YJyWlSgYKlR0zdgKxxUy9+i1MGRkn
    m81NLYza4JLvb8/qjUtvw92Zcppxb7E7wQJAIuQWC+X12c30nLzaOfMIIGpgfKxd
    jhFivZX2f66frkn2fmbKIorCy7c3TIH2gn4uFmJenlaV/ghbe/q3oa7L0QJAFP19
    ipRAXpKGX6tqbAR2N0emBzUt0btfzYrfPKtYq7b7XfgRQFogT5aeOmLARCBM8qCG
    tzHyKnTWZH6ff9M/AQJBAIToUPachXPhDyOpDBcBliRNsowZcw4Yln8CnLqgS9H5
    Ya8iBJilFm2UlcXfpUOk9bhBTbgFp+Bv6BZ2Alag7pY=
    -----END RSA PRIVATE KEY-----";
    if (!$privateKey = openssl_pkey_get_private($private)) die('Loading Private Key failed');

    //Decrypt
    $decrypted_text = "";
    if (!openssl_private_decrypt(base64_decode($_POST['password']), $decrypted_text, $privateKey)) die('Failed to decrypt data');

    //Decrypted :) 
    var_dump($decrypted_text);

    //Free key
    openssl_free_key($privateKey);
}
?>

Enjoy!

Tieme
  • 62,602
  • 20
  • 102
  • 156
  • 1
    As far as using this for the entire form, remember that you need a large key to encrypt a lot of data. A 1024 bit key - which is fast to use - only encrypts 117 characters of plain text. So, yes, perhaps use for form data, but only if your form is small or you encrypt each field individually (likely a slow process). – Geek Stocks Aug 09 '15 at 03:12
1

Your decrypted values are base64 encoded because pidCrypt uses base64 encoding to ensure 8 bit characters prior to RSA encryption. So simply base64-decode your results.

See https://sourceforge.net/projects/pidcrypt/forums/forum/923749/topic/3153476

Jonah
  • 11
  • 1
0

You cannot securely encrypt anything on the client side. This is because the client has full control over any data that will be send, as well as the crypto engine.

There has been some debate about this in the past, and the conclusion is always the same. It cannot be done in any secure manner.

The question you should ask yourself is: what are you trying to protect yourself/your clients from?
If you try to protect yourself from people sniffing the wire or tampering with the request-data, the only viable solution is SSL.
If anything else, crypto is not the solution to look for.

(on a side note, the decrypted string is base64 encoded, try base64_decode)

Jacco
  • 23,534
  • 17
  • 88
  • 105
  • I am hoping to prevent someone from easily viewing things such as a username and password if I connect at a restaurant or some public network. My thought was to use a Public key/Private key so that if they sniff the network they would not be able to easily see the information. I understand this is not safe from a man-in-the-middle attack. If they write something to pull the public key off the page, they could substitute their own key and get the information that way. – digitaleagle Aug 30 '11 at 21:19
  • If they write something that could substitute the key, they could just as well modify the script to just send them the password in plain text. Honestly, what you are trying is noble, but will not add any real security, just obscurity. If you are content with that, go ahead. Just don't fool yourself in thinking it is anything but (complex) smoke and mirrors. – Jacco Aug 31 '11 at 07:30
  • What I don't understand is why would Web Hosting companies advertise a product geared for bloggers that does not include SSL? For example: http://www.lunarpages.com/web-hosting/basic-hosting/ – digitaleagle Aug 31 '11 at 21:37
  • This solution at least inhibits someone using a tool like [Firesheep](http://codebutler.github.com/firesheep/) on the same public network (i.e. restaurant) doesn't it? For anything more, my protection would simply be good backups. I don't have any data worth stealing. Or am I not thinking right? – digitaleagle Aug 31 '11 at 21:43
  • Well, your users' passwords are probably the most valuable data. (people reuse passwords a lot) so yes, you would prevent cleartext passwords from being sniffed. However, the sniffer does not need the password, he/she needs the sessionId. Also, with the construction you choose, sniffing the encrypted password and resending it at a later time is just as good as knowing the plaintext password. What you are trying has been tried a lot, by many people (me including) and in the end, it always turns out SSL is the only viable option. – Jacco Sep 01 '11 at 07:37
0

I ended up using Open ID. It's not secure, but at least it is a little better than nothing. The specific implementation that I found was LightOpenID.

I will be choosing a different web host when it is time to renew that will allow me to use SSL in an affordable fashion.

I never did figure out why my encryption code would not work.

digitaleagle
  • 854
  • 1
  • 9
  • 15