Forum OpenACS Q&A: example: using openssl to encrypt/decrypt cc information

I built these functions two encrypt and decrypt credit card information using openssl. I'm going to ask some questions about these functions in the next thread.

Is there a file storage module on the new openacs server?

Would you send me your credit card information so I can test these functions?

If you help me improve these functions, I will happily send you some credit card numbers that will work with verisign servers.

ad_proc cc_encrypt {
    passphrase  crypto  cc_number  cc_name  cc_type
    cc_exp_month  cc_exp_year  cc_address
    cc_city  cc_state  cc_zip
} {
    Encrypts the cc information according to various crypto parameters
    and crypto schemes.  Schemes: Plaintext (nothing) and openssl-bf (blowfish)

} {

    set plaintext "${cc_number}__:__${cc_name}__:__${cc_type}__:__${cc_exp_month}__:__${cc_exp_year}__:__${cc_address}__:__${cc_city}__:__${cc_state}__:__${cc_zip}"

    switch $crypto {
        plaintext -
        default {
            # plaintext noop
            return $plaintext
        }
        openssl-bf {

            set plainfilename [ns_mktemp /tmp/cc-XXXXXX]
            set plainfd [open $plainfilename w]
            puts -nonewline $plainfd $plaintext
            close $plainfd
            
            set cryptfilename ${plainfilename}.crypt
            exec openssl bf -pass pass:${passphrase} -in $plainfilename -out $cryptfilename
            file delete $plainfilename
            
            set cryptfd [open $cryptfilename r]
            fconfigure $cryptfd -translation binary
            set crypt [read $cryptfd]
            close $cryptfd
            
            file delete $cryptfilename

            return $crypt
        }
    }
}

ad_proc cc_decrypt {
    passphrase
    crypto
    encrypted_string
} {
    Decrypts the cc information according to various crypto parameters
    and crypto schemes.  Schemes: plaintext and openssl-bf (blowfish)

} {
    switch $crypto {
        plaintext -
        default {
            # plaintext noop
            set decrypted_string $encrypted_string
        }
        openssl-bf {

            set cryptfilename [ns_mktemp /tmp/cc-de-XXXXXX]
            set cryptfd [open $cryptfilename w]
            fconfigure $cryptfd -translation binary
            puts -nonewline $cryptfd $encrypted_string
            close $cryptfd
            
            set plainfilename ${cryptfilename}.plain
            exec openssl bf -pass pass:${passphrase} -in $cryptfilename -out $plainfilename -d
            
            set plainfd [open $plainfilename r]
            set decrypted_string [read $plainfd]
            close $plainfd
            
            file delete $plainfilename
            file delete $cryptfilename

        }
    }

    if {[regexp {^(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)$} $decrypted_string match cc_number cc_name cc_type cc_exp_month cc_exp_year cc_address cc_city cc_state cc_zip]} {
        return [list cc_number $cc_number cc_name $cc_name cc_type $cc_type cc_exp_month $cc_exp_month cc_exp_year $cc_exp_year cc_address $cc_address cc_city $cc_city cc_state $cc_state cc_zip $cc_zip]
    } else {
        return [list error error]
    }
}

Same functions, but now they also translate the binary into base64 making it possible to both eliminate the temporary file nonsense and making it easier to stuff these things into the database.
ad_proc cc_encrypt {
    passphrase  crypto  cc_number  cc_name  cc_type
    cc_exp_month  cc_exp_year  cc_address
    cc_city  cc_state  cc_zip
} {
    Encrypts the cc information according to various crypto parameters
    and crypto schemes.  Plaintext (nothing) and openssl bf

} {
    set plaintext "${cc_number}__:__${cc_name}__:__${cc_type}__:__${cc_exp_month}__:__${cc_exp_year}__:__${cc_address}__:__${cc_city}__:__${cc_state}__:__${cc_zip}"

    switch $crypto {
        plaintext -
        default {
            # plaintext noop
            return $plaintext
        }
        openssl-bf {
            set crypt [exec echo $plaintext | openssl bf -pass pass:${passphrase} | openssl base64]
            return $crypt
        }
    }
}

ad_proc cc_decrypt {
    passphrase
    crypto
    encrypted_string
} {
    Decrypts the cc information according to various crypto parameters
    and crypto schemes.  Currently implements plaintext and openssl-bf

} {
    switch $crypto {
        plaintext -
        default {
            # plaintext noop
            set decrypted_string $encrypted_string
        }
        openssl-bf {
            set decrypted_string [exec echo $encrypted_string | openssl base64 -d | openssl bf -pass pass:${passphrase} -d] 
        }
    }

    if {[regexp {^(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)__:__(.*)$} $decrypted_string match cc_number cc_name cc_type cc_exp_month cc_exp_year cc_address cc_city cc_state cc_zip]} {
        return [list cc_number $cc_number cc_name $cc_name cc_type $cc_type cc_exp_month $cc_exp_month cc_exp_year $cc_exp_year cc_address $cc_address cc_city $cc_city cc_state $cc_state cc_zip $cc_zip]
    } else {
        return [list error error]
    }
}

Scott Goodwin pointed me to ns_encrypt, an AOLserver module available at sourceforge that will encrypt a string using des3, bf, idea, or public key mechanisms.

And of course the advantage is that it would not require a fork for each card procesed.

Hey Jerry,

Did you manage to make ns_encrypt work fine? I can't seem to get it to encrypt/decrypt text larger than the key (which is the RSA limitation) but even using blowfish:

set ciphertext [ns_encrypt -blowfish -keysize 448 -public $original_text]
kills my AOLServer when sending a larger text (just a few hundred characters) with:
alloc: invalid block: 0x2a18c00: ef ef 0

Abort trap
Any luck with this? Or you ended up using OpenSSL?

Thanks in advance.

The only drawback of this approach is that if you have a doublequote character, the "echo" command will get confused. I really would prefer to have something like "ns_encrypt" working but I'll need to get deeper into it.