OpenSSL::Cipher.new 加密解密

OpenSSL::Cipher.new

application_controller.rb

  def decrypter(data)
    key = ENV['ENCRYPTION_KEY']
    iv = ENV['ENCRYPTION_IV']
    decipher = OpenSSL::Cipher.new('des3')
    decipher.decrypt
    decipher.key = key
    decipher.iv = iv
    decipher.update(Base64.decode64(data)) + decipher.final
  end
module aaaService
  def self.encrypt(plain_text)
    cipher = OpenSSL::Cipher.new('AES-128-CBC')
    cipher.encrypt
    cipher.key = Base64.decode64(ENV['MIDDLxxxx_ENCRYPTION_KEY'])
    cipher.iv = iv = cipher.random_iv
    encrypted = cipher.update(plain_text) + cipher.final
    final_string = iv + encrypted
    CGI.escape(Base64.encode64(final_string))
  end
end


提供加密和解密的对称算法。可用的算法取决于安装的特定版本的 OpenSSL。

列出所有支持的算法
支持的算法列表可以通过这种方式查看

puts OpenSSL::Cipher.ciphers
实例化密码
有几种方法可以创建密码实例。通常,密码算法按其名称,密钥长度(以位为单位)和要使用的密码模式进行分类。创建密码的最普通的方式如下

cipher = OpenSSL::Cipher.new('<name>-<key length>-<mode>')
也就是说,由单个组件名称,密钥长度和模式的连字符组成的串。可以使用全部大写或全部小写字符串,例如:

cipher = OpenSSL::Cipher.new('AES-128-CBC')
对于每种支持的算法,在 Cipher 类下定义一个按密码名称定义的类,例如为了获得 AES 实例,还可以使用

# these are equivalent
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher = OpenSSL::Cipher::AES.new(128, 'CBC')
cipher = OpenSSL::Cipher::AES.new('128-CBC')
最后,由于它的广泛使用,还为 AES 的不同密钥大小定义了额外的类

cipher = OpenSSL::Cipher::AES128.new(:CBC)
cipher = OpenSSL::Cipher::AES192.new(:CBC)
cipher = OpenSSL::Cipher::AES256.new(:CBC)
选择加密或解密模式
对于对称算法,加密和解密通常是非常相似的操作,这反映了无论为哪种操作选择不同的类,都可以使用相同的类来完成。尽管如此,在获得 Cipher 实例后,我们需要告诉实例我们打算如何处理它,所以我们需要调用加密解密方法在密码实例上。

cipher.encrypt
或者

cipher.decrypt
这应该是创建实例后的第一个调用,否则已经设置的配置可能会在进程中丢失。

选择一个key
对称加密要求密钥与加密和解密方相同,并且在初始密钥建立之后应该保留为私人信息。有很多方法来创建不安全的密钥,最值得注意的是只需将密码作为密钥,而不进一步处理密码。为特定密码创建密钥的简单而安全的方法是

cipher = OpenSSL::AES256.new(:CFB)
cipher.encrypt
key = cipher.random_key # also sets the generated key on the Cipher
如果您绝对需要使用密码作为加密密钥,则应使用由 OpenSSL :: PKCS5.pbkdf2_hmac_sha1 或 OpenSSL :: PKCS5.pbkdf2_hmac 提供的功能来生成密钥,以便使用基于密码的密钥派生函数2(PBKDF2)。

虽然有#pkcs5_keyivgen,但它的使用已被弃用,并且只能用于传统应用程序,因为它不使用较新的 PKCS#5 v2算法。

选择 IV
密码模式 CBC,CFB,OFB 和 CTR 都需要一个“初始化向量”,或简称为 IV。ECB 模式是唯一不需要 IV 的模式,但由于它没有充分隐藏纯文本模式,因此该模式几乎没有合法用例。因此

除非绝对确定自己绝对需要,否则不应该使用 ECB 模式

正因为如此,你最终会得到一种在任何情况下都明确需要 IV 的模式。尽管IV可以看作是公共信息,也就是说一旦生成它就可以在公共场合传播,但它仍然不可预知,以防止某些类型的攻击。所以在理想的情况下

对于 密码的每个加密,始终创建一个安全的随机 IV

应该为每个数据加密创建一个新的随机 IV。把 IV 看作一个随机数(使用一次) - 它是公开的但是随机且不可预测的。一个安全的随机IV可以创建如下

cipher = ...
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv # also sets the generated IV on the Cipher
虽然关键一般也是一个随机值,但作为 IV 是一个不好的选择。攻击者可以利用这种IV的方式有详细的方法。根据一般经验,直接或间接暴露密钥应该不惜一切代价予以避免,例外只能有充分的理由。

调用 #final
ECB(不应该使用)和 CBC 都是基于块的模式。这意味着与其他基于流的模式不同,它们在固定大小的数据块上运行,因此它们需要“完成”步骤来通过适当处理某种形式的填充来产生或正确解密最后一块数据。因此,必须将 #final 的输出添加到加密/解密缓冲区,否则最终会出现解密错误或截断数据。

对于流模式密码来说这些并不是完全必要的,但建议采用相同的模式来添加 #final 的输出 - 它还使你能够在将来更容易地切换模式。

加密和解密一些数据
data = "Very, very confidential data"

cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv

encrypted = cipher.update(data) + cipher.final
...
decipher = OpenSSL::Cipher::AES.new(128, :CBC)
decipher.decrypt
decipher.key = key
decipher.iv = iv

plain = decipher.update(encrypted) + decipher.final

puts data == plain #=> true
经过身份验证的加密和关联(AEAD)
如果使用的 OpenSSL 版本支持它,则认证加密模式(如 GCM 或 CCM)应始终优于任何未经身份验证的模式。目前,OpenSSL 仅支持 AE 和相关数据(AEAD)的组合,其中附加的相关数据包含在加密过程中,以便在加密结束时计算标签。该标签也将用于解密过程中,并通过验证其有效性,建立给定密文的真实性。

这比未验证的模式更好,因为它允许检测是否有人在密文被加密后有效地改变了密文。这样可以防止恶意修改密文,否则这些密文可能会被利用来修改密文,从而有利于潜在的攻击者。

在有附加信息(如标题或某些元数据)的情况下使用关联数据,这些信息也必须经过身份验证,但不一定需要加密。如果不需要关联数据进行加密和稍后解密,则 OpenSSL 库仍需要设置值 - “”可用于没有可用的情况。

一个使用 GCM(伽罗瓦/计数器模式)的例子。你有16个字节key,12个字节(96位)nonce和相关的数据auth_data。一定不要重复使用key和nonce配对。重用随机数会破坏 GCM 模式的安全保障。

cipher = OpenSSL::Cipher::AES.new(128, :GCM).encrypt
cipher.key = key
cipher.iv = nonce
cipher.auth_data = auth_data

encrypted = cipher.update(data) + cipher.final
tag = cipher.auth_tag # produces 16 bytes tag by default
现在你是接收器。你知道key,并已收到nonce,auth_data,encrypted和tag通过一个不可信赖的网络。请注意,GCM 接受1到16个字节之间的任意长度标记。您还需要检查收到的标签是否具有正确的长度,或者您允许攻击者伪造 1/256 概率的篡改密文的有效单字节标签。

raise "tag is truncated!" unless tag.bytesize == 16
decipher = OpenSSL::Cipher::AES.new(128, :GCM).decrypt
decipher.key = key
decipher.iv = nonce
decipher.auth_tag = tag
decipher.auth_data = auth_data

decrypted = decipher.update(encrypted) + decipher.final

puts data == decrypted #=> true
公共类方法
OpenSSL::Cipher.ciphers → arraystring...()

返回数组中所有可用密码的名称。

static VALUE
ossl_s_ciphers(VALUE self)
{
    VALUE ary;

    ary = rb_ary_new();
    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
                    (void(*)(const OBJ_NAME*,void*))add_cipher_name_to_ary,
                    (void*)ary);

    return ary;
}
new(string) → cipher 显示源

该字符串必须包含有效的密码名称,如“AES-128-CBC”或“3DES”。

通过调用:: ciphers 可获得密码名称列表。

static VALUE
ossl_cipher_initialize(VALUE self, VALUE str)
{
    EVP_CIPHER_CTX *ctx;
    const EVP_CIPHER *cipher;
    char *name;

    name = StringValueCStr(str);
    GetCipherInit(self, ctx);
    if (ctx) {
        ossl_raise(rb_eRuntimeError, "Cipher already inititalized!");
    }
    AllocCipher(self, ctx);
    if (!(cipher = EVP_get_cipherbyname(name))) {
        ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str);
    }
    if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
        ossl_raise(eCipherError, NULL);

    return self;
}
公共实例方法
auth_data = string → string 显示源

设置密码的附加认证数据。使用 AEAD 密码模式(如 GCM 或 CCM)时,必须设置此字段。如果不使用关联数据,则仍然必须使用值“”调用此方法。该字段的内容应该是非敏感数据,它将被添加到密文中以生成验证标签,验证密文的内容。

AAD 必须在加密或解密之前设置。在加密模式下,必须在调用#encrypt并设置#key =和#iv =之后进行设置。解密时,必须在密钥,iv 之后设置认证数据,特别是在设置了认证标签后。即只在调用#decrypt,#key =,#iv =和#auth_tag = first之后才设置它。

static VALUE
ossl_cipher_set_auth_data(VALUE self, VALUE data)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char *in;
    long in_len, out_len;

    StringValue(data);

    in = (unsigned char *) RSTRING_PTR(data);
    in_len = RSTRING_LEN(data);

    GetCipher(self, ctx);

    if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len))
        ossl_raise(eCipherError, "couldn't set additional authenticated data");

    return data;
}
auth_tag(tag_len = 16) → String Show source

获取通过身份验证加密密码模式生成的身份验证标记(例如,GCM)。该标签可以与密文一起存储,然后设置在解密密码上,以根据改变来认证密文的内容。如果tag_len给出可选的整型参数,则返回的标签将是tag_len字节长。如果省略该参数,则将使用16字节的默认长度或由 auth_tag_len =先前设置的长度。为了最大限度地提高安全性,应尽可能选择最长的。

标签只能在调用 #final 之后检索。

static VALUE
ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
{
    VALUE vtag_len, ret;
    EVP_CIPHER_CTX *ctx;
    int tag_len = 16;

    rb_scan_args(argc, argv, "01", &vtag_len);
    if (NIL_P(vtag_len))
        vtag_len = rb_attr_get(self, id_auth_tag_len);
    if (!NIL_P(vtag_len))
        tag_len = NUM2INT(vtag_len);

    GetCipher(self, ctx);

    if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "authentication tag not supported by this cipher");

    ret = rb_str_new(NULL, tag_len);
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret)))
        ossl_raise(eCipherError, "retrieving the authentication tag failed");

    return ret;
}
auth_tag = string → string显示源文件

设置验证标记以验证密文的内容。在调用#decrypt,#key =和#iv =之后,但在使用#auth_data =分配相关的认证数据之前,必须设置标签,当然,在解密任何密文之前。在所有解密完成后,标签会在#final的调用中自动验证。

对于 OCB 模式,标签长度必须事先提供auth_tag_len =。

static VALUE
ossl_cipher_set_auth_tag(VALUE self, VALUE vtag)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char *tag;
    int tag_len;

    StringValue(vtag);
    tag = (unsigned char *) RSTRING_PTR(vtag);
    tag_len = RSTRING_LENINT(vtag);

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "authentication tag not supported by this cipher");

    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag))
        ossl_raise(eCipherError, "unable to set AEAD tag");

    return vtag;
}
auth_tag_len = Integer → Intege 显示源

设置需要输入参数的 AEAD 密码要生成或要提供的认证标记的长度。请注意,并非所有 AEAD 密码都支持此方法。

在 OCB 模式下,加密时和解密时必须提供长度,并且必须在指定IV之前提供。

static VALUE
ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
{
    int tag_len = NUM2INT(vlen);
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "AEAD not supported by this cipher");

    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL))
        ossl_raise(eCipherError, "unable to set authentication tag length");

    /* for #auth_tag */
    rb_ivar_set(self, id_auth_tag_len, INT2NUM(tag_len));

    return vlen;
}
authenticated? → true | false显示源

指示此 Cipher 实例是否使用认证加密模式。

static VALUE
ossl_cipher_is_authenticated(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
    return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse;
#else
    return Qfalse;
#endif
}
block_size→ integer 显示源

返回此 Cipher 操作的块的大小(以字节为单位)。

static VALUE
ossl_cipher_block_size(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return INT2NUM(EVP_CIPHER_CTX_block_size(ctx));
}
decrypt → self Show source

初始化密码以解密。

请确保在使用以下任何方法之前调用 #encrypt 或 #decrypt:

key =,iv =,random_key,random_iv,pkcs5_keyivgen内部调用EVP_CipherInit_ex(ctx,NULL,NULL,NULL,NULL,0).static VALUE ossl_cipher_decrypt(int argc,VALUE * argv,VALUE self){return ossl_cipher_init(argc,argv,自我,0); } encrypt→self显示源初始化密码以进行加密。在使用以下任何方法之前,请务必调用#encrypt或#decrypt:
key=, iv=, random_key, random_iv, pkcs5_keyivgen
Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1).

static VALUE
ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self)
{
    return ossl_cipher_init(argc, argv, self, 1);
}
final → string 显示源

返回密码对象中保留的剩余数据。进一步调用 #update 或 #final 将返回垃圾。在将完整的明文或密文送入 Cipher 实例之后,应始终将此呼叫作为加密或解密操作的最后一次呼叫。

如果使用经过身份验证的密码,则会在标签无法成功验证时引发 CipherError。只有在设置了认证标签并将密文的全部内容传递给密码后才调用此方法。

static VALUE
ossl_cipher_final(VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    int out_len;
    VALUE str;

    GetCipher(self, ctx);
    str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx));
    if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len))
        ossl_raise(eCipherError, NULL);
    assert(out_len <= RSTRING_LEN(str));
    rb_str_set_len(str, out_len);

    return str;
}
iv = string → string 显示源

设置密码 IV。请注意,因为你永远不应该使用 ECB 模式,所以总是明确要求 IV,并且应该在加密之前设置。IV 本身可以在公共场合安全传输,但防止某些类型的攻击应该是不可预测的。你可以使用 #random_iv 来创建一个安全的随机 IV。

调用 #encrypt 或 #decrypt 后,只调用此方法。

static VALUE
ossl_cipher_set_iv(VALUE self, VALUE iv)
{
    EVP_CIPHER_CTX *ctx;
    int iv_len = 0;

    StringValue(iv);
    GetCipher(self, ctx);

#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
    if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)
        iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
#endif
    if (!iv_len)
        iv_len = EVP_CIPHER_CTX_iv_length(ctx);
    if (RSTRING_LEN(iv) != iv_len)
        ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len);

    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1)
        ossl_raise(eCipherError, NULL);

    return iv;
}
iv_len→integer 显示来源

返回此密码的 IV 的预期长度(以字节为单位)。

static VALUE
ossl_cipher_iv_length(VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    int len = 0;

    GetCipher(self, ctx);
#if defined(HAVE_AUTHENTICATED_ENCRYPTION)
    if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)
        len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx);
#endif
    if (!len)
        len = EVP_CIPHER_CTX_iv_length(ctx);

    return INT2NUM(len);
}
iv_len = integer → integer 显示源

设置密码的 IV /随机长度。通常,分组密码不允许改变IV的长度,但有些使用 IV 作为'nonce'。您可能需要这样才能与其他应用程序进行互操作。

static VALUE
ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
{
    int len = NUM2INT(iv_length);
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER))
        ossl_raise(eCipherError, "cipher does not support AEAD");

    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL))
        ossl_raise(eCipherError, "unable to set IV length");

    /*
     * EVP_CIPHER_CTX_iv_length() returns the default length. So we need to save
     * the length somewhere. Luckily currently we aren't using app_data.
     */
    EVP_CIPHER_CTX_set_app_data(ctx, (void *)(VALUE)len);

    return iv_length;
}
key = string → string 显示源

设置密码密钥。要生成密钥,您应该使用安全的随机字节字符串,或者如果密钥要从密码派生,则应该依赖 OpenSSL :: PKCS5 提供的 PBKDF2 功能。要生成安全的随机密钥,可以使用 #random_key。

调用 #encrypt 或 #decrypt 后,只调用此方法。

static VALUE
ossl_cipher_set_key(VALUE self, VALUE key)
{
    EVP_CIPHER_CTX *ctx;
    int key_len;

    StringValue(key);
    GetCipher(self, ctx);

    key_len = EVP_CIPHER_CTX_key_length(ctx);
    if (RSTRING_LEN(key) != key_len)
        ossl_raise(rb_eArgError, "key must be %d bytes", key_len);

    if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1)
        ossl_raise(eCipherError, NULL);

    rb_ivar_set(self, id_key_set, Qtrue);

    return key;
}
key_len → integer 显示源

返回密码的密钥长度(以字节为单位)。

static VALUE
ossl_cipher_key_length(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return INT2NUM(EVP_CIPHER_CTX_key_length(ctx));
}
key_len = integer → integer 显示源

设置密码的密钥长度。如果密码是一个固定长度的密码,那么尝试将密钥长度设置为固定值以外的任何值都是错误。

在正常情况下,你不需要调用这个方法(也可能不应该)。

有关更多信息,请参阅 EVP_CIPHER_CTX_set_key_length。

static VALUE
ossl_cipher_set_key_length(VALUE self, VALUE key_length)
{
    int len = NUM2INT(key_length);
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (EVP_CIPHER_CTX_set_key_length(ctx, len) != 1)
        ossl_raise(eCipherError, NULL);

    return key_length;
}
name → string 显示源

返回可能与提供的原始名称略有不同的密码的名称。

static VALUE
ossl_cipher_name(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);

    return rb_str_new2(EVP_CIPHER_name(EVP_CIPHER_CTX_cipher(ctx)));
}
padding = integer → integer 显示源

启用或禁用填充。默认情况下,加密操作使用标准块填充进行填充,并在解密时检查并删除填充。如果 pad 参数为零,则不执行填充,则加密或解密的数据总量必须是块大小的倍数,否则将发生错误。

有关更多信息,请参阅 EVP_CIPHER_CTX_set_padding。

static VALUE
ossl_cipher_set_padding(VALUE self, VALUE padding)
{
    EVP_CIPHER_CTX *ctx;
    int pad = NUM2INT(padding);

    GetCipher(self, ctx);
    if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1)
        ossl_raise(eCipherError, NULL);
    return padding;
}
pkcs5_keyivgen(pass,salt = nil,iterations = 2048,digest =“MD5”)→nil 显示源代码

根据密码生成并设置密钥/ IV。

警告:当使用 RC2,RC4-40 或 DES 与 MD5 或 SHA1时,此方法仅符合 PKCS5 v1.5。使用其他任何东西(如 AES)将使用 OpenSSL特定方法生成密钥/ iv。此方法已弃用,不应再使用。改为使用 OpenSSL :: PKCS5中的 PKCS5 v2密钥生成方法。

参数
salt 必须是8字节的字符串(如果提供)。
iterations 是一个默认值为2048的整数。
digest 是一个 Digest 对象,默认为'MD5'
建议至少进行1000次迭代。

static VALUE
ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    const EVP_MD *digest;
    VALUE vpass, vsalt, viter, vdigest;
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
    int iter;

    rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest);
    StringValue(vpass);
    if(!NIL_P(vsalt)){
        StringValue(vsalt);
        if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN)
            ossl_raise(eCipherError, "salt must be an 8-octet string");
        salt = (unsigned char *)RSTRING_PTR(vsalt);
    }
    iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
    digest = NIL_P(vdigest) ? EVP_md5() : GetDigestPtr(vdigest);
    GetCipher(self, ctx);
    EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
                   (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
    if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1)
        ossl_raise(eCipherError, NULL);
    OPENSSL_cleanse(key, sizeof key);
    OPENSSL_cleanse(iv, sizeof iv);

    rb_ivar_set(self, id_key_set, Qtrue);

    return Qnil;
}
random_iv → iv 显示源

用 OpenSSL :: Random.random_bytes 生成随机 IV 并将其设置为密码,然后返回。

调用此方法之前,您必须调用加密或解密。

# File ext/openssl/lib/openssl/cipher.rb, line 54
def random_iv
  str = OpenSSL::Random.random_bytes(self.iv_len)
  self.iv = str
end
random_key → key 显示源

用 OpenSSL :: Random.random_bytes 生成一个随机密钥,并将其设置为密码,并将其返回。

调用此方法之前,您必须调用加密或解密。

# File ext/openssl/lib/openssl/cipher.rb, line 42
def random_key
  str = OpenSSL::Random.random_bytes(self.key_len)
  self.key = str
end
reset → self 显示源

完全重置密码的内部状态。通过使用这个,相同的 Cipher 实例可能会被多次用于加密或解密任务。

内部调用 EVP_CipherInit_ex(ctx,NULL,NULL,NULL,NULL,-1)。

static VALUE
ossl_cipher_reset(VALUE self)
{
    EVP_CIPHER_CTX *ctx;

    GetCipher(self, ctx);
    if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1)
        ossl_raise(eCipherError, NULL);

    return self;
}
update(data , buffer) → string or buffer 显示源

以流媒体的方式加密数据。将连续的数据块传递给该update方法以便对其进行加密。返回加密的数据块。完成后,#final 的输出应另外添加到结果中。

如果buffer给出,加密/解密结果将被写入它。buffer将自动调整大小。

static VALUE
ossl_cipher_update(int argc, VALUE *argv, VALUE self)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char *in;
    long in_len, out_len;
    VALUE data, str;

    rb_scan_args(argc, argv, "11", &data, &str);

    if (!RTEST(rb_attr_get(self, id_key_set)))
        ossl_raise(eCipherError, "key not set");

    StringValue(data);
    in = (unsigned char *)RSTRING_PTR(data);
    if ((in_len = RSTRING_LEN(data)) == 0)
        ossl_raise(rb_eArgError, "data must not be empty");
    GetCipher(self, ctx);
    out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
    if (out_len <= 0) {
        ossl_raise(rb_eRangeError,
                   "data too big to make output buffer: %ld bytes", in_len);
    }

    if (NIL_P(str)) {
        str = rb_str_new(0, out_len);
    } else {
        StringValue(str);
        rb_str_resize(str, out_len);
    }

    if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))
        ossl_raise(eCipherError, NULL);
    assert(out_len < RSTRING_LEN(str));
    rb_str_set_len(str, out_len);

    return str;
}
 

https://cloud.tencent.com/developer/section/137918

 

如何用serverless创建aws-lambda

https://serverless.com/framework/docs/providers/aws/guide/quick-start/

安装serverless

$npm install -g serverless

$mkdir serverless-demo

$cd serverless-demo/

$serverless create -t aws-nodejs

从aws里找到”My Security Credentials’

https://serverless.com/framework/docs/providers/aws/guide/credentials/#create-an-iam-user-and-access-key


Add credential to your local laptop:

$serverless config credentials –provider aws –key AKIAIDEXAZA –secret xxxEL5xFZYxxx49nuu

$cat ~/.aws/credentials 

接下来打开demo文件,编辑serverless.yml:

service: lambda-test
provider:
  name: aws
  runtime: nodejs10.x
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: users/create
          method: get
  imageResize:
    handler: handler.imageResize
    events:
      - http:
          path: /imageResize
          method: get

编辑handel.js

'use strict';

module.exports.hello = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'This is v1.0',
      },
      null,
      2
    ),
  };
};

module.exports.imageResize = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'resized your image',
      },
      null,
      2
    ),
  };
};

保存修改之后, 发布到测试环境

$serverless deploy

Serverless: Packaging service…

Serverless: Excluding development dependencies…

Serverless: Uploading CloudFormation file to S3…

Serverless: Uploading artifacts…

Serverless: Uploading service lambda-test.zip file to S3 (417 B)…

Serverless: Validating template…

Serverless: Updating Stack…

Serverless: Checking Stack update progress…

…………………………..

Serverless: Stack update finished…

Service Information

service: lambda-test

stage: dev

region: us-east-1

stack: lambda-test-dev

resources: 17

api keys:

  None

endpoints:

  GET – https://xxx.execute-api.us-east-1.amazonaws.com/dev/users/create

  GET – https://xxx.execute-api.us-east-1.amazonaws.com/dev/imageResize

functions:

  hello: lambda-test-dev-hello

  imageResize: lambda-test-dev-imageResize

layers:

  None

Serverless: Run the “serverless” command to setup monitoring, troubleshooting and testing.

发布到正式环境的命令:

$ serverless deploy –stage production

Serverless: Packaging service…

Serverless: Excluding development dependencies…

Serverless: Creating Stack…

Serverless: Checking Stack create progress…

…..

Serverless: Stack create finished…

Serverless: Uploading CloudFormation file to S3…

Serverless: Uploading artifacts…

Serverless: Uploading service lambda-test.zip file to S3 (417 B)…

Serverless: Validating template…

Serverless: Updating Stack…

Serverless: Checking Stack update progress…

……………………………………………

Serverless: Stack update finished…

Service Information

service: lambda-test

stage: production

region: us-east-1

stack: lambda-test-production

resources: 17

api keys:

  None

endpoints:

  GET – https://xxx.execute-api.us-east-1.amazonaws.com/production/users/create

  GET – https://xxx.execute-api.us-east-1.amazonaws.com/production/imageResize

functions:

  hello: lambda-test-production-hello

  imageResize: lambda-test-production-imageResize

layers:

  None

Serverless: Run the “serverless” command to setup monitoring, troubleshooting and testing.

python操作postgreSQL数据库

Recommended solution: https://docs.aws.amazon.com/lambda/latest/dg/vpc-rds.html

Coded a python script (as attached) for insert the biller details to the dev db, it works well per my testing on the dev ec2 instance (xxx-util-services). It use the “awslambda-psycopg2” library to for the Postgres connection.
https://github.com/jkehler/awslambda-psycopg2

Next step is to make the script to be a AWS lambda compatiable (with Lambda function handlers) and then package with the library into a AWS Lambda zip package.

#!/usr/bin/python
import psycopg2
from datetime import datetime

db_host = "xxxxxx-esb-xx.xxxxx.xx-xxxx.rds.xxxxx.com"
db_port = 5432
db_name = "xxx"
db_user = "xxx"
db_pass = "xxxx"
db_table = "xxxxx"
biller_code = "111111"
biller_short_name = "test_short"
biller_long_name = "test_long"

def make_conn():
    conn = None
    try:
        conn = psycopg2.connect("dbname='%s' user='%s' host='%s' password='%s'" % (db_name, db_user, db_host, db_pass))
        print "connected to postgres db successfully"
    except:
        print "I am unable to connect to the database"
    return conn


try:
    connection = make_conn()
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM billers where biller_code = '%s';" % biller_code)
    numOfRows = cursor.rowcount
    print("%s rows found for the biller code: %s" % (numOfRows, biller_code))
    if(numOfRows<=0):
        dt = datetime.now()
        cursor.execute("insert into billers (biller_code, biller_short_name, biller_long_name, active, min_length, max_length, reg_exp, check_digit_algo, created_at, updated_at) values (%s, %s, %s, true, 0, 100, 'NONE', 'NONE', %s, %s)", (biller_code, biller_short_name, biller_long_name, dt, dt))
        connection.commit()
        print("inserted %s rows successfully" % cursor.rowcount)
except (Exception, psycopg2.Error) as error :
    print ("Error caught", error)
finally:
    #closing database connection.
        if(connection):
            cursor.close()
            connection.close()
            print("PostgreSQL connection is closed")