SSL/TLS でアクセスしてみよう (2)

前へ << SSL/TLS でアクセスしてみよう (1) RSA で暗号化してみよう (1) >> 次へ

前項のクライアントは…

前項のクライアント https-client.c は、 不完全です。暗号化自体は正常に行えますので、盗聴と改竄は防げます。 しかし、なりすましは防止できません。 つまり、第三者がネットショップの主催者になりすましたら? https-client はなりすましを検出することができず、 相手がネットショップ主催者だと信じて、個人情報を送信してしまうかもしれません。

なりすましは、サーバ認証を行うことで防ぐことができます。

公開鍵と秘密鍵は誰でも作れます。 誰でも証明局になることができます (公開鍵と秘密鍵を作った本人でも)。 よって、サーバ証明書も誰でも発行できます。

(以下未稿)

サーバ認証対応版

実行例は以下の通り。 https://www.rsasecurity.com/ にアクセスした場合。
% ./https-client-2 rsasecurity.com /
使用する暗号化方式は RC4-MD5 です。
使用するプロトコルは TLSv1/SSLv3 です。
サーバ証明書:
   Subject: CN=www.rsasecurity.com,OU=Information Services,O=RSA Security Inc.,L=Bedford,ST=Massachusetts,C=US
   Issuer: C=US,ST=Massachusetts,L=Bedford,CN=RSA Corporate Server CA,OU=KCA Services,O=RSA Security Inc.
   有効期限開始年月日: Sep 16 14:55:55 2003 GMT
   有効期限終了年月日: Sep 16 14:55:55 2004 GMT
認証に成功しました。エラーはありません。
サーバからのレスポンス
(略)
https://www2.ggn.net/cgi-bin/ssl にアクセスした場合。
% ./https-client-2
使用する暗号化方式は EDH-RSA-DES-CBC3-SHA です。
使用するプロトコルは TLSv1/SSLv3 です。
サーバ証明書:
   Subject: CN=www.ggn.com,O=Gillette Global Network,L=New York,ST=New York,C=US
   Issuer: O=Gillette Global Network,L=New York,ST=New York,C=US
   有効期限開始年月日: Feb 18 00:49:07 2000 GMT
   有効期限終了年月日: Feb 17 00:49:07 2001 GMT
認証時にエラー発生。続行します。
  SSL_get_verify_result の戻り値 = 20
    unable to get local issuer certificate
サーバからのレスポンス
HTTP/1.1 200 OK
Date: Sat, 14 Jun 2003 12:38:52 GMT
Server: Apache/1.3.26 (Unix) GGN-MM/1.3.1 mod_ssl/2.8.10 OpenSSL/0.9.6d
Connection: close
Content-Type: text/html
(略)

https-client-2.c

    1: /*
    2:  * $Id: https-client-2.c,v 1.6 2005/02/19 16:01:53 68user Exp $
    3:  *
    4:  * HTTPS (SSL/TLS) クライアント (2)
    5:  *   サーバ認証・証明書情報の表示
    6:  *
    7:  * written by 68user  http://X68000.q-e-d.net/~68user/
    8:  */
    9: 
   10: #include <stdio.h>
   11: #include <string.h>
   12: #include <sys/types.h>
   13: #include <netdb.h>
   14: #include <sys/socket.h>
   15: #include <netinet/in.h>
   16: #include <sys/types.h>
   17: #include <sys/uio.h>
   18: #include <unistd.h>
   19: 
   20: #include <openssl/crypto.h>
   21: #include <openssl/x509.h>
   22: #include <openssl/pem.h>
   23: #include <openssl/ssl.h>
   24: #include <openssl/err.h>
   25: 
   26: #define BUF_LEN 256
   27: 
   28: /* root CA の証明書 */
   29: #define ROOT_CA "ca-bundle.crt"
   30: 
   31: void verify_ssl(SSL *ssl);
   32: 
   33: int
   34: main(argc, argv)
   35:      int argc;
   36:      char *argv[];
   37: {
   38:   int err;
   39:   int s;
   40:   struct hostent *servhost; 
   41:   struct sockaddr_in server;
   42:   struct servent *service; 
   43: 
   44:   SSL *ssl;
   45:   SSL_CTX *ctx;
   46:   SSL_METHOD *(*ssl_method)() = SSLv23_client_method;
   47:   X509 *server_cert;
   48: 
   49:   char request[BUF_LEN];
   50: 
   51:   char *host = "www2.ggn.net";
   52:   char *path = "/cgi-bin/ssl";
   53: 
   54:   if ( argc > 1 ){
   55:     if ( strcmp(argv[1], "-sslv2") == 0 ){
   56:       ssl_method = SSLv2_client_method;
   57:       argc--; argv++;
   58:     } else if ( strcmp(argv[1], "-sslv3") == 0 ){
   59:       ssl_method = SSLv3_client_method;
   60:       argc--; argv++;
   61:     } else if ( strcmp(argv[1], "-tlsv1") == 0 ){
   62:       ssl_method = TLSv1_client_method;
   63:       argc--; argv++;
   64:     } else if ( strcmp(argv[1], "-sslv23") == 0 ){
   65:       ssl_method = SSLv23_client_method;
   66:       argc--; argv++;
   67:     }
   68:   }
   69:   if ( argc == 3 ){
   70:     host = argv[1];
   71:     path = argv[2];
   72:   }
   73: 
   74:   servhost = gethostbyname(host);
   75:   if ( servhost == NULL ){
   76:     fprintf(stderr, "[%s] から IP アドレスへの変換に失敗しました。\n", host);
   77:     return 0;
   78:   }
   79: 
   80:   bzero((char *)&server, sizeof(server));
   81: 
   82:   server.sin_family = AF_INET;
   83: 
   84:   bcopy(servhost->h_addr, (char *)&server.sin_addr, servhost->h_length);
   85: 
   86:   /* ポート番号取得 */
   87:   service = getservbyname("https", "tcp");
   88:   if ( service != NULL ){
   89:     server.sin_port = service->s_port;
   90:   } else {
   91:     /* 取得できなかったら、ポート番号を 443 に決め打ち */
   92:     server.sin_port = htons(443);
   93:   }
   94: 
   95:   s = socket(AF_INET, SOCK_STREAM, 0); 
   96:   if ( s < 0 ){
   97:     fprintf(stderr, "ソケットの生成に失敗しました。\n");
   98:     return 1;
   99:   }
  100: 
  101:   if ( connect(s, (struct sockaddr*) &server, sizeof(server)) == -1 ){
  102:     fprintf(stderr, "connect に失敗しました。\n");
  103:     return 1;
  104:   }
  105: 
  106:   /* ここからが SSL */
  107: 
  108:   SSL_library_init(); 
  109:   SSL_load_error_strings(); 
  110:   ctx = SSL_CTX_new(ssl_method());
  111: 
  112:   /* rootCA を登録 */
  113:   if ( ! SSL_CTX_load_verify_locations(ctx, ROOT_CA, NULL) ){
  114:     printf("rootCA [%s] の読み込みに失敗しましたが続行します。\n", ROOT_CA);
  115:   }
  116: 
  117:   ssl = SSL_new(ctx);
  118:   SSL_set_fd(ssl, s);
  119:   err = SSL_connect(ssl);
  120: 
  121:   /* どの暗号化方式を使用するかを表示 (DES-CBC3-MD5 など) */
  122:   {
  123:     const char *cipher;
  124:     cipher = SSL_get_cipher(ssl);
  125:     printf("使用する暗号化方式は %s です。\n", cipher);
  126:   }
  127: 
  128:   /* どの SSL プロトコルを使用するかを表示 (SSLv2, SSLv3/TLSv1 など) */
  129:   {
  130:     char *version;
  131:     version = SSL_get_cipher_version(ssl);
  132:     printf("使用するプロトコルは %s です。\n", version);
  133:   }
  134: 
  135:   server_cert = SSL_get_peer_certificate(ssl); 
  136:   printf("サーバ証明書:\n");
  137: 
  138:   {
  139:     BIO *STDout = BIO_new_fp(stdout, BIO_NOCLOSE);
  140: 
  141:     BIO_printf(STDout, "   Subject: ");
  142:     X509_NAME_print_ex(STDout, X509_get_subject_name(server_cert), 0, XN_FLAG_RFC2253);
  143:     BIO_printf(STDout, "\n");
  144: 
  145:     BIO_printf(STDout, "   Issuer: ");
  146:     X509_NAME_print_ex(STDout, X509_get_issuer_name(server_cert), 0, XN_FLAG_RFC2253);
  147:     BIO_printf(STDout, "\n");
  148: 
  149:     ASN1_TIME *notBefore = X509_get_notBefore(server_cert);
  150:     ASN1_TIME *notAfter  = X509_get_notAfter(server_cert);
  151: 
  152:     BIO_printf(STDout, "   有効期限開始年月日: ");
  153:     ASN1_TIME_print(STDout, notBefore);
  154:     BIO_printf(STDout, "\n");
  155:     BIO_printf(STDout, "   有効期限終了年月日: ");
  156:     ASN1_TIME_print(STDout, notAfter);
  157:     BIO_printf(STDout, "\n");
  158:     BIO_free(STDout);
  159:   }
  160: 
  161:   verify_ssl(ssl);
  162: 
  163:   X509_free(server_cert);
  164: 
  165:   /* リクエストを送る */
  166: 
  167:   sprintf(request, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
  168:           path, host);
  169: 
  170:   err = SSL_write(ssl, request, strlen(request));
  171: 
  172:   /* web サーバからレスポンスを受け取る */
  173: 
  174:   printf("サーバからのレスポンス\n");
  175: 
  176:   while (1){
  177:     char buf[BUF_LEN];
  178:     int read_size;
  179:     read_size = SSL_read(ssl, buf, sizeof(buf)-1);
  180:     buf[read_size] = '\0';
  181:     
  182:     if ( read_size > 0 ){
  183:       write(1, buf, read_size);
  184:     } else {
  185:       break;
  186:     }
  187:   }
  188:   
  189:   SSL_shutdown(ssl); 
  190:   
  191:   close(s);
  192:   SSL_free(ssl); 
  193:   SSL_CTX_free(ctx);
  194: 
  195:   return 0;
  196: }
  197: 
  198: 
  199: void
  200: verify_ssl(SSL *ssl){
  201:   long verify_result;
  202: 
  203:   struct sslerror_t {
  204:     long result_code;
  205:     char *ssl_error;
  206:     char *ssl_errordetail;
  207:   };
  208: 
  209:   struct sslerror_t error_table[]={
  210:     {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
  211:      "unable to get issuer certificate. ",
  212:      "the issuer certificate could not be found: this occurs if the"
  213:      "issuer certificate of an untrusted certificate cannot be found."
  214:     },
  215:     {X509_V_ERR_UNABLE_TO_GET_CRL,
  216:      "unable to get certificate CRL. ",
  217:      "the CRL of a certificate could not be found. Unused."
  218:     },
  219:     {X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
  220:      "unable to decrypt certificate's signature",
  221:      "the certificate signature could not be decrypted. This means that"
  222:      "the actual signature value could not be determined rather than it"
  223:      "not matching the expected value, this is only meaningful for RSA keys"
  224:     },
  225:     {X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
  226:      "unable to decrypt CRL's signature",
  227:      "the CRL signature could not be decrypted: this means that the"
  228:      "actual signature value could not be determined rather than it not"
  229:      "matching the expected value. Unused."
  230:     },
  231:     {X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
  232:      "unable to decode issuer public key",
  233:      "the public key in the certificate SubjectPublicKeyInfo could not be read"
  234:     },
  235:     {X509_V_ERR_CERT_SIGNATURE_FAILURE,
  236:      "certificate signature failure",
  237:      "the signature of the certificate is invalid"
  238:     },
  239:     {X509_V_ERR_CRL_SIGNATURE_FAILURE,
  240:      "CRL signature failure",
  241:      "the signature of the certificate is invalid. Unused"
  242:     },
  243:     {X509_V_ERR_CERT_NOT_YET_VALID,
  244:      "証明書の有効期限が未来日付",
  245:      "証明書が有効になる開始日付にまだ達していません。"
  246:      "時刻設定がおかしくない?"
  247:     },
  248:     {X509_V_ERR_CERT_HAS_EXPIRED,
  249:      "証明書の有効期限切れ",
  250:      "証明書が古いす",
  251:     },
  252:     {X509_V_ERR_CRL_NOT_YET_VALID,
  253:      "CRL is not yet valid",
  254:      "the CRL is not yet valid. Unused."
  255:     },
  256:     {X509_V_ERR_CRL_HAS_EXPIRED,
  257:      "CRL has expired",
  258:      "the CRL has expired. Unused."
  259:     },
  260:     {X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
  261:      "format error in certificate's notBefore field",
  262:      "the certificate notBefore field contains an invalid time"
  263:     },
  264:     {X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
  265:      "format error in certificate's notAfter field",
  266:      "the certificate notAfter field contains an invalid time."
  267:     },
  268:     {X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
  269:      "format error in CRL's lastUpdate field",
  270:      "the CRL lastUpdate field contains an invalid time. Unused."
  271:     },
  272:     {X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
  273:      "format error in CRL's nextUpdate field",
  274:      "the CRL nextUpdate field contains an invalid time. Unused.",
  275:     },
  276:     {X509_V_ERR_OUT_OF_MEM,
  277:      "メモリ不足",
  278:      "メモリを確保できませんでした。このエラーは発生するべきではありません。"
  279:     },
  280:     {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
  281:      "self signed certificate",
  282:      "the passed certificate is self signed and the same certificate"
  283:      "cannot be found in the list of trusted certificates."
  284:     },
  285:     {X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
  286:      "self signed certificate in certificate chain",
  287:      "the certificate chain could be built up using the untrusted"
  288:      "certificates but the root could not be found locally."
  289:     },
  290:     {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
  291:      "unable to get local issuer certificate",
  292:      "the issuer certificate of a locally looked up certificate could not"
  293:      "be found. This normally means the list of trusted certificates is"
  294:      "not complete."
  295:     },
  296:     {X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
  297:      "unable to verify the first certificate",
  298:      "no signatures could be verified because the chain contains only one"
  299:      "certificate and it is not self signed."
  300:     },
  301:     {X509_V_ERR_CERT_CHAIN_TOO_LONG,
  302:      "certificate chain too long",
  303:      "the certificate chain length is greater than the supplied maximum"
  304:      "depth. Unused."
  305:     },
  306:     {X509_V_ERR_CERT_REVOKED,
  307:      "certificate revoked",
  308:      "the certificate has been revoked. Unused."
  309:      },
  310:     {X509_V_ERR_INVALID_CA,
  311:      "invalid CA certificate",
  312:      "a CA certificate is invalid. Either it is not a CA or its"
  313:      "extensions are not consistent with the supplied purpose."
  314:     },
  315:     {X509_V_ERR_PATH_LENGTH_EXCEEDED,
  316:      "path length constraint exceeded",
  317:      "the basicConstraints pathlength parameter has been exceeded."
  318:     },
  319:     {X509_V_ERR_INVALID_PURPOSE,
  320:      "unsupported certificate purpose",
  321:      "the supplied certificate cannot be used for the specified purpose."
  322:     },
  323:     {X509_V_ERR_CERT_UNTRUSTED,
  324:      "certificate not trusted",
  325:      "the root CA is not marked as trusted for the specified purpose."
  326:     },
  327:     {X509_V_ERR_CERT_REJECTED,
  328:      "証明書が拒絶された",
  329:      "the root CA is marked to reject the specified purpose.",
  330:     },
  331:     {X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
  332:      "subject issuer mismatch",
  333:      "the current candidate issuer certificate was rejected because its"
  334:      "subject name did not match the issuer name of the current"
  335:      "certificate. Only displayed when the -issuer_checks option is set."
  336:     },
  337:     {X509_V_ERR_AKID_SKID_MISMATCH,
  338:      "authority and subject key identifier mismatch",
  339:      "the current candidate issuer certificate was rejected because its"
  340:      "subject key identifier was present and did not match the authority"
  341:      "key identifier current certificate. Only displayed when the"
  342:      "-issuer_checks option is set."
  343:     },
  344:     {X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
  345:      "authority and issuer serial number mismatch",
  346:      "the current candidate issuer certificate was rejected because its"
  347:      "issuer name and serial number was present and did not match the"
  348:      "authority key identifier of the current certificate. Only displayed"
  349:      "when the -issuer_checks option is set."
  350:     },
  351:     {X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
  352:      "key usage does not include certificate signing",
  353:      "the current candidate issuer certificate was rejected because its"
  354:      "keyUsage extension does not permit certificate signing."
  355:     },
  356:     {X509_V_ERR_APPLICATION_VERIFICATION,
  357:      "application verification failure",
  358:      "an application specific error. Unused."
  359:     }
  360:   };
  361: 
  362:   verify_result = SSL_get_verify_result(ssl);
  363: 
  364:   if ( verify_result == X509_V_OK ){
  365:     printf("認証に成功しました。エラーはありません。\n");
  366:   } else {
  367:     int i;
  368: 
  369:     printf("認証時にエラー発生。続行します。\n");
  370:     for ( i=0 ; i<sizeof(error_table)/sizeof(error_table[0]) ; i++ ){
  371:       if ( error_table[i].result_code == verify_result ){
  372:         printf("  SSL_get_verify_result の戻り値 = %d\n", (int)verify_result);
  373:         printf("    %s\n", error_table[i].ssl_error);
  374:         break;
  375:       }
  376:     }
  377:   }
  378: }
rootCA の証明書。mod_ssl の配布物に含まれているものをそのままいただきました。 https-client-2 を実行する際、カレントディレクトリに置いてください。 ca-bundle.crt
前へ << SSL/TLS でアクセスしてみよう (1) RSA で暗号化してみよう (1) >> 次へ

ご意見・ご指摘は Twitter: @68user までお願いします。