title: Why IE rejects a self-signed localhost certificate for 127.0.0.1 only, when Chrome accepts it?
body: <p>Our Java 7 application needs to listen for HTTPS requests on localhost. It must accept connections on <code>https://localhost:8112</code> and <code>https://127.0.0.1:8112</code>.</p>
  
  <p>To do so we have programmatically built an auto-signed X509v3 certificate, and we have installed this certificate in the <em>Windows-ROOT</em> keystore, as follows:</p>
  
  <pre><code>KeyStore.TrustedCertificateEntry trustedCert = ...;
  KeyStore ks = KeyStore.getInstance("Windows-ROOT");
  ks.load(null, null);
  ks.setEntry("xxxx_localhost", trustedCert, null);
  </code></pre>
  
  <p>This makes the certificate accepted by Chrome 36 in both cases (localhost and 127.0.0.1), but IE 11 does not recognize the certificate as valid when accessing <code>127.0.0.1</code> while it does when accessing <code>localhost</code>:</p>
  
  <p><img src="https://i.stack.imgur.com/NjLne.png" alt="Test ok with Chrome (top), KO with IE (bottom)"></p>
  
  <p>Why? The certificate is built as follows, with <em>BasicConstraints</em>, <em>ExtendedKeyUsage</em> and <em>SubjectAlternativeName</em> extensions, by using the <code>sun.security.x509</code> package:</p>
  
  <pre><code>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&lt;ObjectIdentifier&gt;(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 &lt; 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;
  }
  </code></pre>
  
  <p>By following <a href="http://technet.microsoft.com/en-US/library/cc749296(v=ws.10).aspx#BKMK_CAPI2Vista" rel="nofollow noreferrer">this tutorial</a> on CAPI2 Diagnostics, I have found the error reported by IE:</p>
  
  <pre><code>&lt;CertVerifyCertificateChainPolicy&gt;
      &lt;Policy type="CERT_CHAIN_POLICY_SSL" constant="4" /&gt; 
      &lt;Certificate fileRef="XXX.cer" subjectName="127.0.0.1" /&gt; 
      &lt;CertificateChain chainRef="{XXX}" /&gt; 
      &lt;Flags value="0" /&gt; 
      &lt;SSLAdditionalPolicyInfo authType="server" serverName="127.0.0.1"&gt;
          &lt;IgnoreFlags value="0" /&gt; 
      &lt;/SSLAdditionalPolicyInfo&gt;
      &lt;Status chainIndex="0" elementIndex="0" /&gt; 
      &lt;EventAuxInfo ProcessName="iexplore.exe" /&gt; 
      &lt;CorrelationAuxInfo TaskId="{XXX}" SeqNumber="4" /&gt; 
      &lt;Result value="800B010F"&gt;The certificate's CN name does not match the passed value.&lt;/Result&gt; 
  &lt;/CertVerifyCertificateChainPolicy&gt;
  </code></pre>
  
  <p>The documentation on <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa377163(v=vs.85).aspx" rel="nofollow noreferrer">CertVerifyCertificateChainPolicy</a> and <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa377188(v=vs.85).aspx" rel="nofollow noreferrer">CERT_CHAIN_POLICY_STATUS</a> doesn't help me much: it looks like IE expects a CN equal to server name, but I have tried to change my CN to <code>CN=127.0.0.1</code> without success (same behaviour).</p>