keyczar.js
function _makeKeyczar(data, password) {
    var instance = {};

    instance.metadata = JSON.parse(data.meta);
    if (instance.metadata.encrypted !== false) {
        if (!password) {
            throw new Error('Key is encrypted; you must provide the password');
        }
        if (password.length === 0) {
            throw new Error('Must supply a password length > 0');
        }
    } else if (password) {
        throw new Error('Key is not encrypted but password provided');
    }

    var primaryVersion = _getPrimaryVersion(instance.metadata);
    var primaryKeyString = data[String(primaryVersion)];
    if (instance.metadata.encrypted) {
        primaryKeyString = _decryptKey(primaryKeyString, password);
    }

    var t = instance.metadata.type;
    var p = instance.metadata.purpose;
    if (t == keyczar.TYPE_RSA_PRIVATE) {
        instance.primary = keyczar_util.privateKeyFromKeyczar(primaryKeyString);
        instance.exportPublicKey = function() { return _exportPublicKey(instance); };
    } else if (t == keyczar.TYPE_RSA_PUBLIC) {
        instance.primary = keyczar_util.publicKeyFromKeyczar(primaryKeyString);
    } else if (t == keyczar.TYPE_AES && p == keyczar.PURPOSE_DECRYPT_ENCRYPT) {
        instance.primary = keyczar_util.aesFromKeyczar(primaryKeyString);
    } else {
        throw new Error('Unsupported key type: ' + t);
    }

    if (p == keyczar.PURPOSE_ENCRYPT || p == keyczar.PURPOSE_DECRYPT_ENCRYPT) {
        // Takes a raw byte string, returns a raw byte string
        instance.encryptBinary = function(plaintext) {
            // TODO: assert that plaintext does not contain special characters
            return instance.primary.encrypt(plaintext);
        };

        instance.encrypt = function(plaintext) {
            // encode as UTF-8 in case plaintext contains non-ASCII characters
            plaintext = forge.util.encodeUtf8(plaintext);
            var message = instance.encryptBinary(plaintext);
            message = keyczar_util.encodeBase64Url(message);
            return message;
        };

        // only include decryption if supported by this key type
        if (p == keyczar.PURPOSE_DECRYPT_ENCRYPT) {
            instance.decryptBinary = function(message) {
                return instance.primary.decrypt(message);
            };

            instance.decrypt = function(message) {
                message = keyczar_util.decodeBase64Url(message);
                var plaintext = instance.primary.decrypt(message);
                plaintext = forge.util.decodeUtf8(plaintext);
                return plaintext;
            };
        }
    } else if (p == keyczar.PURPOSE_VERIFY || p == keyczar.PURPOSE_SIGN_VERIFY) {
        instance.verify = function(message, signature) {
            message = forge.util.encodeUtf8(message);
            signature = keyczar_util.decodeBase64Url(signature);
            return instance.primary.verify(message, signature);
        };

        if (p == keyczar.PURPOSE_SIGN_VERIFY) {
            instance.sign = function(message) {
                message = forge.util.encodeUtf8(message);
                var signature = instance.primary.sign(message);
                return keyczar_util.encodeBase64Url(signature);
            };
        }
    }

    var _toJsonObject = function() {
        var out = {};
        out.meta = JSON.stringify(instance.metadata);

        // TODO: Store and serialize ALL keys. For now this works
        if (instance.metadata.versions.length != 1) {
            throw new Error('TODO: Support keyczars with multiple keys');
        }
        var primaryVersion = _getPrimaryVersion(instance.metadata);
        out[String(primaryVersion)] = instance.primary.toJson();
        return out;
    };

    // Returns the JSON serialization of this keyczar instance.
    instance.toJson = function() {
        if (instance.metadata.encrypted) {
            throw new Error('Key is encrypted; use toJsonEncrypted() instead');
        }
        var out = _toJsonObject();
        return JSON.stringify(out);
    };

    // Returns the decrypted version of this password-protected key.
    // WARNING: This is dangerous as it can be used to leak a password-protected key
    instance.exportDecryptedJson = function() {
        if (!instance.metadata.encrypted) {
            throw new Error('Key is not encrypted; use toJson() instead');
        }

        var unencrypted = _toJsonObject();

        // hack the metadata to mark it as unencrypted
        var meta = JSON.parse(unencrypted.meta);
        meta.encrypted = false;
        unencrypted.meta = JSON.stringify(meta);
        return JSON.stringify(unencrypted);
    };

    instance.toJsonEncrypted = function(password) {
        // TODO: Enforce some sort of minimum length?
        if (password.length === 0) {
            throw new Error('Password length must be > 0');
        }

        // get the unencrypted JSON object
        var unencrypted = _toJsonObject();

        // set metadata.encrypted = true
        var meta = JSON.parse(unencrypted.meta);
        meta.encrypted = true;
        unencrypted.meta = JSON.stringify(meta);

        // encrypt each key
        for (var property in unencrypted) {
            if (property == 'meta') continue;
            unencrypted[property] = _encryptKey(unencrypted[property], password);
        }

        return JSON.stringify(unencrypted);
    };

    return instance;
}
function _makeKeyczar(data, password) {
    var instance = {};

    instance.metadata = JSON.parse(data.meta);
    if (instance.metadata.encrypted !== false) {
        if (!password) {
            throw new Error('Key is encrypted; you must provide the password');
        }
        if (password.length === 0) {
            throw new Error('Must supply a password length > 0');
        }
    } else if (password) {
        throw new Error('Key is not encrypted but password provided');
    }

    var primaryVersion = _getPrimaryVersion(instance.metadata);
    var primaryKeyString = data[String(primaryVersion)];
    if (instance.metadata.encrypted) {
        primaryKeyString = _decryptKey(primaryKeyString, password);
    }

    var t = instance.metadata.type;
    var p = instance.metadata.purpose;
    if (t == keyczar.TYPE_RSA_PRIVATE) {
        instance.primary = keyczar_util.privateKeyFromKeyczar(primaryKeyString);
        instance.exportPublicKey = function() { return _exportPublicKey(instance); };
    } else if (t == keyczar.TYPE_RSA_PUBLIC) {
        instance.primary = keyczar_util.publicKeyFromKeyczar(primaryKeyString);
    } else if (t == keyczar.TYPE_AES && p == keyczar.PURPOSE_DECRYPT_ENCRYPT) {
        instance.primary = keyczar_util.aesFromKeyczar(primaryKeyString);
    } else {
        throw new Error('Unsupported key type: ' + t);
    }

    if (p == keyczar.PURPOSE_ENCRYPT || p == keyczar.PURPOSE_DECRYPT_ENCRYPT) {
        // Takes a raw byte string, returns a raw byte string
        instance.encryptBinary = function(plaintext) {
            // TODO: assert that plaintext does not contain special characters
            return instance.primary.encrypt(plaintext);
        };

        instance.encrypt = function(plaintext) {
            // encode as UTF-8 in case plaintext contains non-ASCII characters
            plaintext = forge.util.encodeUtf8(plaintext);
            var message = instance.encryptBinary(plaintext);
            message = keyczar_util.encodeBase64Url(message);
            return message;
        };

        // only include decryption if supported by this key type
        if (p == keyczar.PURPOSE_DECRYPT_ENCRYPT) {
            instance.decryptBinary = function(message) {
                return instance.primary.decrypt(message);
            };

            instance.decrypt = function(message) {
                message = keyczar_util.decodeBase64Url(message);
                var plaintext = instance.primary.decrypt(message);
                plaintext = forge.util.decodeUtf8(plaintext);
                return plaintext;
            };
        }
    } else if (p == keyczar.PURPOSE_VERIFY || p == keyczar.PURPOSE_SIGN_VERIFY) {
        instance.verify = function(message, signature) {
            message = forge.util.encodeUtf8(message);
            signature = keyczar_util.decodeBase64Url(signature);
            return instance.primary.verify(message, signature);
        };

        if (p == keyczar.PURPOSE_SIGN_VERIFY) {
            instance.sign = function(message) {
                message = forge.util.encodeUtf8(message);
                var signature = instance.primary.sign(message);
                return keyczar_util.encodeBase64Url(signature);
            };
        }
    }

    var _toJsonObject = function() {
        var out = {};
        out.meta = JSON.stringify(instance.metadata);

        // TODO: Store and serialize ALL keys. For now this works
        if (instance.metadata.versions.length != 1) {
            throw new Error('TODO: Support keyczars with multiple keys');
        }
        var primaryVersion = _getPrimaryVersion(instance.metadata);
        out[String(primaryVersion)] = instance.primary.toJson();
        return out;
    };

    // Returns the JSON serialization of this keyczar instance.
    instance.toJson = function() {
        if (instance.metadata.encrypted) {
            throw new Error('Key is encrypted; use toJsonEncrypted() instead');
        }
        var out = _toJsonObject();
        return JSON.stringify(out);
    };

    // Returns the decrypted version of this password-protected key.
    // WARNING: This is dangerous as it can be used to leak a password-protected key
    instance.exportDecryptedJson = function() {
        if (!instance.metadata.encrypted) {
            throw new Error('Key is not encrypted; use toJson() instead');
        }

        var unencrypted = _toJsonObject();

        // hack the metadata to mark it as unencrypted
        var meta = JSON.parse(unencrypted.meta);
        meta.encrypted = false;
        unencrypted.meta = JSON.stringify(meta);
        return JSON.stringify(unencrypted);
    };

    instance.toJsonEncrypted = function(password) {
        // TODO: Enforce some sort of minimum length?
        if (password.length === 0) {
            throw new Error('Password length must be > 0');
        }

        // get the unencrypted JSON object
        var unencrypted = _toJsonObject();

        // set metadata.encrypted = true
        var meta = JSON.parse(unencrypted.meta);
        meta.encrypted = true;
        unencrypted.meta = JSON.stringify(meta);

        // encrypt each key
        for (var property in unencrypted) {
            if (property == 'meta') continue;
            unencrypted[property] = _encryptKey(unencrypted[property], password);
        }

        return JSON.stringify(unencrypted);
    };

    return instance;
}
Returns a Keyczar object from data. 
@param {*} data Deserialized JSON object containing the key
@param {string=} password Used to encrypt the key