2009-11-07 1 views
4

Рассмотрим следующий пример кода, который использует TrustManager для входа используется ли исходящее соединение действительный сертификат (но принять соединение во всех случаях):Как проверить, является ли сертификат сертификатом EV с Java?

import java.security.*; 
import java.security.cert.*; 
import javax.net.ssl.*; 

public class CertChecker implements X509TrustManager { 

    private final X509TrustManager defaultTM; 

    public CertChecker() throws GeneralSecurityException { 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init((KeyStore)null); 
     defaultTM = (X509TrustManager) tmf.getTrustManagers()[0]; 
    } 

    public void checkServerTrusted(X509Certificate[] certs, String authType) { 
     if (defaultTM != null) { 
      try { 
       defaultTM.checkServerTrusted(certs, authType); 
       System.out.println("Certificate valid"); 
      } catch (CertificateException ex) { 
       System.out.println("Certificate invalid: " + ex.getMessage()); 
      } 
     } 
    } 

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} 
    public X509Certificate[] getAcceptedIssuers() { return null;} 

    public static void main(String[] args) throws Exception { 
     SSLContext sc = SSLContext.getInstance("SSL"); 
     sc.init(null, new TrustManager[] {new CertChecker()}, new SecureRandom()); 
     SSLSocketFactory ssf = (SSLSocketFactory) sc.getSocketFactory(); 
     ((SSLSocket)ssf.createSocket(args[0], 443)).startHandshake(); 
    } 
} 

Что я должен сделать внутри метода checkClientTrusted проверить если этот сертификат является расширенным сертификатом проверки (зеленая адресная строка в современных браузерах) или обычная (желтая адресная строка)?

редактировать:

Я пытаюсь получить CertPathValidator работы, но почему-то я только получаю исключение о сертификате не является сертификат CA ... Есть идеи?

edit2: Использование PKIXParameters вместо PKIXBuilderParameters

private boolean isEVCertificate(X509Certificate[] certs, String authType) { 
    try { 
     CertPath cp = new X509CertPath(Arrays.asList(certs)); 
     KeyStore ks = KeyStore.getInstance("JKS"); 
     ks.load(new FileInputStream(new File(System.getProperty("java.home"), "lib/security/cacerts")), null); 
     PKIXParameters cpp = new PKIXParameters(ks); 
     cpp.setRevocationEnabled(false); 
     CertPathValidator cpv = CertPathValidator.getInstance("PKIX");   
     PKIXCertPathValidatorResult res = (PKIXCertPathValidatorResult) cpv.validate(cp, cpp); 
     System.out.println(res.getTrustAnchor().getCAName()); 
     System.out.println(res.getPolicyTree().getValidPolicy()); 
     System.out.println(cp); 
     return false; 
    } catch (Exception ex) { 
     ex.printStackTrace(); 
     return false; 
    } 
} 

Я тестирую против реальных сертификатов EV. Код теперь работает с www.paypal.com (в том смысле, что он не генерирует исключение), но не работает с banking.dkb.de. :-(

Но даже с Paypal.com доверие якорь getCAName возвращает нуль, так как я могу знать, против которой CA было подтверждено, так что я могу смотреть на правильную политику EV?

+0

Ваш код валидатора несколько отличается тем, что вы используете 'PKIXBuilderParameters' для проверки; обычно я бы использовал только «PKIXParameters» (цель уже известна). Кроме того, используется ли цепочка сертификатов, выданных через «настоящий» ЦС, или тестовую, которую вы подписали? Если последнее, я буду обеспокоен различными расширениями. Они могут быть очень сложными, чтобы получить право, чтобы они удовлетворяли валидатору PKIX. – erickson

+0

обновил мой вопрос соответственно ... – mihi

+0

Хорошо, посмотрите мой пример. Он работает для 'https: // banking.dkb.de/dkb /'. – erickson

ответ

1

я, наконец, получил его на работу ... бегущий минимальный пример, который показывает, вся логика и проверяет ниже.И да, это работает для banking.dkb.de :-)

Спасибо всем, кто мне помог. Любые комментарии по поводу вопиющих дыр в безопасности или что-нибудь еще (кроме стиля кода или отсутствующей обработки ошибок; я пытался трудно уплотнить свой код к абсолютному минимуму работоспособного кода) приветствуется, так что не стесняйтесь комментировать :)

import java.io.*; 
import java.security.*; 
import java.security.cert.*; 
import java.util.*; 

import javax.net.ssl.*; 
import javax.security.auth.x500.X500Principal; 

public class CertChecker implements X509TrustManager { 

    private final X509TrustManager defaultTM; 

    public CertChecker() throws GeneralSecurityException { 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init((KeyStore)null); 
     defaultTM = (X509TrustManager) tmf.getTrustManagers()[0]; 
    } 

    public void checkServerTrusted(X509Certificate[] certs, String authType) { 
     if (defaultTM != null) { 
      try { 
       defaultTM.checkServerTrusted(certs, authType); 
       if (isEVCertificate(certs)) 
        System.out.println("EV Certificate: "+ certs[0].getSubjectX500Principal().getName() + " issued by " + certs[0].getIssuerX500Principal().getName());      
       System.out.println("Certificate valid"); 
      } catch (CertificateException ex) { 
       System.out.println("Certificate invalid: " + ex.getMessage()); 
      } 
     } 
    } 

    private boolean isEVCertificate(X509Certificate[] certs) { 
     try { 
      // load keystore with trusted CA certificates 
      KeyStore cacerts = KeyStore.getInstance("JKS"); 
      cacerts.load(new FileInputStream(new File(System.getProperty("java.home"), "lib/security/cacerts")), null); 

      // build a cert selector that selects the first certificate of the certificate chain 
      // TODO we should verify this against the hostname... 
      X509CertSelector targetConstraints = new X509CertSelector(); 
      targetConstraints.setSubject(certs[0].getSubjectX500Principal()); 

      // build a cert path from our selected cert to a CA cert 
      PKIXBuilderParameters params = new PKIXBuilderParameters(cacerts, targetConstraints);   
      params.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(certs)))); 
      params.setRevocationEnabled(false); 
      CertPath cp = CertPathBuilder.getInstance("PKIX").build(params).getCertPath(); 

      // validate the cert path 
      PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) CertPathValidator.getInstance("PKIX").validate(cp, params); 
      return isEV(result); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
      return false; 
     } 
    } 

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} 
    public X509Certificate[] getAcceptedIssuers() { return null;} 

    public static void main(String[] args) throws Exception { 
     SSLContext sc = SSLContext.getInstance("SSL"); 
     sc.init(null, new TrustManager[] {new CertChecker()}, new SecureRandom()); 
     SSLSocketFactory ssf = (SSLSocketFactory) sc.getSocketFactory(); 
     ((SSLSocket)ssf.createSocket(args[0], 443)).startHandshake(); 
    } 

    private static final Map<X500Principal, String> policies = new HashMap<X500Principal, String>(); 

    static { 
     // It would make sense to populate this map from Properties loaded through 
     // Class.getResourceAsStream(). 
     policies.put(
       new X500Principal("OU=Class 3 Public Primary Certification Authority,O=VeriSign\\, Inc.,C=US"), 
       "2.16.840.1.113733.1.7.23.6" 
     ); 
     // TODO add more certificates here 
    } 

    // based on http://stackoverflow.com/questions/1694466/1694720#1694720 
    static boolean isEV(PKIXCertPathValidatorResult result) 
    { 
     // Determine the policy to look for. 
     X500Principal root = result.getTrustAnchor().getTrustedCert().getSubjectX500Principal(); 
     System.out.println("[Debug] Found root DN: "+root.getName()); 
     String policy = policies.get(root); 
     if (policy != null) 
      System.out.println("[Debug] EV Policy should be: "+policy); 

     // Traverse the tree, looking at its "leaves" to see if the end-entity 
     // certificate was issued under the corresponding EV policy. 
     PolicyNode tree = result.getPolicyTree(); 
     if (tree == null) 
      return false; 
     Deque<PolicyNode> stack = new ArrayDeque<PolicyNode>(); 
     stack.push(tree); 
     while (!stack.isEmpty()) { 
      PolicyNode current = stack.pop(); 
      Iterator<? extends PolicyNode> children = current.getChildren(); 
      int leaf = stack.size(); 
      while (children.hasNext()) 
       stack.push(children.next()); 
      if (stack.size() == leaf) { 
       System.out.println("[Debug] Found policy: " + current.getValidPolicy()); 
       // If the stack didn't grow, there were no "children". I.e., the 
       // current node is a "leaf" node of the policy tree. 
       if (current.getValidPolicy().equals(policy)) 
        return true; 
      } 
     } 
     // The certificate wasn't issued under the authority's EV policy. 
     return false; 
    } 
} 
3

Во-первых, вы» Требуется таблица имен эмитентов и их соответствующих идентификаторов политики EV.

Когда ЦС выдает сертификат, они могут отметить политику, по которой они выдали сертификат. Идентификатор этой политики, назначенный эмитентом, поэтому вам нужен список эмитентов и их политика EV.

Тогда вам нужно будет получить политику от сертификат сервера. Refer to RFC 5280, § 4.1.2.4, чтобы узнать больше о политике в целом и о том, как они работают.

Вам необходимо проверить цепочку сертификатов для получения PKIXCertPathValidatorResult.. Часть результата: policy tree.. Вы можете перемещаться по дереву политик, чтобы определить, включает ли он политику EV для эмитента целевого сертификата.


Вот подробный пример проверки результата пути сертификата.

private static final Map<X500Principal, String> policies = new HashMap<X500Principal, String>(); 

static { 
    /* 
    * It would make sense to populate this map from Properties loaded through 
    * Class.getResourceAsStream(). 
    */ 
    policies.put(
    new X500Principal("OU=Class 3 Public Primary Certification Authority,O=VeriSign\\, Inc.,C=US"), 
    "2.16.840.1.113733.1.7.23.6" 
); 
    // ... 
} 

static boolean isEV(PKIXCertPathValidatorResult result) 
{ 
    /* Determine the policy to look for. */ 
    X500Principal root = result.getTrustAnchor().getTrustedCert().getSubjectX500Principal(); 
    String policy = policies.get(root); 
    if (policy == null) 
    /* The EV policy for this issuer is unknown (or there is none). */ 
    return false; 
    /* Traverse the tree, looking at its "leaves" to see if the end-entity 
    * certificate was issued under the corresponding EV policy. */ 
    PolicyNode tree = result.getPolicyTree(); 
    Deque<PolicyNode> stack = new ArrayDeque<PolicyNode>(); 
    stack.push(tree); 
    while (!stack.isEmpty()) { 
    PolicyNode current = stack.pop(); 
    Iterator<? extends PolicyNode> children = current.getChildren(); 
    int leaf = stack.size(); 
    while (children.hasNext()) 
     stack.push(children.next()); 
    if (stack.size() == leaf) { 
     /* If the stack didn't grow, there were no "children". I.e., the 
     * current node is a "leaf" node of the policy tree. */ 
     if (current.getValidPolicy().equals(policy)) 
     return true; 
    } 
    } 
    /* The certificate wasn't issued under the authority's EV policy. */ 
    return false; 
} 
+0

Нет библиотеки для этого !? – jrockway

+0

Не то, чтобы я знал. Это не очень распространенное требование; Сертификаты EV наиболее полезны в браузерах, и их не так много. – erickson

+0

Я не понимаю, что 'CertPathValidator' работает. Мой код выше. Пожалуйста, помогите мне, как его инициализировать, чтобы он работал и не генерировал исключений ... – mihi

2

EDIT: добавлен дополнительный код.

Если вы используете реализацию Sun, X509, вы можете сделать что-то вроде этого,

CertificatePoliciesExtension ext = ((X509CertImpl)cert).getCertificatePoliciesExtension(); 
    List<PolicyInformation> policies = (List<PolicyInformation>)ext.get(CertificatePoliciesExtension.POLICIES); 
    boolean evCert = false; 
    for (PolicyInformation info : policies) { 
     CertificatePolicyId id = info.getPolicyIdentifier(); 
     if (isEVPolicy(id)) { 
     evCert = true; 
     break;     
     }    
    } 

    ...... 

    public static ObjectIdentifier[] EV_POLICIES; 

    static { 
     try { 
      EV_POLICIES = new ObjectIdentifier[] { 
       new ObjectIdentifier("2.16.840.1.113733.1.7.23.6"), // Verisign 
       new ObjectIdentifier("1.3.6.1.4.1.14370.1.6"), // Geo-Trust of Verisign 
       new ObjectIdentifier("2.16.840.1.113733.1.7.48.1") // Thawte 
      }; 
     } catch (IOException e) { 
     throw new IllegalStateException("Invalid OIDs"); 
     } 
    } 

    private boolean isEVPolicy(CertificatePolicyId id) { 
    for (ObjectIdentifier oid : EV_POLICIES) { 
     if (oid.equals((Object)id.getIdentifier())) 
      return true; 
    } 
    return false; 
} 

Мы разрешаем EV сертификат от 3 УЦ. Вы можете добавить больше EV OID в этот массив. Вы можете получить полный список Идентификаторов из

http://hg.mozilla.org/mozilla-central/file/05ab1cbc361f/security/manager/ssl/src/nsIdentityChecking.cpp

+0

Нет, вы не можете. Это даст вам все расширения политики, а не только те, которые указывают, что сертификат удовлетворяет требованиям «расширенной проверки». – jarnbjo

+0

Это всего лишь часть кода, который проверяет EV. Однако этого достаточно, если вы просто хотите узнать, имеет ли сертификат EV. Расширение Cert Policies добавляется для сертификата EV, и до сих пор CA не используется для других целей. Покажите мне реальный сертификат с другими политиками. –

+2

Мой банк, например. с использованием обычного сертификата класса 3 Verisign с политикой 2.16.840.1.113733.1.7.23.3 (не EV). – jarnbjo

 Смежные вопросы

  • Нет связанных вопросов^_^