原理请参考:http://blog.youkuaiyun.com/bytxl/article/details/16981931
直接上代码:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define MAXBUF 8192
extern int h_errno;
/*
void ShowCerts(SSL * ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL) {
printf("数字证书信息:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("证书: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("颁发者: %s\n", line);
free(line);
X509_free(cert);
} else
printf("无证书信息!\n");
}
*/
static char * domain2ip( char *psz_domainname )
{
struct hostent *p_hostent;
if ( !psz_domainname )
return NULL;
p_hostent = gethostbyname( psz_domainname );
if ( p_hostent == NULL )
{
printf( "error in gethostbyname: %s\n", hstrerror( h_errno ) );
return NULL;
}
else
{
printf( "name: %s\naddrtype; %d\naddrlength: %d\n", p_hostent->h_name, p_hostent->h_addrtype,
p_hostent->h_length );
printf( "ip address: %s\n", inet_ntoa(*(struct in_addr*)p_hostent->h_addr_list[0]));
return inet_ntoa(*(struct in_addr*)p_hostent->h_addr_list[0]);
}
}
//return value should be freeed outsid
// every 2 char to one char
static unsigned char* hex2str_2(char* str,int len)
{
unsigned char* psz_result =NULL;
int i,n;
char psz_two[2];
i = n = 0;
do
{
psz_result = ( unsigned char * )malloc( len/2 + 2 );
if (NULL == psz_result)
{
break;
}
memset( psz_result, 0, len/2 + 2 );
while( i < len )
{
psz_two[0] = str[i];
// 97 ~ 102: a ~ f
// 65 ~ 70 : A ~ F
// 48 ~ 57 : 0 ~ 1
if ( psz_two[0] >= 97 && psz_two[0] <= 102 )
{
psz_two[0] = psz_two[0] - 87;
}
else if ( psz_two[0] >= 65 && psz_two[0] <= 79 )
{
psz_two[0] = psz_two[0] - 55;
}
else if ( psz_two[0] >= 48 && psz_two[0] <= 57 )
{
psz_two[0] = psz_two[0] - 48;
}
else
{
return NULL;
}
psz_two[1] = str[i+1] ? str[i+1] : 0;
if ( psz_two[1] >= 97 && psz_two[1] <= 102 )
{
psz_two[1] = psz_two[1] - 87;
}
else if ( psz_two[1] >= 65 && psz_two[1] <= 70 )
{
psz_two[1] = psz_two[1] - 55;
}
else if ( psz_two[1] >= 48 && psz_two[1] <= 57 )
{
psz_two[1] = psz_two[1] - 48;
}
else
{
return NULL;
}
psz_result[n++] = 16 * psz_two[0] + psz_two[1];
i += 2;
}
psz_result[n] = 0;
}while(0);
return psz_result;
}
/************关于本文档********************************************
*filename: ssl-client.c
*purpose: 演示利用 OpenSSL 库进行基于 IP层的 SSL 加密通讯的方法,这是客户端例子
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-02-02 20:10
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to:Google
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
int sockfd, len;
struct sockaddr_in dest;
unsigned char buffer[MAXBUF + 1];
SSL_CTX *ctx;
SSL *ssl;
char psz_ip[24] = {0};
if (argc != 5) {
printf( "usage:\n\t%s Domainname port Certificate(include unencrypted key)"
"\n\tfor example:\t%s gateway.sandbox.push.apple.com 2195 cert.pem\n",
argv[0], argv[0]);
exit(0);
}
/* SSL 库初始化,参看 ssl-server.c 代码 */
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
if (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
exit(1);
}
// 载入用户私钥
if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
exit(1);
}
// 检查用户私钥是否正确
if (!SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 创建一个 socket 用于 tcp 通信 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket");
exit(errno);
}
printf("socket created\n");
strncpy( psz_ip, domain2ip( argv[1] ), sizeof( psz_ip ) );
//strncpy( psz_ip, argv[1], sizeof( psz_ip ) );
/* 初始化服务器端(对方)的地址和端口信息 */
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(atoi(argv[2]));
if (inet_aton(psz_ip, (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
perror(argv[1]);
exit(errno);
}
printf("address created\n");
/* 连接服务器 */
if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
perror("Connect ");
exit(errno);
}
printf("server connected\n");
/* 基于 ctx 产生一个新的 SSL */
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
/* 建立 SSL 连接 */
if (SSL_connect(ssl) == -1)
ERR_print_errors_fp(stderr);
else {
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
//ShowCerts(ssl);
}
/* 发消息给服务器 */
char psz_payload[128] = {0};
strncpy( psz_payload,
"{\"aps\":{\"alert\":\"A test message from cylan1\",\"badge\":1,\"sound\":\"default\"}}",
sizeof( psz_payload ) -1 );
bzero(buffer, MAXBUF + 1);
//unsigned char *psz_devicetoken_bin =
// hex2str_2( "cbb4b3ca1932e9ad4e761ac0da2e3cf47178f7fd2029a927036a5afb81f1373f", 64 );
unsigned char *psz_devicetoken_bin =
hex2str_2( "ee03ee86c6c57219d167d06f1fae87b09e857a2d635ffacc71046f4e1974594f", 64 );
printf( "\nint value devicetoken:\n" );
int i_tmp = 0;
for ( i_tmp = 0; i_tmp < strlen( ( char * )psz_devicetoken_bin ); i_tmp ++ )
{
printf( "psz_devicetoken_bin[%d]: %d\n", i_tmp, psz_devicetoken_bin[i_tmp] );
}
printf ( "\npsz_devicetoken_bin: %s\n", psz_devicetoken_bin );
printf ( "\nstrlen( psz_payload ): %d\n", ( int )strlen( psz_payload ) );
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 32;
memcpy( buffer + 3, psz_devicetoken_bin, 32 );
buffer[35] = 0;
buffer[36] = strlen( psz_payload );
memcpy( buffer + 37, psz_payload, strlen( psz_payload ) );
// 37 bytes before psz_payload
len = SSL_write(ssl, buffer, strlen( psz_payload ) + 37 );
if (len < 0)
printf
("\n消息'%s'发送失败!错误代码是%d,错误信息是'%s', len: %d\n",
buffer, errno, strerror(errno), len);
else
printf("\n消息'%s'发送成功,共发送了%d个字节!\n",
buffer, len);
/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
bzero(buffer, MAXBUF + 1);
/* 接收服务器来的消息 */
len = SSL_read(ssl, buffer, MAXBUF);
if (len > 0)
printf("接收消息成功:'%s',共%d个字节的数据\n",
buffer, len);
else {
printf
("消息接收失败!错误代码是%d,错误信息是'%s'\n",
errno, strerror(errno));
goto finish;
}
finish:
/* 关闭连接 */
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockfd);
SSL_CTX_free(ctx);
return 0;
}
编译:
gcc -g -Werror -Wall ./push.c -o ./push -L /usr/lib -lssl -lcrypto
运行:
./push gateway.sandbox.push.apple.com 2195 ./aps_development_merge.pem ./aps_development_unencrypted.key