getCipherSuite() 返回 SSL_NULL_WITH_NULL_NULL
Posted
技术标签:
【中文标题】getCipherSuite() 返回 SSL_NULL_WITH_NULL_NULL【英文标题】:getCipherSuite() returns SSL_NULL_WITH_NULL_NULL 【发布时间】:2014-08-13 23:41:47 【问题描述】:我正在尝试使用以编程方式生成的自签名证书在 android 上创建 https 服务器。我觉得我很接近,但我仍然无法连接到 https 服务器。当我尝试使用 openssl 连接到服务器时,我得到以下信息:
openssl s_client -connect 192.168.1.97:8888
CONNECTED(00000003)
2895:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:/SourceCache/OpenSSL098/OpenSSL098-50/src/ssl/s23_clnt.c:602:
代码如下:
public class HttpsHello
private static String domainName = "localhost";
static
Security.addProvider(new BouncyCastleProvider());
public static void test(String[] args)
try
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair KPair = keyPairGenerator.generateKeyPair();
X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();
int ran = new SecureRandom().nextInt();
if (ran < 0) ran = ran * -1;
BigInteger serialNumber = BigInteger.valueOf(ran);
v3CertGen.setSerialNumber(serialNumber);
v3CertGen.setIssuerDN(new X509Principal("CN=" + domainName + ", OU=None, O=None L=None, C=None"));
v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30));
v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365 * 10)));
v3CertGen.setSubjectDN(new X509Principal("CN=" + domainName + ", OU=None, O=None L=None, C=None"));
v3CertGen.setPublicKey(KPair.getPublic());
// v3CertGen.setSignatureAlgorithm("MD5WithRSAEncryption");
v3CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption");
X509Certificate pkcert = v3CertGen.generateX509Certificate(KPair.getPrivate());
// FileOutputStream fos = new FileOutputStream("/path/to/testCert.cert");
// fos.write(pkcert.getEncoded());
// fos.close();
ByteArrayInputStream cert = new ByteArrayInputStream(pkcert.getEncoded());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("localhost", pkcert);
// ks.load(cert,null);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance("X509");
kmf.init(ks, null);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory ssf = sc.getServerSocketFactory();
SSLServerSocket s
= (SSLServerSocket) ssf.createServerSocket(8888);
s.setEnabledCipherSuites(s.getSupportedCipherSuites());
// s.setEnabledCipherSuites(new String[]"SSL_DH_anon_WITH_RC4_128_MD5");
// s.setEnabledCipherSuites(new String[]"SHA1WithRSAEncryption");
System.out.println("Server started:");
printServerSocketInfo(s);
// Listening to the port
SSLSocket c = (SSLSocket) s.accept();
printSocketInfo(c);
BufferedWriter w = new BufferedWriter(
new OutputStreamWriter(c.getOutputStream()));
BufferedReader r = new BufferedReader(
new InputStreamReader(c.getInputStream()));
String m = r.readLine();
w.write("HTTP/1.0 200 OK");
w.newLine();
w.write("Content-Type: text/html");
w.newLine();
w.newLine();
w.write("<html><body>Hello world!</body></html>");
w.newLine();
w.flush();
w.close();
r.close();
c.close();
catch (Exception e)
e.printStackTrace();
private static void printSocketInfo(SSLSocket s)
System.out.println("Socket class: " + s.getClass());
System.out.println(" Remote address = "
+ s.getInetAddress().toString());
System.out.println(" Remote port = " + s.getPort());
System.out.println(" Local socket address = "
+ s.getLocalSocketAddress().toString());
System.out.println(" Local address = "
+ s.getLocalAddress().toString());
System.out.println(" Local port = " + s.getLocalPort());
System.out.println(" Need client authentication = "
+ s.getNeedClientAuth());
SSLSession ss = s.getSession();
System.out.println(" Cipher suite = " + ss.getCipherSuite());
System.out.println(" Protocol = " + ss.getProtocol());
private static void printServerSocketInfo(SSLServerSocket s)
System.out.println("Server socket class: " + s.getClass());
System.out.println(" Socker address = "
+ s.getInetAddress().toString());
System.out.println(" Socker port = "
+ s.getLocalPort());
System.out.println(" Need client authentication = "
+ s.getNeedClientAuth());
System.out.println(" Want client authentication = "
+ s.getWantClientAuth());
System.out.println(" Use client mode = "
+ s.getUseClientMode());
谢谢。
编辑:我查看了两个 keytool 生成的密钥库,一个有效,一个无效。一个有效的密钥库在其中有一个 PrivateKeyEntry 的条目,而那个不起作用的密钥库有一个trustedCertEntry。然后我更改了此代码以打印出“localhost”别名的条目,下面是我得到的,我猜问题是它是受信任的证书条目而不是私钥条目。我该如何改变呢?
可信证书条目: [0] 版本:3 序列号:752445443 IssuerDN: CN=localhost,OU=None,O=None L,C=None 开始日期:2014 年 5 月 26 日星期一 09:17:01 CDT 最终日期:2024 年 6 月 22 日星期六 09:17:01 CDT SubjectDN: CN=localhost,OU=None,O=None L,C=None 公钥:RSA公钥 模量:b75870cd29db79f8c015d440a27cc1e81c9dd829268efa2ce48efc596b33e9c60e1d1621e10aba34472b6f7890b16392db021c0358e665b1bf58a426fbc47e7c135da583e4cd6bb9c69668ee4ff1e05b1de8e7f5fb5604044a1087ac0181ba09f61ab5345d9be5d930889b7c328329d0d18cf53f4c5af6bff1f0e488744ea1fb 公共指数:10001 签名算法:SHA1WITHRSA 签名:83df0e761e9df2e61d5354ca58379975e0d97fcd 5201f8904b695d7bdbe08c5dfdfb8bcd6447657c 19740797a66314b2547a45985166c11ebadc16c6 c24b8e1d3c5de83ec1ac2c1c1092c3d06ed33408 4cf2811c5f9dba8a9d3ef0dcb8fef760e4d1d704 8fbb60eaa83eec23426fb9d8589e859a21a5ecce 951901f8e16ab6cd
【问题讨论】:
SSL_NULL_WITH_NULL_NULL
是从哪里来的?
"CN=" + domainName
不太对。 IETF 和 CA/B 论坛均不赞成在 CommonName
中放置 DNS 名称。您仍然可以使用它 - 只需放置一个友好的名称,例如“Internet Widgets”。将 DNS 名称放在 SubjectAltNames
中。
ssl空行来自代码中的这一行`System.out.println(" Cipher suite = " + ss.getCipherSuite());`
啊,好的。这看起来像是握手失败的结果;而不是在协商中使用的密码套件。您可以提供服务器的外部 URL 吗?您CERTAIN是否有一个 SSL/TLS 服务器正在监听 8888?
有一个服务器,我用 curl 试过,握手失败。这是在 Android 手机上运行的,但我会看看是否可以在某个时候将一些端口转发给它。
【参考方案1】:
s.setEnabledCipherSuites(s.getSupportedCipherSuites());
删除此行。
【讨论】:
如果我这样做,那么我会在s.accept()
行上得到 javax.net.ssl.SSLException: Could not find any key store entries to support the enabled cipher suites.
。
那么您的密钥生成有问题。无法立即看到它,但我会将它与通过 keytool 生成密钥时发生的情况进行比较。事实上,我不明白你为什么不使用keytool。没有人会信任您在此处生成的证书。
这是供安卓应用内部使用的,我有点希望每个用户都有自己的证书。我愿意使用生成的密钥工具,但我也遇到了麻烦,我认为我得到了错误的密钥库版本错误,即使我使用的是每个人都声称有效的充气城堡版本。
好吧,除非您由服务器信任的颁发者签署客户端证书,否则它将无法正常工作。
我有点迷路了。为什么需要客户证书?我只想拥有一个简单的浏览器,并且浏览器所属的同一个应用程序将拥有自己的 https 服务器。这里的整个问题只是关于 https 服务器。【参考方案2】:
握手失败通常意味着没有共享密码套件:
2895:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
SSL_NULL_WITH_NULL_NULL
看起来像 eNULL
和 aNULL
。 BouncyCastle 没有加载任何密码吗?
下面是我用于强化SSLSocketFactoryEx
的代码。它仅提供 TLS(getInstance("TLS")
仍将返回 SSLv3 套接字),并且仅提供经过批准的密码套件(由我批准)。仅提供经过批准的密码是不够的 - 该列表必须与可用的密码相交,否则会有例外。有一些备用密码套件可确保在旧服务器(如 Microsoft 提供的服务器)之间共享密码套件。
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.io.IOException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.net.Socket;
import java.net.InetAddress;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
class SSLSocketFactoryEx extends SSLSocketFactory
public SSLSocketFactoryEx() throws NoSuchAlgorithmException, KeyManagementException
initSSLSocketFactoryEx(null,null,null);
public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException
initSSLSocketFactoryEx(km, tm, random);
public SSLSocketFactoryEx(SSLContext ctx) throws NoSuchAlgorithmException, KeyManagementException
initSSLSocketFactoryEx(ctx);
public String[] getDefaultCipherSuites()
return m_ciphers;
public String[] getSupportedCipherSuites()
return m_ciphers;
public String[] getDefaultProtocols()
return m_protocols;
public String[] getSupportedProtocols()
return m_protocols;
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(s, host, port, autoClose);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(address, port, localAddress, localPort);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(host, port, localHost, localPort);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
public Socket createSocket(InetAddress host, int port) throws IOException
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(host, port);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
public Socket createSocket(String host, int port) throws IOException
SSLSocketFactory factory = m_ctx.getSocketFactory();
SSLSocket ss = (SSLSocket)factory.createSocket(host, port);
ss.setEnabledProtocols(m_protocols);
ss.setEnabledCipherSuites(m_ciphers);
return ss;
private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random)
throws NoSuchAlgorithmException, KeyManagementException
m_ctx = SSLContext.getInstance("TLS");
m_ctx.init(km, tm, random);
m_protocols = GetProtocolList();
m_ciphers = GetCipherList();
private void initSSLSocketFactoryEx(SSLContext ctx)
throws NoSuchAlgorithmException, KeyManagementException
m_ctx = ctx;
m_protocols = GetProtocolList();
m_ciphers = GetCipherList();
protected String[] GetProtocolList()
String[] preferredProtocols = "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" ;
String[] availableProtocols = null;
SSLSocket socket = null;
try
SSLSocketFactory factory = m_ctx.getSocketFactory();
socket = (SSLSocket)factory.createSocket();
availableProtocols = socket.getSupportedProtocols();
Arrays.sort(availableProtocols);
catch(Exception e)
return new String[] "TLSv1" ;
finally
if(socket != null)
socket.close();
List<String> aa = new ArrayList<String>();
for(int i = 0; i < preferredProtocols.length; i++)
int idx = Arrays.binarySearch(availableProtocols, preferredProtocols[i]);
if(idx >= 0)
aa.add(preferredProtocols[i]);
return aa.toArray(new String[0]);
protected String[] GetCipherList()
String[] preferredCiphers =
// *_CHACHA20_POLY1305 are 3x to 4x faster than existing cipher suites.
// http://googleonlinesecurity.blogspot.com/2014/04/speeding-up-and-strengthening-https.html
// Use them if available. Normative names can be found at (TLS spec depends on IPSec spec):
// http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-01
// http://tools.ietf.org/html/draft-mavrogiannopoulos-chacha-tls-02
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_SHA",
"TLS_ECDHE_RSA_WITH_CHACHA20_SHA",
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_RSA_WITH_CHACHA20_POLY1305",
"TLS_DHE_RSA_WITH_CHACHA20_SHA",
"TLS_RSA_WITH_CHACHA20_SHA",
// Done with bleeding edge, back to TLS v1.2 and below
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
// TLS v1.0 (with some SSLv3 interop)
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
"SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
// RSA key transport sucks, but they are needed as a fallback.
// For example, microsoft.com fails under all versions of TLS
// if they are not included. If only TLS 1.0 is available at
// the client, then google.com will fail too. TLS v1.3 is
// trying to deprecate them, so it will be interesteng to see
// what happens.
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA"
;
String[] availableCiphers = null;
try
SSLSocketFactory factory = m_ctx.getSocketFactory();
availableCiphers = factory.getSupportedCipherSuites();
Arrays.sort(availableCiphers);
catch(Exception e)
return new String[]
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
;
List<String> aa = new ArrayList<String>();
for(int i = 0; i < preferredCiphers.length; i++)
int idx = Arrays.binarySearch(availableCiphers, preferredCiphers[i]);
if(idx >= 0)
aa.add(preferredCiphers[i]);
aa.add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV");
return aa.toArray(new String[0]);
private SSLContext m_ctx;
private String[] m_ciphers;
private String[] m_protocols;
【讨论】:
Yuor 代码泄漏了 SSL 套接字,正如他已经在 cmets 中对另一个答案所说的那样,使用所有启用的协议仍然会导致问题,进一步限制它们不可能解决它。 感谢@EJP。我对其他问题一无所知(他需要在这里明确说明)。握手警报指示有 NO 共享密码套件。因此,他被提供代码以根据可用的内容设置密码套件的原因。以上是关于getCipherSuite() 返回 SSL_NULL_WITH_NULL_NULL的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 协程Flow 异步流 ① ( 以异步返回返回多个返回值 | 同步调用返回多个值的弊端 | 尝试在 sequence 中调用挂起函数返回多个返回值 | 协程中调用挂起函数返回集合 )