PHP mc-client 自动重连的奥秘

我们知道memcache的PHP客户端在连接mc后,如果中间memcached宕掉了,你们重启之后,mc-client会自动连接到server的,这是如何做到的呢?

先写一个test脚本:

<?php
$mc
= new Memcache
();
$mc->connect("10.10.10.10",11211
);

while(1){
$ret = $mc->set("a","a"
);
if(
$ret
) {
echo
"okn"
;
} else {
echo
"failn"
;
}
sleep(1
);
}

?>

启动脚本后,先宕掉memcached,再启动memcached:

                                   间隔时间
第一次失败的时间: 1240491343.709920
第一次重连:      1240491358.751046 15s
第二次重连:      1240491373.790693 15s
第三次重连:      1240491388.829329 15s
第四次重连:      1240491403.869810 15s

结论: mc client 失败后,如果没有主动重连,在15s后再使用该连接,将尝试重连,在15s内根本就不做尝试重连的动作,即使服务器15s内
又恢复正常服务,mc client也是不知道的

——————————————-

关于pconnect的研究:

先写一个test脚本:

<?php
$mc
= new Memcache();
while(
1){
$mc->pconnect("10.55.38.18",11211);
$ret = $mc->set("a","a");
if(
$ret) {
echo
"okn";
} else {
echo
"failn";
}
sleep(1);
}
?>

1. 虽然pconnect是针对apache类程序开发的,但是命令行程序里有相同的效果
2. 虽然$mc->pconnect("10.55.38.18",11211);是写在了循环里面,但是通过tcpdump发现,并没有每次都重连mc
3. 虽然好像每次都重连,其实都还是完全有connect里的机制来控制了,就是说循环里面的pconnect与写到循环外面没有任何的区别
4. 如果把里面的pconnect 换成connect,则效果就和我们想象的一样了,这样,不管服务器端是否有异常,每次都是真正的连接

程序在$mc->pconnect("10.55.38.18",11211);时报的两种错误:
第一种:Warning: Memcache::pconnect(): Can’t connect to 10.55.38.18:11211, Unknown error (0) in /usr/home/junjie2/testmc.php on line 4

第二种:PHP Warning:  Memcache::pconnect(): Can’t connect to 10.55.38.18:11211, Connection refused (111) in /usr/home/junjie2/testmc.php on line 4

前者是因为并没有真正去连接,只是发现连接不可用(已关闭)就直接返回错误了
后者是第一次失败的15s之后,就尝试做一次真正的重新连接,因为这是memcached还没有重启,就返回了 Connection refused,这样下次pconnect,又接着返回第一种错误,直到又过15s

php的memcahce-2.2.4 中文件 php_memcache.h 中有如下定义

#define MMC_DEFAULT_RETRY 15                             /* retry failed server after x seconds */

即:  15s内是不做真正的连接的。

注意: memcache客户端默认设置的超时时间为1s, 极容易超时的,所以你要知道,当超时后,客户端会主动关闭连接的,因为它以为服务器down了,然后就是15s内的操作统统返回失败。

如何避免这种问题呢?

办法一: 在第一个测试脚本中,使用了connect,而不是pconnect, 连接操作是在循环体外的;如果connect(注意不是pconnect)发生在循环体内就能及时发现连接已有效了,就不需要等待15s了,当然,这样的话,每次都是真实的重新连接了,而且特别需要注意的是,如果这样的话,close()操作也一定要写在循环体里面,否则你将创建n多连接,知道不允许你在连接了。

办法二: 使用addServer的方式: bool Memcache::addServer     ( string $host    [, int $port = 11211    [, bool $persistent    [, int $weight    [, int $timeout    [, int $retry_interval    [, bool $status    [, callback $failure_callback    [, int $timeoutms   ]]]]]]]] )

    将$retry_interva 设置为0,这样就每次都重新连接了。


关于http 压力测试工具

1. ab 、 webbench 、http_load的特点
这些测试工具都是单进程、非线程的程序,它的并发是通过异步实现的,虽然也实现的并发,就是说,同时确实存在着n个并发,但是需要注意的是,对于一个非常简单的接口来讲,客户端要做的事情和服务器端要做的事情差不太多;这时,一个单进程的程序去压一个多进程的程序,显然客户端可能一直忙于发请求,接受响应,而服务器端却很清闲,甚至n/3个httpd子进程就可以搞定n个并发了,这就是为什么n个并发,而服务器端的httpd子进程数却远小于n的可能的原因之一。

2. siege
siege 是一个单进程多线程的程序,只是测试结果数据太少,不太能说明问题

3. 尽管如此,我们还是可以用ab做多进程的并发的,如下:

for i in seq 1 50;do  nohup /data1/apache/bin/ab -n 10000 -c 1 "http://10.20.30.40/test.php" >ab.$i.txt &;done

这就是一个并发50,总数50万的ab压力测试

关于keep-alive

你的服务器是否打开了keep-alive 呢?看看httpd.conf 就知道了,但是也不是必须的,简单测试一下就知道了,用第三个请求比较稳妥一些,看看下面的几个请求,顺便理解一下HTTP/1.0  与 HTTP/1.1 的几个区别:

请求1:
———————————-
GET / HTTP/1.1
Host: phpor.net

返回结果的编码格式:Transfer-Encoding: chunked
没有立即关闭连接,说明:HTTP/1.1 默认支持(启用)keep-alive

———————————-

请求2:
———————————-
GET / HTTP/1.0
Host: pengyou.sina.com.cn
Connection: Keep-Alive

返回结果的编码格式:Content-Length: 5556       说明: HTTP/1.0 还不支持Transfer-Encoding: chunked的传输编码方式
没有立即关闭连接,说明:HTTP/1.0 也可以使用 keep-alive
———————————-

请求3:
———————————-
GET / HTTP/1.1
Host: phpor.net
Connection: Keep-Alive

这个就肯定keep-alive了
———————————-

请求4:
———————————-
GET / HTTP/1.0
Host: phpor.net

这个请求就;
1. 不能Transfer-Encoding: chunked
2. 不能keep-alive
———————————-

结论:
要了解某个server是否打开了keep-alive ,只需用telnet发一个类似于前三种的请求,看看是否立即关闭了就行了

全组合 之 PHP实现

<?php
$arr
= array(
array(
‘A1’,‘A2’
),
array(
‘B1’,‘B2’
),
array(
‘C1’,‘C2’
)
);

print_r(zuhe($arr,false));
// or
zuhe($arr,true
);

/**
* @param: $arr: array for deal
* @param: $echo: print at end or not
*/
function zuhe($arr, $echo = true
) {
$len = count($arr
);
if (
$len == 0
) return array();
if (
$len == 1
) {
if (
$echo
) {
foreach(
$arr[0] as $val
) {
echo
$val ."\n"
;
}
} else {
return
$arr[0
];
}
exit();
}
$tmparr
= array();
foreach(
$arr[0] as $val0
) {
foreach(
$arr[1] as $val1
) {
$tmparr[] = $val0 ."\t". $val1
;
}
}
array_shift($arr
);
array_shift($arr
);
array_unshift($arr, $tmparr
);
return
zuhe($arr, $echo
);
}
/**
* result:
* A1 B1 C1
* A1 B1 C2
* A1 B2 C1
* A1 B2 C2
* A2 B1 C1
* ….
* A2 B2 C2
*/
?>

实现2:

<?php

$arr = array(
    
‘gateway’=>array(‘set’,‘noset’
),
    
‘returntype’=>array(‘META’,‘TEXT’,‘TEXT2’
),
    
‘url’=>array(‘login.php’,‘set&not_login.php’,‘noset’
),
    
‘st’=>array(‘ok’,‘err’,‘no’
)
);

echo implode("\t\t\t\t",array_keys($arr))."\n"."\n";
//print_r(zuhe($arr,false));
// or
zuhe($arr,true, –20
);

/**
 * @param: $arr: array for deal
 * @param: $echo: print at end or not
 */
function zuhe($arr$echo true$width "\t"
) {
    
$len count($arr
);
    if (
$len == 
) return array();
    if (
$len == 1
) {
        if (
$echo
) {
            foreach( 
$arr[0] as $val
) {
                echo 
$val ."\n"
;
            }
        } else {
            return 
$arr[0
];
        }
        exit();
    }
    
$tmparr 
= array();
    
$arr0 array_shift($arr
);
    
$arr1 array_shift($arr
);
    foreach(
$arr0 as $val0
) {
        foreach(
$arr1 as $val1
) {
            if (
$width == "auto"
) {
                
// 希望能自动调整列宽,为实现
            
}else if ($width == "\t"
) {
                
$tmparr[] = $val0 ."\t"$val1
;
            } else {
                
$tmparr[] = sprintf("%${width}s",$val0) .sprintf("%${width}s",$val1
);
            }
        }
    }

    array_unshift($arr$tmparr);
    return 
zuhe($arr$echo$width
);
}
/**
 * result:
 * A1    B1    C1
 * A1     B1  C2
 * A1   B2  C1
 * A1   B2  C2
 * A2   B1  C1
 * ….
 * A2   B2  C2
 */

?>

利用openssl创建一个简单的CA

本文旨在利用开源openssl软件,在Linux(或UNIX/Cygwin)下创建一个简单的CA。我们可以利用这个CA进行PKI、数字证书相关的测试。比如,在测试用Tomcat或Apache构建HTTPS双向认证时,我们可以利用自己建立的测试CA来为服务器端颁发服务器数字证书,为客户端(浏览器)生成文件形式的数字证书(可以同时利用openssl生成客户端私钥)。

该简单的CA将建立在用户自己的目录下($HOME/testca),无需超级用户(root)权限。

一. 创建CA
1. 创建CA需要用到的目录和文件:
执行命令如下:
mkdir "$HOME/testca"
cd "$HOME/testca"
mkdir newcerts private conf
chmod g-rwx,o-rwx private
echo "01" > serial
touch index.txt

说明:
$HOME/testca为待建CA的主目录。其中newcerts子目录将存放CA签署(颁发)过的数字证书(证书备份目录)。而private目录用于存放CA的私钥。目录conf只是用于存放一些简化参数用的配置文件。

文件serial和index.txt分别用于存放下一个证书的序列号和证书信息数据库。
当然,偷懒起见,可以只用按照本文操作即可,不一定需要关心各个目录和文件的作用。

2. 生成CA的私钥和自签名证书(即根证书)
创建文件:
vi "$HOME/testca/conf/gentestca.conf"
文件内容如下:
####################################
[ req ]
default_keyfile = $ENV::HOME/testca/private/cakey.pem
default_md = md5
prompt = no
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions

[ ca_distinguished_name ]
organizationName = TestOrg
organizationalUnitName = TestDepartment
commonName = TestCA
emailAddress = ca_admin@testorg.com

[ ca_extensions ]
basicConstraints = CA:true
########################################

然后执行命令如下:
cd "$HOME/testca"
openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 2190 -config "$HOME/testca/conf/gentestca.conf"
执行过程中需要输入CA私钥的保护密码,假设我们输入密码: 888888

可以用如下命令查看一下CA自己证书的内容
openssl x509 -in cacert.pem -text -noout

3. 创建一个配置文件,以便后续CA日常操作中使用:
vi "$HOME/testca/conf/testca.conf"
文件内容如下:
####################################
[ ca ]
default_ca = testca # The default ca section

[ testca ]
dir = $ENV::HOME/testca # top dir
database = $dir/index.txt # index file.
new_certs_dir = $dir/newcerts # new certs dir

certificate = $dir/cacert.pem # The CA cert
serial = $dir/serial # serial no file
private_key = $dir/private/cakey.pem # CA private key
RANDFILE = $dir/private/.rand # random number file

default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = md5 # message digest method to use
unique_subject = no # Set to ‘no’ to allow creation of
# several ctificates with same subject.
policy = policy_any # default policy

[ policy_any ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

########################################

二. CA的日常操作
1. 根据证书申请请求签发证书
假设收到一个证书请求文件名为req.pem,文件格式应该是PKCS#10格式(标准证书请求格式)。

首先可以查看一下证书请求的内容,执行命令:
openssl req -in req.pem -text -noout
将看到证书请求的内容,包括请求者唯一的名字(DN)、公钥信息(可能还有一组扩展的可选属性)。

执行签发命令:
openssl ca -in req.pem -out cert.pem -config "$HOME/testca/conf/testca.conf"
执行过程中会要求输入访问CA的私钥密码(刚才设置的888888)。

完成上一步后,签发好的证书就是cert.pem,另外$HOME/testca/newcerts里也会有一个相同的证书副本(文件名为证书序列号)。
你可以执行以下语句来查看生成的证书的内容:
openssl x509 -in cert.pem -text -noout

2. 吊销证书(作废证书)
一般由于用户私钥泄露等情况才需要吊销一个未过期的证书。(当然我们用本测试CA时其时很少用到该命令,除非专门用于测试吊销证书的情况)
假设需要被吊销的证书文件为cert.pem,则执行以下命令吊销证书:
openssl ca -revoke cert.pem -config "$HOME/testca/conf/testca.conf"

3. 生成证书吊销列表文件(CRL)
准备公开被吊销的证书列表时,可以生成证书吊销列表(CRL),执行命令如下:
openssl ca -gencrl -out testca.crl -config "$HOME/testca/conf/testca.conf"
还可以添加-crldays和-crlhours参数来说明下一个吊销列表将在多少天后(或多少小时候)发布。

可以用以下命令检查testca.crl的内容:
openssl crl -in testca.crl -text -noout

三. 自己生成公钥密钥,并用测试CA签发数字证书
我们在平时测试时,可以自己用openssl为服务器或用户生成公钥密钥,并用上面创建的CA签发对应私钥(密钥)的数字证书。
假设,我们就用刚才创建CA的操作系统用户为名为testuser的用户创建数字证书,我们要把待创建的私钥、证书等都放在目录$HOME/testuser下:

1. 创建密钥和证书请求(证书请求里包含了公钥)
创建$HOME/testuser目录并执行命令:
mkdir $HOME/testuser
cd $HOME/testuser
openssl req -newkey rsa:1024 -keyout testkey.pem -keyform PEM -out testreq.pem -outform PEM -subj "/O=TestCom/OU=TestOU/CN=testuser"
执行过程中需要输入私钥的保护密码,假设我们输入密码: 222222

执行完后,testkey.pem即为用户的密钥,而testreq.pem即为证书请求。
可以用openssl req -in testreq.pem -text -noout查看证书请求的内容。

2. 用测试CA为testuser签发证书
同样还在$HOME/testuser目录下执行命令:
openssl ca -in testreq.pem -out testcert.pem -config "$HOME/testca/conf/testca.conf"
执行过程中需要输入CA的密钥保护密码(刚才设置的888888),并且最后询问你是否要给该用户签发证书时要选y。

执行完后,testcert.pem即为证书,
可以用命令openssl x509 -in testcert.pem -text -noout查看证书内容。

3. 制作一个PKCS12格式的文档(个人数字证书)
我们制作的这个PKCS#12文件将包含密钥、证书和颁发该证书的CA证书。该文件可以直接用于服务器数字证书或个人数字证书。
把前几步生成的密钥和证书制作成一个pkcs12文件的方法执行命令:
openssl pkcs12 -export -in testcert.pem -inkey testkey.pem -out testuser.p12 -name testuser -chain -CAfile "$HOME/testca/cacert.pem"
执行过程中需要输入保护密钥的密码(222222),以及新的保护pkcs12文件的密码。

执行完后,testuser.p12即为pkcs12文件。你可以直接拷贝到windows下,作为个人数字证书,双击导入IE后就可以使用了。该文件也可以直接用于tomcat作为服务器证书使用(我尽量在近期再写一篇关于如何自己用tomcat建立HTTPS双向认证测试环境的文章)。

若要查看testuser.p12的内容可以用命令openssl pkcs12 -in testuser.p12

说明:
– 本文中名词“个人数字证书”意识为包含私钥和证书的实体,而不是单指只保护公钥的数字证书。
– openssl版本OpenSSL 0.9.8g 19

JAVA 双向SSL,SOCKET客户端/服务端

实现技术:
JSSE(Java Security Socket Extension)
Server需要:
1)KeyStore: 其中保存服务端的私钥
2)Trust KeyStore:其中保存客户端的授权证书
Client需要:
1)KeyStore:其中保存客户端的私钥
2)Trust KeyStore:其中保存服务端的授权证书

使用Java自带的keytool命令,去生成这样信息文件:

1)生成服务端私钥,并且导入到服务端KeyStore文件中

2)根据私钥,导出服务端证书

3)将服务端证书,导入到客户端的Trust KeyStore中

采用同样的方法,生成客户端的私钥,客户端的证书,并且导入到服务端的Trust KeyStore中
1)keytool -genkey -alias clientkey -keystore kclient.keystore
2)keytool -export -alias clientkey -keystore kclient.keystore -file client.crt
3)keytool -import -alias clientkey -file client.crt -keystore tserver.keystore

Server:
Java代码
public class Server implements Runnable{   
  
    private static final int     DEFAULT_PORT                     = 7777;   
  
    private static final String SERVER_KEY_STORE_PASSWORD        = "123456";   
    private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "123456";   
  
    private SSLServerSocket      serverSocket;   
  
    /**
      * 启动程序
      *
      * @param args
      */  
    public static void main(String[] args) {   
         Server server = new Server();   
         server.init();   
         Thread thread = new Thread(server);   
         thread.start();   
     }   
  
    public synchronized void start() {   
        if (serverSocket == null) {   
             System.out.println("ERROR");   
            return;   
         }   
        while (true) {   
            try {   
                 Socket s = serverSocket.accept();   
                 InputStream input = s.getInputStream();   
                 OutputStream output = s.getOutputStream();   
  
                 BufferedInputStream bis = new BufferedInputStream(input);   
                 BufferedOutputStream bos = new BufferedOutputStream(output);   
  
                byte[] buffer = new byte[20];   
                 bis.read(buffer);   
                 System.out.println("——receive:——–"+new String(buffer).toString());   
  
                 bos.write("yes".getBytes());   
                 bos.flush();   
  
                 s.close();   
             } catch (Exception e) {   
                 System.out.println(e);   
             }   
         }   
     }   
    public void init() {   
        try {   
             SSLContext ctx = SSLContext.getInstance("SSL");   
  
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");   
             TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");   
  
             KeyStore ks = KeyStore.getInstance("JKS");   
             KeyStore tks = KeyStore.getInstance("JKS");   
  
             ks.load(new FileInputStream("src/ssl/kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray());   
             tks.load(new FileInputStream("src/ssl/tserver.keystore"), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());   
  
             kmf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());   
             tmf.init(tks);   
  
             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);   
  
             serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);   
             serverSocket.setNeedClientAuth(true);   
         } catch (Exception e) {   
             System.out.println(e);   
         }   
     }   
  
    public void run() {   
        // TODO Auto-generated method stub   
         start();   
     }   
}  

Client:
Java代码
package ssl;   
  
import java.io.BufferedInputStream;   
import java.io.BufferedOutputStream;   
import java.io.FileInputStream;   
import java.io.IOException;   
import java.io.InputStream;   
import java.io.OutputStream;   
import java.security.KeyStore;   
  
import javax.net.ssl.KeyManagerFactory;   
import javax.net.ssl.SSLContext;   
import javax.net.ssl.SSLSocket;   
import javax.net.ssl.TrustManagerFactory;   
  
/**
* SSL Client
*
* @author Leo
*/  
public class Client {   
  
    private static final String DEFAULT_HOST                     = "127.0.0.1";   
    private static final int     DEFAULT_PORT                     = 7777;   
  
    private static final String CLIENT_KEY_STORE_PASSWORD        = "123456";   
    private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";   
  
    private SSLSocket            sslSocket;   
  
    /**
      * 启动客户端程序
      *
      * @param args
      */  
    public static void main(String[] args) {   
        Client client = new Client();   
         client.init();   
         client.process();   
     }   
  
  
    public void process() {   
        if (sslSocket == null) {   
             System.out.println("ERROR");   
            return;   
         }   
        try {   
             InputStream input = sslSocket.getInputStream();   
             OutputStream output = sslSocket.getOutputStream();   
  
             BufferedInputStream bis = new BufferedInputStream(input);   
             BufferedOutputStream bos = new BufferedOutputStream(output);   
  
             bos.write("1234567890".getBytes());   
             bos.flush();   
  
            byte[] buffer = new byte[20];   
             bis.read(buffer);   
             System.out.println(new String(buffer));   
  
             sslSocket.close();   
         } catch (IOException e) {   
             System.out.println(e);   
         }   
     }   
  
  
    public void init() {   
        try {   
             SSLContext ctx = SSLContext.getInstance("SSL");   
  
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");   
             TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");   
  
             KeyStore ks = KeyStore.getInstance("JKS");   
             KeyStore tks = KeyStore.getInstance("JKS");   
  
             ks.load(new FileInputStream("src/ssl/kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray());   
             tks.load(new FileInputStream("src/ssl/tclient.keystore"), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());   
  
             kmf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());   
             tmf.init(tks);   
  
             ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);   
  
             sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT);   
         } catch (Exception e) {   
             System.out.println(e);   
         }   
     }   
  
}  

启动Server

启动Client,发送信息。

Server接收如下:正确解密
返回Client信息,

如此,就完成了服务端和客户端之间的基于身份认证的交互。

client采用kclient.keystore中的clientkey私钥进行数据加密,发送给server。
server采用tserver.keystore中的client.crt证书(包含了clientkey的公钥)对数据解密,如果解密成功,证明消息来自client,进行逻辑处理。

server采用kserver.keystore中的serverkey私钥进行数据加密,发送给client。
client采用tclient.keystore中的server.crt证书(包含了serverkey的公钥)对数据解密,如果解密成功,证明消息来自server,进行逻辑处理。

如果过程中,解密失败,那么证明消息来源错误。不进行逻辑处理。这样就完成了双向的身份认证。

keytools简介

keytool JAVA是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据 完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥(以证书形式)。通过keytool –help查看其用法;

创建证书Java 中的 keytool.exe (位于 JDK/Bin 目录下)可以用来创建数字证书,所有的数字证书是以一条一条(采用别名区别)的形式存入证书库的中,证书库中的一条证书包含该条证书的私钥,公钥和对应的 数字证书的信息。证书库中的一条证书可以导出数字证书文件,数字证书文件只包括主体信息和对应的公钥。
每一个证书库是一个文件组成,它有访问密码,在首次创建时,它会自动生成证书库,并要求指定访问证书库的密码。
在创建证书的的时候,需要填写证书的一些信息和证书对应的私钥密码。这些信息包括 CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx,它们的意思是:
Ø CN(Common Name – 名字与姓氏):其实这个“名字与姓氏”应该是域名,比如说localhost或是blog.devep.net之类的。输成了姓名,和真正运行的时候域名 不符,会出问题。浏览器访问时,弹出一个对话框,提示“安全证书上的名称无效,或者与站点名称不匹配”,用户选择继续还是可以浏览网页。但是用http client写程序访问的时候,会抛出类似于“javax.servlet.ServletException: HTTPS hostname wrong: should be ”的异常。
Ø OU(Organization Unit – 组织单位名称)
Ø O(Organization – 组织名称)
Ø L(Locality – 城市或区域名称)
Ø ST(State – 州或省份名称)
Ø C(Country – 国家名称)
可以采用交互式让工具提示输入以上信息,也可以采用参数,如:-dname “CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx”来自动创建。

创建一个证书
指定证书库为 D:/keystore/test,创建别名为 Tomcat 的一条证书,它指定用 RSA 算法生成,且指定密钥长度为 1024,证书有效期为 1 年:
keytool -genkey -alias Tomcat -keyalg RSA -keysize 1024 -keystore C:/keystore/test -validity 365
显示证书库中的证书使用如下命令: keytool -list -keystore C:/keystore/test 将显示 C:/keystore/test 证书库的的所有证书列表

导出到证书文件
使用命令:keytool -export -alias Tomcat -file C:/keystore/TC.cer -keystore C:/keystore/test 将把证书库C:/keystore/test 中的别名为 Tomcat 的证书导出到 TC.cer 证书文件中,它包含证书主体的信息及证书的公钥,不包括私钥,可以公开。
导出的证书文件是以二进制编码文件,无法用文本编辑器正确显示,可以加上 -rfc参数以一种可打印的编者编码输出。 如:
keytool -export -alias Tomcat -file C:/keystore/TC.cer -keystore C:/keystore/test –rfc

查看证书的信息
通过命令: keytool -printcert -file D:/keystore/TC.cer 可以查看证书文件的信息。 也可以在 Windows 资源管理器中双击产生的证书文件直接查看。

删除密钥库中的条目

keytool -delete -alias Tomcat -keystore C:/keystore/test
这条命令将 C:/keystore/test 库中的 Tomcat 这一条证书删除了。

修改证书条目口令
keytool -keypasswd -alias Tomcat -keystore C:/keystore/test,可以以交互的方式修改 C:/keystore/test 证书库中的条目为 Tomcat 的证书。
Keytool -keypasswd -alias Tomcat -keypass oldpasswd -new newpasswd -storepass storepasswd -keystore C:/keystore/test

这一行命令以非交互式的方式修改库中别名为 Tomcat 的证书的密码为新密码 newpasswd,行中的 oldpasswd 是指该条证书的原密码, storepasswd 是指证书库的密码。