服务器的瞬时 Diffie-Hellman 公共密钥过弱

sswhsz 2016-07-11 01:03:12
最近了解了一下证书的构成、DER编码结构、SSL协议的握手过程,尝试代码写了一个动态生成证书的EchoServer。

不过在IE浏览器下把证书加到信任列表的话可以正常访问,
但在Chrome下报:服务器的瞬时 Diffie-Hellman 公共密钥过弱,无法访问。
网上搜了一下相关解释,都没有能从SSL的握手协议来做个解释,尝试从代码中去掉了几个CipherSuite
好像也没效果。。

请教一下。

示例代码:

<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.54</version>
</dependency>



import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;

public class SSLServerDemo implements Runnable {

private Socket socket;

private SSLServerDemo(Socket socket) {
this.socket = socket;
}

public static void main(String[] args) throws Exception {
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SUNX509"/*NEWSUNX509*/);
KeyStore serverStore = CertMakeDemo.createKeyStore("DemoServer", "123456");
kmf.init(serverStore, "123456".toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SUNX509"/*PKIX*/);
KeyStore clientStore = KeyStore.getInstance("PKCS12"/*JKS, WINDOWS-ROOT, JCEKS等*/);
clientStore.load(null, null);
tmf.init(clientStore);

SSLContext context = SSLContext.getInstance("SSL"/*TLS, SSLV3, DEFAULT, "TLSV1"*/);
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLParameters parameters = context.getDefaultSSLParameters();
List<String> cipherSuites = new ArrayList<String>(Arrays.asList(parameters.getCipherSuites()));
cipherSuites.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
parameters.setCipherSuites(cipherSuites.toArray(new String[0]));
System.out.println(Arrays.asList(parameters.getCipherSuites()));

SSLServerSocketFactory factory = context.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(443);
//serverSocket.setNeedClientAuth(true);浏览器会提示选择个人私钥证书,tmf需要能够认证客户端证书
System.out.println("server started. try https://localhost");

ExecutorService executor = Executors.newCachedThreadPool();
for (Socket socket; (socket = serverSocket.accept()) != null;) {
executor.submit(new SSLServerDemo(socket));
}
}

public void run() {
BufferedReader in = null;
BufferedWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));

out.write("HTTP/1.1 200 OK\r\n");
out.write("Content-Type: text/html;charset=UTF-8\r\n");
out.write("\r\n");
for (String line; (line = in.readLine()) != null;) {
out.write(line + "<br>\r\n");
out.flush();
System.out.println(line);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (!socket.isClosed() && socket.isConnected()) {
closeQuietly(in);
closeQuietly(out);
}
}
}

private void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}



import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class CertMakeDemo {

public static void main(String[] args) throws Exception {
X500Name subject = new X500Name("CN=root, O=root, OU=root");
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(1024);
KeyPair pair = gen.generateKeyPair();
X509Certificate certificate = signerSelf(subject, pair);
System.out.println("证书:" + certificate);

KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
pkcs12.load(null, null);
pkcs12.setKeyEntry("root", pair.getPrivate(), "123456".toCharArray(), new Certificate[] { certificate });
for (Enumeration<String> e = pkcs12.aliases(); e.hasMoreElements();) {
String alias = e.nextElement();
System.out.println(pkcs12.getCertificateChain(alias));
System.out.println(pkcs12.getKey(alias, "123456".toCharArray()));
}
OutputStream out = new FileOutputStream("f:/temp/root.pfx");
pkcs12.store(out, "123456".toCharArray());
out.close();

//root为张三签发证书
X500Name zsSubject = new X500Name("CN=张三, O=张三, OU=张三");
gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(1024);
KeyPair zsKeypair = gen.generateKeyPair();
X509Certificate zsCertificate = signer(zsSubject, zsKeypair.getPublic(), certificate, pair.getPrivate());
System.out.println("张三证书:" + zsCertificate);
out = new FileOutputStream("f:/temp/zhangsan.cer");
out.write(zsCertificate.getEncoded());
out.close();
}

public static KeyStore createKeyStore(String subjectName, String pwd) throws Exception {
X500Name subject = new X500Name("CN=" + subjectName);
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(1024);
KeyPair pair = gen.generateKeyPair();
X509Certificate certificate = signerSelf(subject, pair);

KeyStore store = KeyStore.getInstance("PKCS12");
store.load(null, null);
store.setKeyEntry("root", pair.getPrivate(), pwd.toCharArray(), new Certificate[] { certificate });
return store;
}

public static X509Certificate signer(X500Name subject, PublicKey subjectPublicKey,//
X509Certificate issuerCert, PrivateKey issuerPrivateKey) throws Exception {

X500Name issuer = X500Name.getInstance(issuerCert.getSubjectX500Principal().getEncoded());
String signatureAlgorithm = issuerCert.getSigAlgName();
return signer(subject, subjectPublicKey, issuer, issuerPrivateKey, signatureAlgorithm);
}

public static X509Certificate signerSelf(X500Name subject, KeyPair pair) throws Exception {
String signatureAlgorithm = "SHA1With" + pair.getPrivate().getAlgorithm();
return signer(subject, pair.getPublic(), subject, pair.getPrivate(), signatureAlgorithm);
}

public static X509Certificate signer(X500Name subject, PublicKey subjectPublicKey,//
X500Name issuer, PrivateKey issuerPrivateKey, String signatureAlgorithm) throws Exception {

BigInteger sn = new BigInteger(new SimpleDateFormat("yyyyMMdd").format(new Date()));
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + 365L * 24 * 60 * 60 * 1000);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(subjectPublicKey.getEncoded());
ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).build(issuerPrivateKey);

X509v3CertificateBuilder builder = new X509v3CertificateBuilder(//
issuer, sn, notBefore, notAfter, subject, publicKeyInfo);
builder.addExtension(createSubjectAlternativeName());
byte[] certBytes = builder.build(signer).getEncoded();

X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X509")//
.generateCertificate(new ByteArrayInputStream(certBytes));

return certificate;
}

/** 使用者备用名称:绑定证书和域名,解决此证书与网站不符的问题 */
private static Extension createSubjectAlternativeName() throws Exception {
DERTaggedObject tag1 = new DERTaggedObject(false, 2, new DEROctetString("localhost".getBytes()));
DERTaggedObject tag2 = new DERTaggedObject(false, 2, new DEROctetString("127.0.0.1".getBytes()));
DERSequence seq = new DERSequence(new ASN1Encodable[] { tag1, tag2 });
Extension extension = new Extension(Extension.subjectAlternativeName, false, new DEROctetString(seq));
return extension;
}
}
...全文
720 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
sswhsz 2016-07-11
  • 打赏
  • 举报
回复
缺省的CipherSuite是下面这些:

[
	SSL_RSA_WITH_RC4_128_MD5,
	SSL_RSA_WITH_RC4_128_SHA,
	TLS_RSA_WITH_AES_128_CBC_SHA,
	TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
	TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
	SSL_RSA_WITH_3DES_EDE_CBC_SHA,
	SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
	SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
	SSL_RSA_WITH_DES_CBC_SHA,
	SSL_DHE_RSA_WITH_DES_CBC_SHA,
	SSL_DHE_DSS_WITH_DES_CBC_SHA,
	SSL_RSA_EXPORT_WITH_RC4_40_MD5,
	SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
	SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
	SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
	TLS_EMPTY_RENEGOTIATION_INFO_SCSV
]
sswhsz 2016-07-11
  • 打赏
  • 举报
回复
再附一下SSL的握手过程:(摘自:http://blog.csdn.net/wzsy/article/details/38332819) 可能重点是第二步的“* 握手期间将使用的密钥交换算法 (Deffie-Hellman算法,基于RSA的密钥交换算法等)”

1. 客户端发送一个Hello消息给服务器,该消息包含以下参数:
* 客户端支持的SSL的版本列表
* 客户端所支持的加密算法列表
* 随机数 ClientHello.random
2.1 服务器端回应一个Hello消息给客户端,该消息包含以下内容:
* 握手期间使用的SSL的版本
* 握手期间将使用的密钥交换算法 (Deffie-Hellman算法,基于RSA的密钥交换算法等)
* 握手期间将使用的加密算法 (DES,RC4,RC2,DES3等)
* 握手最后使用的MAC算法 (MD5或SHA-1)
* 随机数 ServerHello.random
2.2 服务端发送自己的X.509证书或证书链 (除了匿名DH交换算法都需要)
2.3 服务器发送server_key_exchange请求
- 可选,仅三种情况需要:
a. 服务器无证书
b. 服务器有证书,但是仅能当作签名 (RSA算法中步骤3中发出去的证书里包含的是DSS或DSA公钥,不能拿来加密)
c. 使用fortezza/DMS密码交换
2.4 服务器发送certificate_request请求,要求客户端提供它的证书 (可选)
2.5 服务器发送server_done,等待应答
3.1 客户端检查服务器证书,做校验
3.2 如果服务端请求证书,客户端发送自己的证书过去
3.3 客户端发送client_key_exchange请求
- 若采用RSA,会产生一个新的随机数作为premaster secret,将该随机数通过证书中公钥或者server_key_exchange消息内的临时RSA密钥加密发过去
客户端根据premaster secret, ClientHello.random, ServerHello.random三个值算出master secret作为对称密钥
服务器端收到premaster secret以后,也根据这三个值算出master secret
- 若采用DH,证书中已包含了DH算法需要的两个整数p,g,直接通过算法根据已经交换的两个随机数可以算出premaster secret,服务器端也可以算出同样的premaster secret
3.4 如果客户端发送了自己的证书,那么它还会发一个certificate_verify消息,对第一条消息以来所有的握手消息的HMAC值用master secret进行签名。
3.5 客户端发送一个change_cipher_spec消息,并将协商好的加密信息拷贝到当前连接的状态中,保证以后所有的消息都用新的密码规范加解密和认证
3.5 客户端发送一个finished消息
4. 服务端同样发送change_ciper_spec消息和finish消息
至此,握手完成,客户端和服务器可以进行数据通信了。
握手时用非对称加密算法,保证安全;通信时用对称算法,保证效率。
sswhsz 2016-07-11
  • 打赏
  • 举报
回复
对了, 忘记说了, 把服务器证书加入受信任的根证书。 IE访问正常,chrome还是不行。(我的是IE8,点地址栏的证书,查看详情,安装到受信任的根证书,再刷新页面---因为证书是动态生成的,服务器不要重启就好)

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧