OpenSSL Cipher 加密解密 Ruby on Rails


最近在做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。一定不要重複使用keynonce配對。重用隨機數會破壞 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,並已收到nonceauth_dataencryptedtag通過一個不可信賴的網絡。請注意,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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章