我们的Java 7应用程序需要在localhost上侦听HTTPS请求。它必须接受https://localhost:8112
和https://127.0.0.1:8112
上的连接。
为此,我们以编程方式构建了自动签名的X509v3证书,我们已在 Windows-ROOT 密钥库中安装此证书,如下所示:
KeyStore.TrustedCertificateEntry trustedCert = ...;
KeyStore ks = KeyStore.getInstance("Windows-ROOT");
ks.load(null, null);
ks.setEntry("xxxx_localhost", trustedCert, null);
这使得Chrome 36在两种情况下都接受了证书(localhost和127.0.0.1),但在访问127.0.0.1
时,IE 11在访问localhost
时无法将证书识别为有效:
为什么呢?通过sun.security.x509
包使用 BasicConstraints , ExtendedKeyUsage 和 SubjectAlternativeName 扩展,构建如下证书:
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair kp = generator.generateKeyPair();
X509Certificate cert = generateCertificate("CN=localhost, OU=XXX, O=XXX", kp,
1825, "SHA256withRSA", "ip:127.0.0.1,dns:localhost,uri:https://127.0.0.1:8112");
/**
* Create a self-signed X.509 Certificate.
* @param dn the X.509 Distinguished Name
* @param pair the KeyPair
* @param days how many days from now the Certificate is valid for
* @param algorithm the signing algorithm, eg "SHA256withRSA"
* @param san SubjectAlternativeName extension (optional)
*/
private static X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm, String san)
throws GeneralSecurityException, IOException {
PrivateKey privkey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
Date to = new Date(from.getTime() + days * 86400000l);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
CertificateExtensions ext = new CertificateExtensions();
// Critical: Not CA, max path len 0
ext.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(true, false, 0));
// Critical: only allow TLS ("serverAuth" = 1.3.6.1.5.5.7.3.1)
ext.set(ExtendedKeyUsageExtension.NAME, new ExtendedKeyUsageExtension(true,
new Vector<ObjectIdentifier>(Arrays.asList(new ObjectIdentifier("1.3.6.1.5.5.7.3.1")))));
if (san != null) {
int colonpos;
String[] ps = san.split(",");
GeneralNames gnames = new GeneralNames();
for(String item: ps) {
colonpos = item.indexOf(':');
if (colonpos < 0) {
throw new IllegalArgumentException("Illegal item " + item + " in " + san);
}
String t = item.substring(0, colonpos);
String v = item.substring(colonpos+1);
gnames.add(createGeneralName(t, v));
}
// Non critical
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(false, gnames));
}
info.set(X509CertInfo.EXTENSIONS, ext);
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
// Update the algorithm, and resign.
algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
return cert;
}
通过关于CAPI2诊断的this tutorial,我发现了IE报告的错误:
<CertVerifyCertificateChainPolicy>
<Policy type="CERT_CHAIN_POLICY_SSL" constant="4" />
<Certificate fileRef="XXX.cer" subjectName="127.0.0.1" />
<CertificateChain chainRef="{XXX}" />
<Flags value="0" />
<SSLAdditionalPolicyInfo authType="server" serverName="127.0.0.1">
<IgnoreFlags value="0" />
</SSLAdditionalPolicyInfo>
<Status chainIndex="0" elementIndex="0" />
<EventAuxInfo ProcessName="iexplore.exe" />
<CorrelationAuxInfo TaskId="{XXX}" SeqNumber="4" />
<Result value="800B010F">The certificate's CN name does not match the passed value.</Result>
</CertVerifyCertificateChainPolicy>
CertVerifyCertificateChainPolicy和CERT_CHAIN_POLICY_STATUS上的文档对我帮助不大:看起来IE期望CN等于服务器名称,但我尝试将CN更改为{{1没有成功(相同的行为)。
答案 0 :(得分:6)
IE不支持主题备用名称(SAN)中的IP地址值,仅支持DNS条目。
根据微软的说法,这是一个known limitation无法解决的问题:
我们不支持使用Subject Alternative名称中的IP选项来匹配服务器名称。您可以通过将IP地址添加为DNS名称选择的字符串来解决此问题。目前我们还没有计划解决这个问题。
所以处理它的正确方法是添加一个包含IP地址的DNS条目:
"dns:127.0.0.1"
不幸的是,由于Java bug 8016345,使用 keytool 或以编程方式使用sun.security.x509
类是不可能的。
然而,只需复制最新版本的DNSName.java并删除此项检查即可自行修复此错误:
//DNSName components must begin with a letter A-Z or a-z
if (alpha.indexOf(name.charAt(startIndex)) < 0)
throw new IOException("DNSName components must begin with a letter");
答案 1 :(得分:-1)
为什么不在用户使用127.0.0.1将其发送到localhost时发出HTTP 301重定向?