分类 OpenSSL 下的文章

“OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连线者的身份。这个包广泛被应用在互联网的网页服务器上。 其主要库是以C语言所写成,实现了基本的加密功能,实现了SSL与TLS协议。”

来源:https://github.com/majek/openssl/blob/master/demos/evp/aesgcm.c
包含NIST 测试向量

/* Simple AES GCM test program, uses the same NIST data used for the FIPS
 * self test but uses the application level EVP APIs.
 */
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/evp.h>

/* AES-GCM test data from NIST public test vectors */

static const unsigned char gcm_key[] = {
    0xee,0xbc,0x1f,0x57,0x48,0x7f,0x51,0x92,0x1c,0x04,0x65,0x66,
    0x5f,0x8a,0xe6,0xd1,0x65,0x8b,0xb2,0x6d,0xe6,0xf8,0xa0,0x69,
    0xa3,0x52,0x02,0x93,0xa5,0x72,0x07,0x8f
};

static const unsigned char gcm_iv[] = {
    0x99,0xaa,0x3e,0x68,0xed,0x81,0x73,0xa0,0xee,0xd0,0x66,0x84
};

static const unsigned char gcm_pt[] = {
    0xf5,0x6e,0x87,0x05,0x5b,0xc3,0x2d,0x0e,0xeb,0x31,0xb2,0xea,
    0xcc,0x2b,0xf2,0xa5
};

static const unsigned char gcm_aad[] = {
    0x4d,0x23,0xc3,0xce,0xc3,0x34,0xb4,0x9b,0xdb,0x37,0x0c,0x43,
    0x7f,0xec,0x78,0xde
};

static const unsigned char gcm_ct[] = {
    0xf7,0x26,0x44,0x13,0xa8,0x4c,0x0e,0x7c,0xd5,0x36,0x86,0x7e,
    0xb9,0xf2,0x17,0x36
};

static const unsigned char gcm_tag[] = {
    0x67,0xba,0x05,0x10,0x26,0x2a,0xe4,0x87,0xd7,0x37,0xee,0x62,
    0x98,0xf7,0x7e,0x0c
};

void aes_gcm_encrypt(void)
    {
    EVP_CIPHER_CTX *ctx;
    int outlen, tmplen;
    unsigned char outbuf[1024];
    printf("AES GCM Encrypt:\n");
    printf("Plaintext:\n");
    BIO_dump_fp(stdout, gcm_pt, sizeof(gcm_pt));
    ctx = EVP_CIPHER_CTX_new();
    /* Set cipher type and mode */
    EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    /* Set IV length if default 96 bits is not appropriate */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
    /* Initialise key and IV */
    EVP_EncryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv);
    /* Zero or more calls to specify any AAD */
    EVP_EncryptUpdate(ctx, NULL, &outlen, gcm_aad, sizeof(gcm_aad));
    /* Encrypt plaintext */
    EVP_EncryptUpdate(ctx, outbuf, &outlen, gcm_pt, sizeof(gcm_pt));
    /* Output encrypted block */
    printf("Ciphertext:\n");
    BIO_dump_fp(stdout, outbuf, outlen);
    /* Finalise: note get no output for GCM */
    EVP_EncryptFinal_ex(ctx, outbuf, &outlen);
    /* Get tag */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, outbuf);
    /* Output tag */
    printf("Tag:\n");
    BIO_dump_fp(stdout, outbuf, 16);
    EVP_CIPHER_CTX_free(ctx);
    }

void aes_gcm_decrypt(void)
    {
    EVP_CIPHER_CTX *ctx;
    int outlen, tmplen, rv;
    unsigned char outbuf[1024];
    printf("AES GCM Derypt:\n");
    printf("Ciphertext:\n");
    BIO_dump_fp(stdout, gcm_ct, sizeof(gcm_ct));
    ctx = EVP_CIPHER_CTX_new();
    /* Select cipher */
    EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    /* Set IV length, omit for 96 bits */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
    /* Specify key and IV */
    EVP_DecryptInit_ex(ctx, NULL, NULL, gcm_key, gcm_iv);
#if 0
    /* Set expected tag value. A restriction in OpenSSL 1.0.1c and earlier
         * required the tag before any AAD or ciphertext */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof(gcm_tag), gcm_tag);
#endif
    /* Zero or more calls to specify any AAD */
    EVP_DecryptUpdate(ctx, NULL, &outlen, gcm_aad, sizeof(gcm_aad));
    /* Decrypt plaintext */
    EVP_DecryptUpdate(ctx, outbuf, &outlen, gcm_ct, sizeof(gcm_ct));
    /* Output decrypted block */
    printf("Plaintext:\n");
    BIO_dump_fp(stdout, outbuf, outlen);
    /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof(gcm_tag), gcm_tag);
    /* Finalise: note get no output for GCM */
    rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
    /* Print out return value. If this is not successful authentication
     * failed and plaintext is not trustworthy.
     */
    printf("Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!");
    EVP_CIPHER_CTX_free(ctx);
    }

int main(int argc, char **argv)
    {
    aes_gcm_encrypt();
    aes_gcm_decrypt();
    }

在对数据进行加解密时,通常将数据按照固定的大小(block size)分成多个组,那么随之就产生了一个问题,如果分到最后一组,不够一个 block size 了,要怎么办?此时就需要进行补齐操作。
我们常用的填充的方式就包括ZeroPadding、PKCS5Padding与PKCS7Padding。
NoPadding
不填充,如果加密內容不是block size整数倍加密则报错。
ZeroBytePadding
所有需要填充的地方都以0填充。
示例如下:Block 大小为 8 Byte,需要填充 4 Byte(以十六进位表示)
DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |

PKCS#7 Padding
每个填充字节的值是用于填充的字节数,若需要填充 N 个字节,每个填充字节值都是 N 。
01
02 02
03 03 03
04 04 04 04
05 05 05 05 05
etc.
示例如下:Block 大小为 8 Byte,需要填充 4 Byte(以十六进位表示)

| DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |

PKCS#5 Padding
​ PKCS#5 和PKCS#7计算方式都相同,區別是PKCS5 只能用來填充 8 Byte (64bit)的Block。

示例如下:Block 大小主 8 Byte,需要填充 4 Byte(以十六进位表示)

| DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |

PKCS7Padding补齐规则:The value of each added byte is the number of bytes that are added, i.e. N bytes, each of value N are added.

举例:

36 位的 UUID,如果按照 block size=16 字节(即 128 比特),那么就需要补齐到 48 位,差 12 个字节。那么最后填充的 12 个字节的内容,都是字节表示的 0x0c(即 12)。

PKCS7Padding,假设每个区块大小为blockSize,分为二种填充情况。

1、已对齐,填充一个长度为blockSize且每个字节均为blockSize的数据。

2、未对齐,需要补充的字节个数为n,则填充一个长度为n且每个字节均为n的数据。

PKCS5Padding是PKCS7Padding的子集,只是块大小固定为8字节。在aes/sm4等加密算法中,密码的最小长度都是16字节(128位),所以,事实上都没PKCS5Padding什么事了,只有在类似RC2 / RC5和 DES/3DES 算法的情況,这些算法 BlockSize=64bits=8bytes算法才有有到PKCS5Padding。

PKCS#5 PKCS#7 差异
最大的差异是要求的BlockSize 不同

PKCS#5只针对8 Byte(BlockSize=8)填充,填充內容为 0x01- 0x08;
PKCS#7是对任意BlockSize填充,其BlockSize範圍是 1-255 Byte。

cat /etc/os-release 

PRETTY_NAME="Ubuntu 22.04 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

openssl version

OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.1 14 Dec 2021)

cat rsa_pkcs1.pem 

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA43VHuSL3C9WzCAm2ECoM/vILkoEBwwkvZYIDWPw+irKBLIZZ
xfVnQ2gBU7Je6BnQQGOm8balQhedyOJgj6TRWYI9hmCMP4aLw7tdV5IWtluR8RjI
6hRFxGyAxb+JnTU9Yr+97rqtvjSBcsUydILj7A2zcdhowB/4mKS6V55AnNhEWxqk
3+JVZSq34Qg26VfxrwRXWV5CLm57p2L3DDyo3MbY7nY+7TQ/t7NL8FJvrAp324A/
uLsOi+wt7ITbUW1QjxHxaxjwNQiKCPyQto/TJiG7IGL5I9O4/FC0QLA2iKJ1BSbN
A/AQyIFQtgJr+eIGwIrJKWH379A8LGJx/tJDiwIDAQABAoIBAB+2nujrvJksUvlo
bTeEvWogckebcBTy52ZWQcQ66zdoWwVLuCGXLS4jRqgWQxBGZRhNLJ4f8pur8kZr
db4hY0UBGTXuqS9EQaFpMsL+bedADjGY3I5yhCW2YR/xLC08mpH1nPQ3l/jgljnb
z8SZ6VBkuqPBYh2Dd6REt2x29phHdaFJbblLiqmipc+r3dX58uD/1r7dbuXxdBkH
yQHEjCMJnHOJZHlhxx2sTmugY9Trwcx0lbxnppXyHlgiB/jKK/KYh4SUECrIYOTD
/wRNprKHWzIk04PY/rW3pfFUxqDi9KbckIvgHE4ZiNbSHtn7oblq6+lIMmiyAC+O
Uov6vT0CgYEA/qbE1KPqr8G0RmYtujVSU2B6nIYeUJJ3rE2bWzDvoiHOogGFSA9w
FSwK0u+jG/9ufD4clndYjbgGtRmElEU6vmB2xoTVeuhjPzmB4eFjgofX5BoiRwFn
vjQ+XzoyJB/h/Lx0Chiqi9Z3+OYyXvED+lKTVWR5wTmrgK/40I2NXd0CgYEA5Kml
Mpf3QYfp8USREeUgcrqMmDgeLTe2JZ0bQDMPj5onyWe92RrrRvSfr9jAtlNKXWoG
Q1iSUEIw99Rgm+oVYqF1xsegEtcLpS/4ie08CDgG6pufIn1K8le3O3YBdL4EdvG+
R1qz1DKwWb+7fv0PFeI7C+9DztuP20FIo2/qlIcCgYAzfP7BS99Q6CaOMWy/gLaD
mzdPXKLWgbbmxM5LsupQ6ur/wLT1LAHqlfweSEu/hncNcfWPytPB29qnVWCEJjdJ
dPGvE/6iGkiu6O7FADCBnPve6BXUK4mRYLjXwkmPGk80Ldp8jB43pgad8dVvcuXu
ADJACs+/bOyrWWlCqcxSlQKBgCAaVTAaN8OCgmy2FKvvgDO7Txvb3Ri6Fp+HRBkq
Ugf2O5nCL9w4NEn89CJrCvtODEW7Ke/wknIkKN3SelHJdoEna/Khei7K9D6X0owC
u4fn3ci4WoosDwahG/+EHMY4G0ZbwDwiVtb22t1u1rMIckK/RUzypZF4L+U3cCxJ
/SQnAoGBANiS3QN2mdkQgoz1+0uIW6BA6brGjHiw79aqD6Wvmg/dns8e3t7gCvrG
23eWlS4LJfVvnneMjIlGxy0nuMzJOl8nRYcOp5X/esAjrfxzGbILkaiZidJYZgF4
sy9/G6XjrpSXq+vrjNzxCnoUV64sXFb8ftad6DCj9XpNTWabqfjO
-----END RSA PRIVATE KEY-----

上面是pkcs1格式的密钥,

openssl 3.0.2 convect pkcs1.pem to pkcs8.pem

openssl rsa -in rsa_pkcs1.pem 

writing RSA key
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDjdUe5IvcL1bMI
CbYQKgz+8guSgQHDCS9lggNY/D6KsoEshlnF9WdDaAFTsl7oGdBAY6bxtqVCF53I
4mCPpNFZgj2GYIw/hovDu11Xkha2W5HxGMjqFEXEbIDFv4mdNT1iv73uuq2+NIFy
xTJ0guPsDbNx2GjAH/iYpLpXnkCc2ERbGqTf4lVlKrfhCDbpV/GvBFdZXkIubnun
YvcMPKjcxtjudj7tND+3s0vwUm+sCnfbgD+4uw6L7C3shNtRbVCPEfFrGPA1CIoI
/JC2j9MmIbsgYvkj07j8ULRAsDaIonUFJs0D8BDIgVC2Amv54gbAiskpYffv0Dws
YnH+0kOLAgMBAAECggEAH7ae6Ou8mSxS+WhtN4S9aiByR5twFPLnZlZBxDrrN2hb
BUu4IZctLiNGqBZDEEZlGE0snh/ym6vyRmt1viFjRQEZNe6pL0RBoWkywv5t50AO
MZjcjnKEJbZhH/EsLTyakfWc9DeX+OCWOdvPxJnpUGS6o8FiHYN3pES3bHb2mEd1
oUltuUuKqaKlz6vd1fny4P/Wvt1u5fF0GQfJAcSMIwmcc4lkeWHHHaxOa6Bj1OvB
zHSVvGemlfIeWCIH+Mor8piHhJQQKshg5MP/BE2msodbMiTTg9j+tbel8VTGoOL0
ptyQi+AcThmI1tIe2fuhuWrr6UgyaLIAL45Si/q9PQKBgQD+psTUo+qvwbRGZi26
NVJTYHqchh5QknesTZtbMO+iIc6iAYVID3AVLArS76Mb/258PhyWd1iNuAa1GYSU
RTq+YHbGhNV66GM/OYHh4WOCh9fkGiJHAWe+ND5fOjIkH+H8vHQKGKqL1nf45jJe
8QP6UpNVZHnBOauAr/jQjY1d3QKBgQDkqaUyl/dBh+nxRJER5SByuoyYOB4tN7Yl
nRtAMw+PmifJZ73ZGutG9J+v2MC2U0pdagZDWJJQQjD31GCb6hVioXXGx6AS1wul
L/iJ7TwIOAbqm58ifUryV7c7dgF0vgR28b5HWrPUMrBZv7t+/Q8V4jsL70PO24/b
QUijb+qUhwKBgDN8/sFL31DoJo4xbL+AtoObN09cotaBtubEzkuy6lDq6v/AtPUs
AeqV/B5IS7+Gdw1x9Y/K08Hb2qdVYIQmN0l08a8T/qIaSK7o7sUAMIGc+97oFdQr
iZFguNfCSY8aTzQt2nyMHjemBp3x1W9y5e4AMkAKz79s7KtZaUKpzFKVAoGAIBpV
MBo3w4KCbLYUq++AM7tPG9vdGLoWn4dEGSpSB/Y7mcIv3Dg0Sfz0ImsK+04MRbsp
7/CSciQo3dJ6Ucl2gSdr8qF6Lsr0PpfSjAK7h+fdyLhaiiwPBqEb/4QcxjgbRlvA
PCJW1vba3W7WswhyQr9FTPKlkXgv5TdwLEn9JCcCgYEA2JLdA3aZ2RCCjPX7S4hb
oEDpusaMeLDv1qoPpa+aD92ezx7e3uAK+sbbd5aVLgsl9W+ed4yMiUbHLSe4zMk6
XydFhw6nlf96wCOt/HMZsguRqJmJ0lhmAXizL38bpeOulJer6+uM3PEKehRXrixc
Vvx+1p3oMKP1ek1NZpup+M4=
-----END PRIVATE KEY-----

openssl 3.0.2 cannot convert pkcs8.pem to pkcs1.pem

openssl rsa -in pkcs8.pem -out pkcs1.pem

in openssl 1.1.1 is ok.
in openssl 3.0.2 is not work...

pyOpenSSL是Python的openssl库.
通过pip安装:

pip install pyOpenSSL

产生密钥对

from OpenSSL.crypto import PKey
from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM
from OpenSSL.crypto import dump_privatekey, dump_publickey

pk = PKey()
print(pk)
pk.generate_key(TYPE_RSA, 1024)
dpub = dump_publickey(FILETYPE_PEM, pk)
print(dpub)
dpri = dump_privatekey(FILETYPE_PEM, pk)
print(dpri)

运行结果:
<OpenSSL.crypto.PKey object at 0x76c3b090>
b'-----BEGIN PUBLIC KEY-----nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyNCTZuEzZrX2OaaPgcdCsd3VInPXVGyWKzCc0rUdmmrD7+czdeCgoeHuCwwkig+pGhYFYZvFNZFaEzxKmmJOTxrklBnxOk2K2mTvqsviPMFG780qG69zM+Zm+tYPy+aU4taRoPhlSY9hy2YWubKiLqUkGWXnfoJOElkGFD+O4IwsWwIDAQABn-----END PUBLIC KEY-----n'
b'-----BEGIN PRIVATE KEY-----nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALI0JNm4TNmtfY5pno+Bx0Kx3dUg9dUbJYrMJzStR2aasPv5zN14KCh4e4LDCSKD6kaFgVhm8U1kVoTPEnqaYk5PGuSUHE6TYraZO+qy+I8wUbvzSobr3Mz5mb61g/L5pTi1pGg+GVJj2HLZhan5sqIupSQZZd+gk4SWQYUP47gjCxbAgMBAAECgYEAhqYNvhCayNNlDmlV8O4uvVIZn5TbC2XSrRhq+0t+qtFxr0Llf+Ydec7njDswOMsyBo0z2YcXBuIs2XbZYdXhlH9AgnWWrkhRPVLN0mvs/XPpXRkQh233orznIgoz8UBuoUlcXppA/KOnSSJ2RuwVtiqAl5nQf71oRSVZ7AmKy6PLOECQQDofMyLSx9KWoQ/HSiw7w9lgX7+olyB3ybg+Hi9YdrhnyxWJ2VJoY5TTCCsREsAyF86fRSNuaF3PpU/6oYkT+6brAkEAxDnp8jAKmrfX8qKTnnNOwFJaAJBAihtrgeURPWGiCTSWR9w0S5w+AKeeGU9oEhuPaUK1rUdtqYlFMhAgjn2iYUUQJAEw9wQZdGGHV1VCtS07a1v2+vdrbe+LLP4C/ezkAAjvR0bpnHnNFVOTv5nM+win7i98ubbMckSr9xwwy6NK3s9QwJAU2wjr5kJCRnbrwW7J9M/aqFJPQu3AgoPnoL6P1RApRU8RrSxbuuv2GtqZWxC3F/nKmL4BgD1+DuptUzx6sYW64QJAC5jNhQw7nd1AzDBc4X8fkcOH1+fn3sNGT5UBZ5+1l8jy44QR0ZaDsbGKyWEizDiJVvC01eNJdnTDj99Venyyug6Q==n-----END PRIVATE KEY-----n'

签名与验签

from OpenSSL.crypto import PKey
from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM
from OpenSSL.crypto import sign, verify
from OpenSSL.crypto import X509

pk = PKey()
pk.generate_key(TYPE_RSA, 1024)
 
signature = sign(pk, 'hello, world!', 'sha256')
print(signature)
 
x509 = X509()
x509.set_pubkey(pk)
verify(x509, signature, 'hello, world!', 'sha256')

结果:
b'txe1xb1rxc1}x82x9dxbexa2x97x14x88xdbxf7x19x835xeb=xc0x87xa5xe9xe7x10xcdxaax90Qx11xee;oxf4Axafxa0xfcj3Xtxd9=x10xf3xbdxe9xc3>@xc1xafxffx8dxfbtxd9x81xfaxdexa2QLxc2xf0t+_wxfex1bx86x0f\xebJ\x17xcaxf4x11xb0lxd6x17`xfdx194xa6x0cxe3yx93Exd2x92Bx984-(xc8qxdax1e:,xd4x83jxca(jxe4xb5Gxa6(xfaxffx97xa2xabxa9xd6'

如果验签失败,会出现以下错误.
OpenSSL.crypto.Error: [('rsa routines', 'int_rsa_verify', 'bad signature')]