【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は、mime形式の区切り記号ではなくbase 64ブロックに情報原文を含め、メール送受信サーバによって情報が再構築されないことを保証しやすく、検証に失敗します.
-nocertsはオプションで、設定すればaliceの証明書(公開鍵など)は署名情報に含まれません(base 64ブロック)
-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、メール受信
pop 3の例について
コメント:
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;
};

PKCS 7の繰返し定義
/*-
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
", to); if (from) BIO_printf(out, "From: %s
", from); if (subject) BIO_printf(out, "Subject: %s
", 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
"); goto end; }
    else {
        if (to)
            BIO_printf(out, "To: %s
", to); if (from) BIO_printf(out, "From: %s
", from); if (subject) BIO_printf(out, "Subject: %s
", 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
"); 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