我們的Java 7應用程序需要監聽本地主機上的HTTPS請求。它必須接受https://localhost:8112
和https://127.0.0.1:8112
上的連接。爲什麼IE只接受127.0.0.1的自簽名本地主機證書,當Chrome接受它時?
要做到這一點,我們編程內置自動簽名採用X509v3證書,我們已經安裝在的Windows-ROOT密鑰存儲這個證書,如下所示:
KeyStore.TrustedCertificateEntry trustedCert = ...;
KeyStore ks = KeyStore.getInstance("Windows-ROOT");
ks.load(null, null);
ks.setEntry("xxxx_localhost", trustedCert, null);
這使得Chrome所接受證書36在兩種情況下(本地主機和127.0.0.1),但IE 11訪問127.0.0.1
當同時訪問時localhost
確實不能識別該證書是有效的:
爲什麼?該證書是建立如下,BasicConstraints,執行extendedKeyUsage和主題替代擴展,通過使用sun.security.x509
包:
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更改爲CN=127.0.0.1
而沒有成功(相同的行爲)。
「CN = localhost」 - 可能不是。這被IETF和更重要的[CA /瀏覽器論壇](https://cabforum.org/baseline-requirements-documents/)所棄用。參見第9.2節。CA/B基線要求2:*「棄用(不鼓勵,但不禁止)」*。使CN像「XXX IT」一樣友好(個人而言,我不使用合法名稱)。由於SAN包含'localhost'和'127.0.0.1',因此SAN看起來不錯。請參閱CA/B基準要求的第9.2.1節。 – jww
我不認爲應將最終實體證書(服務器或用戶證書)置於* Trusted Root *存儲(其實際稱爲*受信任根證書頒發機構*存儲)。聽起來很奇怪,他們可以用來簽署其他證書。相反,他們進入*個人 - >證書*)。 – jww
您確定您的標題中沒有交換IE和Chrome嗎?這看起來像一個Chrome屏幕截圖給我(拒絕證書的那個)。可以肯定的是,您是否檢查過您的證書是否具有* iPAddress *類型的127.0.0.1的SAN條目? (你可以導出它,並用'openssl x509 -text ...'查看。) – Bruno