【OpenSSL】使用SMIME发送签名和加密邮件

1,通信双方的证书生成

1.1生成根节点证书

openssl genrsa -out cakey.pem 2048
openssl req -new -key cakey.pem -subj "/CN=rootca.bitbaba.com" -out cacsr.pem
openssl x509 -req -in cacsr.pem -days 999 -signkey cakey.pem -out cacert.pem

1.2生成alice的证书

openssl genrsa -out alicekey.pem 2048
openssl req -new -key cakey.pem -subj "/[email protected]" -out alicecsr.pem
openssl x509 -req -in alicecsr.pem -days 999 -CA cacert.pem -CAKey cakey.pem -set_serial 01 -name "alice" -out alicecert.pem
openssl pkcs12 -export -in alicecert.pem -inkey alicekey.pem -certfile cacert.pem -out alice.p12

1.3生成bob的证书

openssl genrsa -out bobkey.pem 2048
openssl req -new -key bobkey.pem -subj "/[email protected]" -out bobcsr.pem
openssl x509 -req -in bobcsr.pem -days 999 -CA cacert.pem -CAkey cakey.pem -set_serial 02 -name "bob" -out bobcert.pem
openssl pkcs12 -export -in bobcert.pem -inkey bobkey.pem -certfile cacert.pem -out bob.p12


2,邮件签名

openssl smime -sign -in /tmp/msg.txt -signer alicecert.pem -inkey alicekey.pem -nocerts -nodetach -text -out /tmp/alicesigned.eml 


提醒:

-nodetach 把信息原文也包含到base64块里面,而不是用mime格式的分隔符单独放置,比较容易保证信息不被邮件收发服务器重构,导致验证失败。

-nocerts    是一个可选选项,如果设置的话,alice的证书(公钥等)不会被包含到签名信息里面(base64块)

-signer      在签发邮件的时候,指定发送人的证书位置,这里指定alice的证书


3,签名验证

openssl smime -verify -in /tmp/alicesigned.eml -certfile alicecert.pem -CAfile cacert.pem


注意:

-signer  参数这个时候的意思:导出签发邮件的证书保存。所以要小心覆盖原有证书

-CAfile  设置信任的ca位置,否则验证不通过

-certfile 是一个可选选项,如果签名的时候指定了-nocerts,这里可以指定验证证书(alice),这样网络传输经济。


4,邮件加密

 smime -encrypt -in /tmp/msg.txt -from [email protected] -to [email protected] -des3 -out /tmp/msg.eml bobcert.pem


5,邮件解密

 openssl smime -decrypt -in /tmp/msg.eml -recip bobcert.pem -inkey bobkey.pem


6, 签名并加密

openssl smime -sign -in msg.txt -signer alicecert.pem -inkey alicekey.pem -nocerts -nodetach -text | openssl smime -encrypt -des3 -from [email protected] -to [email protected] -subject HiBob  bobcert.pem

To: [email protected]
From: [email protected]
Subject: HiBob
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64

MIIGKQYJKoZIhvcNAQcDoIIGGjCCBhYCAQAxggFmMIIBYgIBADBKMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQCAQMwDQYJKoZIhvcNAQEBBQAEggEAdmR6L5KWIhmIFBwj
UV2wxBb8S20+TH+ZsQ7NtHN/FucV+LMkRWsif03ExRqnsLwigQSXinigsOrgpovn
X8JpfiOQp3cg6KEyhX0+q2Ziiu4qRNZLCKoWXZ4oxyYORe8PT1R9NwSfnfVcHvec
duyOMYdp3T50844CXQ0NMv/FjEv5bdhN5TdPvEhyOcaQ4g/Fhsh0kPPFVNWnLFIw
6p+2K5sZr749RvGuzmZuwd4WvAtPO/bgBks7sbR8mR7jXLZaq8Q/NHN913Jq5Pf2
VATbIDUbvbD2Znv22NJOt600Et+yi4xHT/TG5TXVYZSuayOPhVkX032Gycgp3c35
UaYHozCCBKUGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQI525g7nh7tU6AggSAxAL5
yGDcR3M+xZZELlG1/JcUKWLUmZQoqKyKtT5gk61lMJ6Si4LcQSlYP+IySeAYB9fw
RY+uKepW0snNHMO/IrT+EtKn/qJgm/OJSEf5YrCwmJx/k1Pv2BZu4GeTmnB5cfKx
xTus0bhiGR7tqWRv9XrexhdeUKumyJMzr9Vxzfnz+ahTk510npW3Ei+4rKkQOO3z
mrwRUril/J9onvInkvjAOwMceKTyjrCzEJv3f5JMreqwSjeTnxQcihJHdqPo1NpZ
RSNIsG83Hz2/mMNuRgVQi7DVWqJlcsv4OG9J6Dw6Xp7bnskppoMYMsZzOon0X4o+
JTjb9lyhxcXPE8htbd3qg1EroMByoWD/HcKLYv00vo1Gd5mgHCEqp0a6/WrpLlGQ
JDJDFqJ2TEJCbgAiTejxns+t/zQyC9iOVSP+S1/rkzuZV28O7zCDjJwxGK1rUFa1
Epa4jlciFyppuzzxYICxSDNVAfFsGLW3heuxFqew3aUufDcjzhkDYGrZpWd91C0/
WYqVQM5VYtpqTZHUfNAkFvyuB2jqi265/rvsgpIb1mlC3ee4DbAqAz9a8HMMajkN
rRWGuGm9S/Zz4QskAaNYdQXqmv2UT2NmDhcv+zGqH0vzlGs0k9Ae0My1cucBPmFO
NjbM11yHTSKN1sqlRtzQiCi7IbM5B4Q2O8/jhvO07ezRz5Fj4o4PxpElQ/7mtusg
Fz97HA4guZ+8Ig/NoG7HkdJh5Ju0fTGnqDosG3jXJWtJlXrsA3qHwdbk1Z8ux7xf
L6fVmO2dEBUkc6ihMvDgyZByUbyauEfyCwSb0UfJeJbbfY9UmsBWbU635rLn5Fcx
MTY9JwoD/SgEYRm3XhxphO+xy2LAVal4GULlcqtXeGKLUzuRavn/MCP7V5xLEX8Z
3hih/eJ/OFOUL/Hndr5py3oLavhV+mFwNTJrQ2Jrk9DwnsnUS70Z8nEKbe9O41Xl
rcll6nLMnY6qXmA47tz5NM7uPGBpus5cInRnI2ndGnGJt0pW30ubn1gH/sYA6iwR
aWTz4gxo1h9pVfJ6z8P6guXJaS5X6NLGVfl6EPs1EwEEbc0zd3r9iphcARtnI4U6
fOa5QeRWd8KI/XpSS9KXM90Jdphxz1E1iD9VGkFPhxRq0HMaEZu2ggD2HUTHibyZ
9fV9f6wf2Hx4Wng1Eh1PDiffnIYkrEYdfu2Uqt7HaYxz1T0qbrkHNJOjT8eZ3aZW
yLn/bEDeyxeLcsGDOt6hdW0C0PaoLzv6okKbprLgx4Dfxfh0ytu1m/M6WoVO3/QB
ELvxshSBc77P4+UAAlDjRhpQRuDOa0xU5DHeb3DDogRGw8nWpleyqQsTlgQmzYWJ
w8ps+VbrGTn3hNkGcLgkqXWy5uLocuj+61RB8HZAdNMhjAKHvr9pGhac1hbLW7aP
uehFCfCo4P0vDNJpjV7JDG8y+j89guGbC8ew3Hd7LYGucdGYWgnKxBRpmG5dJ7Vi
swGNTSOWATI+Lz+sySCVHyrKbsf3IfFsjdMZxQrZttnm63q5rRam/F0VMbEE


7,解密并验证

openssl smime -decrypt -in enc.eml -recip bobcert.pem -inkey bobkey.pem | openssl smime -verify -certfile alicecert.pem -CAfile cacert.pem


Content-Type: text/plain

 Hello, World !
Verification successful


管道命令

openssl smime -sign -in msg.txt -signer alicecert.pem -inkey alicekey.pem -nocerts -nodetach -text | \
openssl smime -encrypt -des3 -from [email protected] -to [email protected] -subject HiBob  bobcert.pem | \
openssl smime -decrypt -recip bobcert.pem -inkey bobkey.pem | \
openssl smime -verify -certfile alicecert.pem -CAfile cacert.pem



8,邮件发送

关于smtp客户端的例子


9,邮件接收

关于pop3的例子


备注:

1)关于cms的一些数据结构定义

子结构的详细定义可以参考:https://github.com/openssl/openssl/blob/master/crypto/cms/cms_lcl.h

struct CMS_ContentInfo_st {
    ASN1_OBJECT *contentType;
    union {
        ASN1_OCTET_STRING *data;
        CMS_SignedData *signedData;
        CMS_EnvelopedData *envelopedData;
        CMS_DigestedData *digestedData;
        CMS_EncryptedData *encryptedData;
        CMS_AuthenticatedData *authenticatedData;
        CMS_CompressedData *compressedData;
        ASN1_TYPE *other;
        /* Other types ... */
        void *otherData;
    } d;
};


PKCS7的递推定义

/*-
Encryption_ID           DES-CBC
Digest_ID               MD5
Digest_Encryption_ID    rsaEncryption
Key_Encryption_ID       rsaEncryption
*/

typedef struct pkcs7_issuer_and_serial_st {
    X509_NAME *issuer;
    ASN1_INTEGER *serial;
} PKCS7_ISSUER_AND_SERIAL;

typedef struct pkcs7_signer_info_st {
    ASN1_INTEGER *version;      /* version 1 */
    PKCS7_ISSUER_AND_SERIAL *issuer_and_serial;
    X509_ALGOR *digest_alg;
    STACK_OF(X509_ATTRIBUTE) *auth_attr; /* [ 0 ] */
    X509_ALGOR *digest_enc_alg;
    ASN1_OCTET_STRING *enc_digest;
    STACK_OF(X509_ATTRIBUTE) *unauth_attr; /* [ 1 ] */
    /* The private key to sign with */
    EVP_PKEY *pkey;
} PKCS7_SIGNER_INFO;

DECLARE_STACK_OF(PKCS7_SIGNER_INFO)
DECLARE_ASN1_SET_OF(PKCS7_SIGNER_INFO)

typedef struct pkcs7_recip_info_st {
    ASN1_INTEGER *version;      /* version 0 */
    PKCS7_ISSUER_AND_SERIAL *issuer_and_serial;
    X509_ALGOR *key_enc_algor;
    ASN1_OCTET_STRING *enc_key;
    X509 *cert;                 /* get the pub-key from this */
} PKCS7_RECIP_INFO;

DECLARE_STACK_OF(PKCS7_RECIP_INFO)
DECLARE_ASN1_SET_OF(PKCS7_RECIP_INFO)

typedef struct pkcs7_signed_st {
    ASN1_INTEGER *version;      /* version 1 */
    STACK_OF(X509_ALGOR) *md_algs; /* md used */
    STACK_OF(X509) *cert;       /* [ 0 ] */
    STACK_OF(X509_CRL) *crl;    /* [ 1 ] */
    STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
    struct pkcs7_st *contents;
} PKCS7_SIGNED;
/*
 * The above structure is very very similar to PKCS7_SIGN_ENVELOPE. How about
 * merging the two
 */

typedef struct pkcs7_enc_content_st {
    ASN1_OBJECT *content_type;
    X509_ALGOR *algorithm;
    ASN1_OCTET_STRING *enc_data; /* [ 0 ] */
    const EVP_CIPHER *cipher;
} PKCS7_ENC_CONTENT;

typedef struct pkcs7_enveloped_st {
    ASN1_INTEGER *version;      /* version 0 */
    STACK_OF(PKCS7_RECIP_INFO) *recipientinfo;
    PKCS7_ENC_CONTENT *enc_data;
} PKCS7_ENVELOPE;

typedef struct pkcs7_signedandenveloped_st {
    ASN1_INTEGER *version;      /* version 1 */
    STACK_OF(X509_ALGOR) *md_algs; /* md used */
    STACK_OF(X509) *cert;       /* [ 0 ] */
    STACK_OF(X509_CRL) *crl;    /* [ 1 ] */
    STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
    PKCS7_ENC_CONTENT *enc_data;
    STACK_OF(PKCS7_RECIP_INFO) *recipientinfo;
} PKCS7_SIGN_ENVELOPE;

typedef struct pkcs7_digest_st {
    ASN1_INTEGER *version;      /* version 0 */
    X509_ALGOR *md;             /* md used */
    struct pkcs7_st *contents;
    ASN1_OCTET_STRING *digest;
} PKCS7_DIGEST;

typedef struct pkcs7_encrypted_st {
    ASN1_INTEGER *version;      /* version 0 */
    PKCS7_ENC_CONTENT *enc_data;
} PKCS7_ENCRYPT;

typedef struct pkcs7_st {
    /*
     * The following is non NULL if it contains ASN1 encoding of this
     * structure
     */
    unsigned char *asn1;
    long length;
# define PKCS7_S_HEADER  0
# define PKCS7_S_BODY    1
# define PKCS7_S_TAIL    2
    int state;                  /* used during processing */
    int detached;
    ASN1_OBJECT *type;
    /* content as defined by the type */
    /*
     * all encryption/message digests are applied to the 'contents', leaving
     * out the 'type' field.
     */
    union {
        char *ptr;
        /* NID_pkcs7_data */
        ASN1_OCTET_STRING *data;
        /* NID_pkcs7_signed */
        PKCS7_SIGNED *sign;
        /* NID_pkcs7_enveloped */
        PKCS7_ENVELOPE *enveloped;
        /* NID_pkcs7_signedAndEnveloped */
        PKCS7_SIGN_ENVELOPE *signed_and_enveloped;
        /* NID_pkcs7_digest */
        PKCS7_DIGEST *digest;
        /* NID_pkcs7_encrypted */
        PKCS7_ENCRYPT *encrypted;
        /* Anything else */
        ASN1_TYPE *other;
    } d;
} PKCS7;


PKCS7 vs CMS

PKCS#7 is an old standard from RSA Labs, published later on as an "informational RFC". Then, a new versions was produced, as an "Internet standard", i.e. with the seal of approval from the powers-that-be at IETF; a new name was invented for that: CMS. Newer versions were subsequently defined: RFC 3369, RFC 3852, RFC 5652. You can consider CMS and PKCS#7 to both designate the same standard, which has several versions.

CMS (PKCS#7) is a format for applying encryption, signatures and/or integrity checks on arbitrary binary messages (which can be large). It can be nested. It is the basis for some protocols such as time stamping. It is also frequently (ab)used as a container for X.509 certificates: the SignedData type includes a field for a set of signatures (and that set can be empty), and also a field to store arbitrary X.509 certificates which are considered as "potentially useful" for whoever processes the object.

CMS is nominally backward compatible: there are version fields at various places, and a library which understands a version of CMS should be able to process messages from older versions; moreover, such a library should also produce messages which are compatible with other versions, inasmuch as it is feasible given the message contents. See section 5.1 for an example: the "version" fields are set to the minimal values which still make sense given what else is in the structure.

        if (noout) {
            if (print)
                CMS_ContentInfo_print_ctx(out, cms, 0, NULL);
        } else if (outformat == FORMAT_SMIME) {
            if (to)
                BIO_printf(out, "To: %s\n", to);
            if (from)
                BIO_printf(out, "From: %s\n", from);
            if (subject)
                BIO_printf(out, "Subject: %s\n", subject);
            if (operation == SMIME_RESIGN)
                ret = SMIME_write_CMS(out, cms, indata, flags);
            else
                ret = SMIME_write_CMS(out, cms, in, flags);
        } else if (outformat == FORMAT_PEM)
            ret = PEM_write_bio_CMS_stream(out, cms, in, flags);
        else if (outformat == FORMAT_ASN1)
            ret = i2d_CMS_bio_stream(out, cms, in, flags);
        else {
            BIO_printf(bio_err, "Bad output format for CMS file\n");
            goto end;
        }

    else {
        if (to)
            BIO_printf(out, "To: %s\n", to);
        if (from)
            BIO_printf(out, "From: %s\n", from);
        if (subject)
            BIO_printf(out, "Subject: %s\n", subject);
        if (outformat == FORMAT_SMIME) {
            if (operation == SMIME_RESIGN)
                SMIME_write_PKCS7(out, p7, indata, flags);
            else
                SMIME_write_PKCS7(out, p7, in, flags);
        } else if (outformat == FORMAT_PEM)
            PEM_write_bio_PKCS7_stream(out, p7, in, flags);
        else if (outformat == FORMAT_ASN1)
            i2d_PKCS7_bio_stream(out, p7, in, flags);
        else {
            BIO_printf(bio_err, "Bad output format for PKCS#7 file\n");
            goto end;
        }



2) smtp如何发送邮件

https://technet.microsoft.com/en-us/library/dd441082.aspx

https://tools.ietf.org/html/rfc821

https://tools.ietf.org/html/rfc2554

https://tools.ietf.org/html/rfc2821

http://tools.ietf.org/html/rfc5321

邮件如何在smtp服务器之间路由 http://en.wikipedia.org/wiki/MX_record

如何通过邮件地址后缀的domain来查询邮件服务器(smtp)的地址:nslookup -type=mx sina.com



你可能感兴趣的:(cms,email,OpenSSL,sign,smime)