Crypter Component

By Christopher E. Franklin (Thades)
I never could get MySQL's encryption routines to work properly for me so, I created this component to help me out. This is based on the fact that I needed encryption as well as decryption. If you have the same problems and need this type of functionality, feel free to use this.

This code is the example on http://us.php.net/manual/en/ref.mcrypt.php rolled into a component. This works wonderfully.

Please note that this code has not been tested using cake's testing facility and the code is provided AS IS.

The mcrypt module must be enabled in the php.ini!

Please visit http://us.php.net/manual/en/ref.mcrypt.php for more information

Download code
<?php
class CrypterComponent extends Object {
    
/**
     * ! WARNING ! WARNING ! WARNING ! WARNING ! WARNING ! WARNING ! WARNING !
     *
     * Do not change this key after we are in production unless you know what
     * the hell you are doing!!  If you change this key, no one can process
     * payments!
     *
     * If you want to change your key every so often, decrypt all the data that
     * has been previously encrypted and store as plain text somewhere, change
     * the key and then re-encrypt it.
     *
     * ! WARNING ! WARNING ! WARNING ! WARNING ! WARNING ! WARNING ! WARNING !
     */
    
var $key 'PuTyOuRK3yHeRe'; <----change this.

    var 
$name "Crypter";
    
/**
     * This function will encrypt the string that is passed to it
     *
     * @param String $data The string to be encrypted.
     * @return String Returns the encrypted string or false
     * @access public
     */
    
function enCrypt($data null) {
        if (
$data != null) {
            
// Make an encryption resource using a cipher
            
$td mcrypt_module_open('cast-256''''ecb''');
            
// Create and encryption vector based on the $td size and random
            
$iv mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
            
// Initialize the module using the resource, my key and the string vector
            
mcrypt_generic_init($td$this->key$iv);
            
// Encrypt the data using the $td resource
            
$encrypted_data mcrypt_generic($td$data);
            
// Encode in base64 for DB storage
            
$encoded base64_encode($encrypted_data);
            
// Make sure the encryption modules get un-loaded
            
if (!mcrypt_generic_deinit($td) || !mcrypt_module_close($td)) {
                
$encoded false;
            }
        } else {
            
$encoded false;
        }
        return 
$encoded;
    }
    
/**
     * This function will de-crypt the string that is passed to it
     *
     * @param String $data The string to be encrypted.
     * @return String Returns the encrypted string or false
     */
    
function deCrypt($data null) {
        if (
$data != null) {
            
// The reverse of encrypt.  See that function for details
            
$data = (string) base64_decode(trim($data));
            
$td mcrypt_module_open('cast-256''''ecb''');
            
$iv mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
            
mcrypt_generic_init($td$this->key$iv);
            
$data = (string) trim(mdecrypt_generic($td$data));
            
// Make sure the encryption modules get un-loaded
            
if (!mcrypt_generic_deinit($td) || !mcrypt_module_close($td)) {
                
$data false;
            }
        } else {
            
$data false;
        }
        return 
$data;
    }
    
    
/**
     * This method will mask a credit card number
     *
     * @param String $cardNumber The card number to be masked
     * @return String The masked card number
     * @access public
     */
    
function maskCardNumber($cardNumber) {
        
$cardArray str_split($cardNumber);
        
$length count($cardArray);
        
$maskedCardNumber "";
        
// Mask all numbers except the last 4
        
for ($i 0$i $length -4$i++) {
            
$cardArray[$i] = 'X';
        }
        
// Turn back into a string
        
for ($i 0$i $length$i++) {
            
$maskedCardNumber $maskedCardNumber $cardArray[$i];
        }
        return 
$maskedCardNumber;
    }
}
?>

 

Comments 600

CakePHP Team Comments Author Comments
 

Question

1 Purpose of maskCardNumber() loops

Thanks for posting this component.

Question: I'm not understanding the reasoning for the loops in maskCardNumber()? Why not just:

function maskCardNumber($cardNumber) {
    return 'XXXXXXXXXXXX' . substr( $cardNumber, -4 );
}

Thanks again.
Posted Jan 15, 2008 by Joel Mama
 

Comment

2 Inline maskCardNumber Suggestion

@Joel
To be honest, I am not sure if there are any credit card numbers out there longer than 16 characters. I guess this would be a good implementation until (if there aren't already) cards longer than 16. Do you know of any?

With this loop, you can have a card number longer than 16. I really don't know how the rest of the credit cards throughout the world look =)

Thanks for posting this component.

Question: I'm not understanding the reasoning for the loops in maskCardNumber()? Why not just:

function maskCardNumber($cardNumber) {
    return 'XXXXXXXXXXXX' . substr( $cardNumber, -4 );
}

Thanks again.
Posted Jan 15, 2008 by Christopher E. Franklin
 

Comment

3 19 character max

@Joel
To be honest, I am not sure if there are any credit card numbers out there longer than 16 characters. I guess this would be a good implementation until (if there aren't already) cards longer than 16. Do you know of any?

In theory, they can be up to 19 characters under the current standard, but I don't know of anyone currently using over 16.
Posted Jan 16, 2008 by Matthew Walker
 

Comment

4 Credit card length

Might I suggest the following?

return str_pad(
substr($cardNumber, -4),
strlen($cardNumber)-4,
'X',
STR_PAD_LEFT
);

Haven't tested it, but the idea should do just fine.
Posted Jan 17, 2008 by Brad Beattie
 

Question

5 Validate and encrypt

How do you accomplish both validation and encryption? I tried using the encryption beforeSave in the model, but that, of course, doesn't work. I am currently saving the encrypted credit card separately upon successfully validating and saving all fields. But, I'm thinking that there must be a better way than that.

Thanks!
Posted Feb 19, 2008 by Brendan Sonnichsen
 

Comment

6 Validate and Encrypt Reply

How do you accomplish both validation and encryption? I tried using the encryption beforeSave in the model, but that, of course, doesn't work. I am currently saving the encrypted credit card separately upon successfully validating and saving all fields. But, I'm thinking that there must be a better way than that.

Thanks!

From my point of view, and I still believe this is CakePHP's point of view, is that the component is to the controller what the helper is to the view.

So, if I am understanding the question correctly, you want the credit card encrypted using beforeSave. Based on that just copy the encryption and paste it into your model. In the before save, call the function. This way, it passed validation and then it will encrypt before the save.

You cannot call a component method in the model unless you specifically instantiate the object within the model.

Is this the answer to your question?
Posted Feb 20, 2008 by Christopher E. Franklin
 

Comment

7 Validate and Encrypt Reply

Is this the answer to your question?
Yes. That's it. I have to set the key in both the model and the component now. Is there a way that would be more ideal?

Thank you very much!
Posted Feb 20, 2008 by Brendan Sonnichsen
 

Comment

8 No

Is this the answer to your question?
Yes. That's it. I have to set the key in both the model and the component now. Is there a way that would be more ideal?

Thank you very much!

Well, once the component is loaded, the sky is the limit! The only thing you have to remember is that the model is not designed to access the component and vice-versa.
Off the top of my head there are tons of ways to get around this but, for the sake of keeping to convention, you should store it in both places.
There is nothing saying that you cannot make a "Crypter Model" and just load that in the controller when you want the encryption functions but, model to model won't work again.
One of them is going to have to do it and the only two things that interact are the controller and model.
Don't know what else to tell you except that if you really wanted to, load the component in the model but, don't expect support from the cake community if you do that ;)
Posted Feb 21, 2008 by Christopher E. Franklin