In 138b, this will encrypt/decrypt anything using the Vigenère cypher. Input is entered as solely lower case letters (no numbers, spaces, special characters etc). Outputs may be verified against http://sharkysoft.com/misc/vigenere/
As described on http://ub.ly/um (the parser chokes on the full URL), the essence of the Vigenère cypher is that it is a cyclic version of the Caesar shift cypher. It's not unbreakable but it's pretty good. Also, as a corollary we get rot13 (use "n" as an encoding parameter - yes, as originally defined there's a one byte offset from what you might expect...) and of course Caesar.
My initial attempts were encryption only (138b):
function(j,s,h,g,i,n){for(g="",i=0;i-j.length;g+=String.fromCharCode(n?n+64:90))n=(j.charCodeAt(i)+s.charCodeAt(i++%s.length)+15)%26;h(g)}
and encryption + decryption (151b):
function(j,s,h,t,g,i,n){for(g="",i=0;i-j.length;g+=String.fromCharCode(n?n+64:90))n=(j.charCodeAt(i)+t*s.charCodeAt(i++%s.length)+(t>0?15:27))%26;h(g)}
with t=+/- 1 being encryption/decryption. Thanks to all the help in the comments (especially @subzey!) this is now down to 138b, with both encryption and decryption.
The phrases "fromCharCode", "charCodeAt" and "String" are FAR too long =(
Savings don't always come where you expect them to (see @jed's ultimately unused 'harCode' hack in the comments). In fact, from my experience so far in 140byt.es, they most often come from some slight (or indeed major!) reworking of the algorithm (c.f. the successive contributions to fibonacci, base64 enc/dec etc).
http://esparser.qfox.nl/ is amazing
"Cypher" also wrong. Use "cipher".