/*
 * ====================================================================
 * Copyright (c) 2004-2012 TMate Software Ltd.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://svnkit.com/license.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.core.internal.util;

import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;


/**
 * @version 1.0
 * @author  TMate Software Ltd.
 */
public class SVNSSLUtil {
    
    public static StringBuffer getServerCertificatePrompt(X509Certificate cert, String realm, String hostName) {
        int failures = getServerCertificateFailures(cert, hostName);
        StringBuffer prompt = new StringBuffer();
        prompt.append("Error validating server certificate for '");
        prompt.append(realm);
        prompt.append("':\n");
        if ((failures & 8) != 0) {
            prompt.append(" - The certificate is not issued by a trusted authority. Use the\n" +
                          "   fingerprint to validate the certificate manually!\n");
        }
        if ((failures & 4) != 0) {
            prompt.append(" - The certificate hostname does not match.\n");
        }
        if ((failures & 2) != 0) {
            prompt.append(" - The certificate has expired.\n");
        }
        if ((failures & 1) != 0) {
            prompt.append(" - The certificate is not yet valid.\n");
        }
        getServerCertificateInfo(cert, prompt);
        return prompt;

    }
    
    private static String getFingerprint(X509Certificate cert) {
        try  {
           return getFingerprint(cert.getEncoded(), "SHA1");
        } catch (Exception e)  {
        } 
        return null;
    }

    public static String getFingerprint(byte[] key, String digestAlgorithm) {
        StringBuffer s = new StringBuffer();
        try  {
           MessageDigest md = MessageDigest.getInstance(digestAlgorithm != null ? digestAlgorithm : "SHA1");
           md.update(key);
           byte[] digest = md.digest();
           for (int i= 0; i < digest.length; i++)  {
              if (i != 0) {
                  s.append(':');
              }
              int b = digest[i] & 0xFF;
              String hex = Integer.toHexString(b);
              if (hex.length() == 1) {
                  s.append('0');
              }
              s.append(hex.toLowerCase());
           }
        } catch (Exception e)  {
        } 
        return s.toString();
    }

  private static void getServerCertificateInfo(X509Certificate cert, StringBuffer info) {
      info.append("Certificate information:");
      info.append('\n');
      info.append(" - Subject: ");
      info.append(cert.getSubjectDN().getName());
      info.append('\n');
      info.append(" - Valid: ");
      info.append("from " + cert.getNotBefore() + " until " + cert.getNotAfter());
      info.append('\n');
      info.append(" - Issuer: ");
      info.append(cert.getIssuerDN().getName());
      info.append('\n');
      info.append(" - Fingerprint: ");
      info.append(getFingerprint(cert));
  }

  public static int getServerCertificateFailures(X509Certificate cert, String realHostName) {
      int mask = 8;
      Date time = new Date(System.currentTimeMillis());
      if (time.before(cert.getNotBefore())) {
          mask |= 1;
      }
      if (time.after(cert.getNotAfter())) {
          mask |= 2;
      }
      String certHostName = cert.getSubjectDN().getName();
      int index = certHostName.indexOf("CN=");
      if (index >= 0) {
          index += 3;
          certHostName = certHostName.substring(index);
          if (certHostName.indexOf(' ') >= 0) {
              certHostName = certHostName.substring(0, certHostName.indexOf(' '));
          }
          if (certHostName.indexOf(',') >= 0) {
              certHostName = certHostName.substring(0, certHostName.indexOf(','));
          }
      }
      if (!realHostName.equals(certHostName)) {
          try {
              Collection altNames = cert.getSubjectAlternativeNames();
              if(altNames != null) {
                  for (Iterator names = altNames.iterator(); names.hasNext();) {
                      Object nameList = names.next();
                      if (nameList instanceof Collection && ((Collection) nameList).size() >= 2) {
                          Object[] name = ((Collection) nameList).toArray();
                          Object type = name[0];
                          Object host = name[1];
                          if (type instanceof Integer && host instanceof String) {
                              if (((Integer) type).intValue() == 2 && host.equals(realHostName)) {
                                  return mask;
                              }
                          }
                      }
                  }
              }
          } catch (CertificateParsingException e) {
          }
          mask |= 4;
      }
      return mask;
  }

    public static class CertificateNotTrustedException extends CertificateException {

        private static final long serialVersionUID = 4845L;

        public CertificateNotTrustedException() {
            super();
        }

        public CertificateNotTrustedException(String msg) {
            super(msg);
        }
    }

    @SuppressWarnings("serial")
    public static class CertificateDoesNotConformConstraints extends CertificateException {

        public CertificateDoesNotConformConstraints(String msg, Exception source) {
            super(msg, source);
        }
    }
}
