加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number 问题的解决

(2013-04-10 08:26:53)
标签:

1408f10b

ssl

tls

解决思路

分类: 电脑技术-Java
    在一个项目开发过程中,需要Java客户端和C++开发的服务端进行 TLS链接,协议是 TLSv1,双向验证,在连调过程中,一开始客户端怎么也连不上服务端,查询服务端日志,发现如下错误:
    error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number.

    初始代码:
    try {
      SSLContext sslContext = SSLContext.getInstance("TLSv1");

      // TrustManager
      KeyStore kstm = KeyStore.getInstance("JKS"); 
      kstm.load(new FileInputStream("F:/box/tclient.keystore"), "123456".toCharArray()); 
       
      TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509" ); 
      tmf.init(kstm);
      TrustManager[] tm = tmf.getTrustManagers(); 

      // KeyManager      
      KeyStore ks_p = KeyStore.getInstance("PKCS12");
      FileInputStream fis = new FileInputStream("F:/box/SMSCERT.p12");
      ks_p.load(fis, "666666".toCharArray());

      KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      keyFactory.init(ks_p, "666666".toCharArray());
      KeyManager[] keyManagers = keyFactory.getKeyManagers();

      // create SSL socket
      sslContext.init(keyManagers, tm, new java.security.SecureRandom());
      SSLSocketFactory ssf = sslContext.getSocketFactory();
      
      SSLSocket  socket= (SSLSocket )ssf.createSocket("172.23.159.98", 4001);

      // write message
      socket.getOutputStream().write("1111111111111111".getBytes());
      socket.getOutputStream().flush();
      
    } catch (Exception e) {
      e.printStackTrace();
   

    客户端执行时一直等待在ocket.getOutputStream().write() 直至超时, 服务端提示握手失败,给出如题目的错误信息。

    而C++开发的客户端却能够联通正常, 代码如下:
======== C++ =============
  vector calist;
  BIO *pBio = NULL;
  pBio = BIO_new_file("SMrootCAcert.pem", "r");
  X509 *pTLRoot = PEM_read_bio_X509(pBio, NULL, NULL, NULL);
  calist.push_back(pTLRoot);
  BIO_free(pBio);

  pBio = BIO_new_file("SMsecLevelCAcert.pem", "r");
  X509 *pTLlevel = PEM_read_bio_X509(pBio, NULL, NULL, NULL);
  calist.push_back(pTLlevel);
  BIO_free(pBio);
  
  //本地证书
  pBio = BIO_new_file("SMStheLeafcert.pem", "r");
  X509 *a_SMSCert = PEM_read_bio_X509(pBio, NULL, NULL, NULL);
  BIO_free(pBio);

  //本地私钥
  pBio = BIO_new_file("theLeafprikey.pem", "r");
  EVP_PKEY *a_SMSPKey = PEM_read_bio_PrivateKey(pBio, NULL, NULL, NULL);
  BIO_free(pBio);
  
  pTLS = new CTLS_Translate_Client( a_SMSCert, calist,  a_SMSPKey );

startup:
  rt = pTLS->CTLS_Translate_Client_Init(SSL_VERIFY_PEER);
//  rt = pTLS->CTLS_Translate_Client_Init(SSL_VERIFY_NONE);
  if((int)rt != PUB_SHARE_OK)
  {
    printf("[ERROR] initialize failed! error code %d\n", rt);
    delete pTLS;
    return -1;
  }
  rt = pTLS->TLS_StartUp(ServerIP, ServerPort);
  if((int)rt != PUB_SHARE_OK)
  {
    printf("[ERROR] Sconnect SM failed! error code %d\n", rt);
    printf("[INFO]  try to connect SM 3 seconds later\n\n");
    sleep(3);
    goto startup;
  }

  //测试获取服务器方证书
  printf("\n---------------------------------------------*\n");
  printf("[SM Certificate:]\n");
  BIO   *bio_f = NULL;
  const X509  *peer = NULL;
  pTLS->TLS_Get_Peer_Cert(&peer);
  bio_f=BIO_new(BIO_s_file_internal());
  BIO_set_fp(bio_f,stdout,BIO_NOCLOSE);
  X509_print(bio_f, (X509  *)peer);
  BIO_free(bio_f);
  printf("---------------------------------------------*\n");

  CRYPTO_malloc_init(); 
  OpenSSL_add_all_algorithms();
  ERR_load_crypto_strings();

  printf( "TLS Connect Server : %s:%d SUCCESS\n", ServerIP, ServerPort );

  memset(message_buf, 0, 20);
  strcpy((char*)message_buf,"1111111111111111");
  memset(w_message_buf, 0, 20);
  if(PUB_SHARE_OK==pTLS->TLS_Write(message_buf,16,0,&WriteLen))
  {
    printf("TLS_Write SUCCESSFULLY!");
  }
==========End C++===========
  经代码检查 CTLS_Translate_Client 是使用openssl v0.9.8开发包的包装类,确实是使用了TLSv1协议。

  客户端试过多种办法,都没有解决问题:1、试验不同的协议 如:SSL,SSLv2,SSLv3;2、试验不同的证书加载方式,将本地密钥的公开密钥放到可信任列表中;3、调整超时时间,增加延时(因为服务器是嵌入式,速度没有台式机快);4、调整为单线程处理,因为网上有文章怀疑是多线程问题。

  以上办法没有解决,继续从网上搜索资料,变换不同的关键字,使用不同的搜索引擎。
  最后,在 serverfault.com 和 fixunix.com 网站的两篇文章的启发下解决了问题。(网址参加最后)
  论坛文章中分别提到:
   Changing Courier's setting "TLS_STARTTLS_PROTOCOL" from imapd-ssl -configuration file to "TLS_STARTTLS_PROTOCOL=SSL3" seemed to fix this problem for me.
    和 
   This error is returned when SSL version number in peer record can not be recognized (not 20, 30, 31).” 

     由此想到,可能因为是要服务端和客户端要双向验证,服务端需要客户端的协议版本号的原因导致。根据此思路调整代码,果然解决问题。

     正确连接服务器的代码如下:
        SSLSocket  socket= (SSLSocket )ssf.createSocket("172.23.159.98", 4001);

        SSLParameters parameter =new SSLParameters();
        parameter.setProtocols(new String[]{"TLSv1"});
        //parameter.setNeedClientAuth(true);
        //parameter.setWantClientAuth(true);
        socket.setSSLParameters(parameter);
        socket.startHandshake();

     关键就是通过setSSLParameters指定互联的协议是 TLSv1. startHandshake可以不需要,但增加上可以加快连接,今早发现问题。

     以上就是问题解决思路和办法,下面是参考文档:
    《Re: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version - Openssl》 
    《ssl握手过程和服务器认证》 http://www.iteye.com/topic/327645
    《ssl双向认证和单向认证原理》 http://edison0663.iteye.com/blog/996526

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有