zoukankan      html  css  js  c++  java
  • SSL 规格严格

     

    我们使用java实现SSL安全连接,在遇到未信任的证书时,弹出一个对话框,让用户选择是否信任该站点,信任的话,就把此站点提供的证书导入本地证书库。

    为了实现此功能,我们需要重载X509TrustManager类

    StoreCertTrustManager.java
    ----------------------------------begin--------------------------------------------------
    import java.io.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import java.security.*;
    import java.security.cert.*;
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    import javax.net.ssl.*;

    /**
    * This class implements a TrustManager for authenticating the servers certificate.
    * It enhances the default behaviour.
    */

    class StoreCertTrustManager implements X509TrustManager {
    /** The trustmanager instance used to delegate to default behaviour.*/
    private TrustManager tm=null;

    /** Password for own keystore */
    private final char[] keyStorePassword=new String("changeit").toCharArray();

    /** Path to own keystore. Store it into the home directory to avoid permission problems.*/
    private final String keyStorePath=System.getProperty("user.home")+"/https-keystore";
    /** The stream for reading from the keystore. */
    FileInputStream keyStoreIStream=null;
    /** The instance of the keystore */
    private KeyStore keyStore=null;

    /**
    * Creates a TrustManager which first checks the default behaviour of the X509TrustManager.
    * If the default behaviour throws a CertificateException ask the user if the certificate
    * should be declared trustable.
    *
    * @throws Exception: If SSL - initialization failed.
    */


    StoreCertTrustManager() throws Exception {
    /* Try to set the truststore system property to our keystore
    * if we have the appropriate permissions.*/

    try{
    File httpsKeyStore=new File(keyStorePath);
    if(httpsKeyStore.exists()==true) {
    System.setProperty("javax.net.ssl.trustStore",keyStorePath);
    }
    }catch(SecurityException se) {}

    /* Create the TrustManagerFactory. We use the SunJSSE provider
    * for this purpose.*/

    TrustManagerFactory tmf=TrustManagerFactory.getInstance("SunX509", "SunJSSE");
    tmf.init((java.security.KeyStore)null);
    tm=tmf.getTrustManagers()[0];
    /* Something failed we could not get a TrustManager instance.*/
    if(tm == null) {
    throw new SSLException("Could not get default TrustManager instance.");
    }

    /* Create the file input stream for the own keystore. */
    try{
    keyStoreIStream = new FileInputStream(keyStorePath);
    } catch( FileNotFoundException fne ) {
    // If the path does not exist then a null stream means
    // the keystore is initialized empty. If an untrusted
    // certificate chain is trusted by the user, then it will be
    // saved in the file pointed to by keyStorePath.
    keyStoreIStream = null;
    }
    /* Now create the keystore. */
    try{
    keyStore=KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(keyStoreIStream,keyStorePassword);
    }catch(KeyStoreException ke) {
    System.out.println("Loading of https keystore from file <"+keyStorePath+"> failed. error message: "+ke.getMessage());
    keyStore=null;
    }
    }

    /**
    * Authenticates a client certificate. For we don't need that case only implement the
    * default behaviour.
    *
    * @param chain In: The certificate chain to be authenticated.
    * @param authType In: The key exchange algorithm.
    */

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    ((X509TrustManager)tm).checkClientTrusted(chain,authType);
    }

    /**
    * Authenticates a server certificate. If the given certificate is untrusted ask the
    * user whether to proceed or not.
    *
    * @param chain In: The certificate chain to be authenticated.
    * @param authType In: The key exchange algorithm.
    */


    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    /* Output the certifcate chain for debugging purposes */
    System.out.println("got X509 certificate from server:");
    for(int i=0; i<chain.length; i++) {
    System.out.println("chain["+i+"]: "+chain[i].getIssuerDN().getName());
    }

    try{
    /* First try the default behaviour. */
    ((X509TrustManager)tm).checkServerTrusted(chain,authType);
    }catch(CertificateException ce) {
    System.out.println("in checkServerTrusted: authType: "+authType+", got certificate exception: "+ce.getMessage());
    /* If we got here the certificate is untrusted. */

    /* If we could not craete a keystore instance forward the certificate exception. So we have
    * at least the default behaviour. */

    if(keyStore==null || chain == null || chain.length==0) {
    throw(ce);
    }
    try{
    /* If we could not find the certificate in the keystore
    * ask the user if it should be treated trustable. */

    AskForTrustability ask=new AskForTrustability (chain);
    boolean trustCert=ask.showCertificateAndGetDecision();
    if(trustCert==true) {
    // Add Chain to the keyStore.
    for (int i = 0; i < chain.length; i++){
    keyStore.setCertificateEntry(chain[i].getIssuerDN().toString(), chain[i]);
    }
    // Save keystore to file.
    FileOutputStream keyStoreOStream = new FileOutputStream(keyStorePath);
    keyStore.store(keyStoreOStream, keyStorePassword);
    keyStoreOStream.close();
    keyStoreOStream = null;
    System.out.println("Keystore saved in " + keyStorePath);
    } else {
    throw(ce);
    }
    }catch(Exception ge) {
    /* Got an unexpected exception so throw the original exception. */
    System.out.println("in checkServerTrusted: got exception type: "+ge.getClass()+" message: "+ge.getMessage());
    throw ce;
    }
    }
    }

    /**
    * Merges the system wide accepted issuers and the own ones and
    * returns them.
    *
    * @return: Array of X509 certificates of the accepted issuers.
    */

    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    X509Certificate[] cf=((X509TrustManager)tm).getAcceptedIssuers();
    X509Certificate[] allCfs=cf;

    if(keyStore != null) {
    try{
    Enumeration ownCerts=keyStore.aliases();
    Vector certsVect=new Vector();
    while(ownCerts.hasMoreElements()) {
    Object cert=ownCerts.nextElement();
    certsVect.add(keyStore.getCertificate(cert.toString()));
    }
    int newLength=cf.length+certsVect.size();
    allCfs=new X509Certificate[newLength];
    Iterator it=certsVect.iterator();
    for(int i=0; i<newLength ; i++) {
    if(i<cf.length){
    allCfs=cf;
    }
    else {
    allCfs=(X509Certificate[])it.next();
    }
    }
    }catch(KeyStoreException e) {}
    }
    for(int i=0; i<allCfs.length;i++) {
    System.out.println("allCfs["+i+"]: "+allCfs[i].getIssuerDN());
    }
    return allCfs;
    }

    /**
    * This class implements an interactive dialog. It shows the contents of a
    * certificate and asks the user if it is trustable or not.
    */

    class AskForTrustability implements ActionListener, ListSelectionListener {
    private JButton yes=new JButton("Yes"),no=new JButton("No");
    /** default to not trustable */
    private boolean isTrusted=false;
    private JDialog trust=null;
    private JList certItems=null;
    private JTextArea certValues=null;
    private JComboBox certChain=null;
    private final String certParms[]={"Version","Serial Number","Signature Algorithm", "Issuer", "Validity Period", "Subject", "Signature","Certificate Fingerprint"};
    private X509Certificate[] chain;
    private int chainIdx=0;

    /**
    * Creates an instance of the class and stores the certificate to show internally.
    *
    * @param chain In: The certificate chain to show.
    */

    AskForTrustability (X509Certificate[] chain) {
    this.chain=chain;
    }
    /**
    * This method shows a dialog with all interesting information of the certificate and
    * asks the user if the certificate is trustable or not. This method blocks until
    * the user presses the 'Yes' or 'No' button.
    *
    * @return: true: The certificate chain is trustable
    * false: The certificate chain is not trustable
    */


    public boolean showCertificateAndGetDecision() {
    if(chain == null || chain.length == 0) {
    return false;
    }

    trust=new JDialog((Frame)null,"Untrusted server certificate for SSL connection",true);
    Container cont=trust.getContentPane();
    GridBagLayout gl=new GridBagLayout();
    cont.setLayout(gl);

    JPanel pLabel=new JPanel(new BorderLayout());
    Icon icon = UIManager.getIcon("OptionPane.warningIcon");
    pLabel.add(new JLabel(icon),BorderLayout.WEST);
    JTextArea label=new JTextArea("The certificate sent by the server is unknown and not trustable!\n"+
    "Do you want to continue creating a SSL connection to that server ?\n\n"+
    "Note: If you answer 'Yes' the certificate will be stored in the file\n\n"+
    keyStorePath+"\n\n"+
    "and the next time treated trustable automatically. If you want to remove\n"+
    "the certificate delete the file or use keytool to remove certificates\n"+
    "selectively.");
    label.setEditable(false);
    label.setBackground(cont.getBackground());
    label.setFont(label.getFont().deriveFont(Font.BOLD));
    pLabel.add(label,BorderLayout.EAST);
    GridBagConstraints gc=new GridBagConstraints();
    gc.fill=GridBagConstraints.HORIZONTAL;
    gl.setConstraints(pLabel,gc);
    pLabel.setBorder(new EmptyBorder(4,4,4,4));
    cont.add(pLabel);

    Vector choices=new Vector();
    for(int i=0; i<chain.length ; i++) {
    choices.add((i+1)+". certificate of chain");
    }

    certChain = new JComboBox(choices);
    certChain.setBackground(cont.getBackground());
    certChain.setFont(label.getFont().deriveFont(Font.BOLD));
    certChain.addActionListener(this);
    JPanel pChoice=new JPanel(new BorderLayout());
    pChoice.add(certChain);
    gc=new GridBagConstraints();
    gc.fill=GridBagConstraints.HORIZONTAL;
    gc.insets=new Insets(4,4,4,4);
    gc.gridy=1;
    gl.setConstraints(pChoice,gc);
    pChoice.setBorder(new TitledBorder(new EmptyBorder(0,0,0,0), "Certificate chain"));
    cont.add(pChoice);

    certItems=new JList(certParms);
    certItems.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    certItems.addListSelectionListener(this);
    JPanel pList=new JPanel(new BorderLayout());
    pList.add(certItems);
    pList.setBorder(new TitledBorder(new EtchedBorder(), "Certificate variables"));
    gc=new GridBagConstraints();
    gc.fill=GridBagConstraints.HORIZONTAL;
    gc.insets=new Insets(4,4,4,4);
    gc.gridy=2;
    gl.setConstraints(pList,gc);
    cont.add(pList);

    certValues=new JTextArea();
    certValues.setFont(label.getFont().deriveFont(Font.BOLD));
    certValues.setEditable(false);
    certValues.setBackground(cont.getBackground());
    certValues.setLineWrap(true);
    certValues.setWrapStyleWord(true);
    JPanel pVals=new JPanel(new BorderLayout());
    pVals.add(certValues);
    pVals.setBorder(new TitledBorder(new EtchedBorder(), "Variable value"));
    gc=new GridBagConstraints();
    gc.insets=new Insets(4,4,4,4);
    gc.weightx=1.0;
    gc.weighty=1.0;
    gc.fill=GridBagConstraints.BOTH;
    gc.gridy=3;
    gl.setConstraints(pVals,gc);
    cont.add(pVals);

    JPanel p=new JPanel();
    yes.addActionListener(this);
    no.addActionListener(this);
    p.add(yes);
    p.add(no);
    gc=new GridBagConstraints();
    gc.weightx=1.0;
    gc.fill=GridBagConstraints.HORIZONTAL;
    gc.gridy=4;
    gl.setConstraints(p,gc);
    cont.add(p);

    //This should be the subject item
    certItems.setSelectedIndex(5);
    certItems.requestFocus();
    trust.pack();
    trust.setSize(500,600);
    trust.setVisible(true);
    return isTrusted;
    }

    /**
    * Listener method for changin the contents of the JTextArea according to the
    * selected list item.
    */

    public void valueChanged(ListSelectionEvent e) {
    if (e.getValueIsAdjusting()){
    return;
    }

    JList theList = (JList)e.getSource();
    if (theList.isSelectionEmpty()) {
    certValues.setText("");
    } else {
    String selVal = theList.getSelectedValue().toString();

    if(selVal.equals("Version")==true) {
    certValues.setText(String.valueOf(chain[chainIdx].getVersion()));
    } else if(selVal.equals("Serial Number")==true) {
    certValues.setText(byteArrayToHex(chain[chainIdx].getSerialNumber().toByteArray()));
    } else if(selVal.equals("Signature Algorithm")==true) {
    certValues.setText(chain[chainIdx].getSigAlgName());
    } else if(selVal.equals("Issuer")==true) {
    certValues.setText(chain[chainIdx].getIssuerDN().getName());
    } else if(selVal.equals("Validity Period")==true) {
    certValues.setText(chain[chainIdx].getNotBefore().toString()+" - "+chain[chainIdx].getNotAfter().toString());
    } else if(selVal.equals("Subject")==true) {
    certValues.setText(chain[chainIdx].getSubjectDN().getName());
    } else if(selVal.equals("Signature")==true) {
    certValues.setText(byteArrayToHex(chain[chainIdx].getSignature()));
    } else if(selVal.equals("Certificate Fingerprint")==true) {
    try{
    certValues.setText(getFingerprint(chain[chainIdx].getEncoded(),"MD5")+"\n"+
    getFingerprint(chain[chainIdx].getEncoded(),"SHA1"));
    }catch(Exception fingerE) {
    certValues.setText("Couldn't calculate fingerprints of the certificate.\nReason: "+fingerE.getMessage());
    }
    }
    }
    }

    /**
    * This method calculates the fingerprint of the certificate. It takes the encoded form of
    * the certificate and calculates a hash value from it.
    *
    * @param certificateBytes In: The byte array of the encoded data.
    * @param algorithm In: The algorithm to be used for calculating the hash value.
    * Two are possible: MD5 and SHA1
    *
    * @return: Returns a hex formated string of the fingerprint.
    */


    private String getFingerprint(byte[] certificateBytes, String algorithm) throws Exception {
    MessageDigest md = MessageDigest.getInstance(algorithm);
    md.update(certificateBytes);
    byte[] digest = md.digest();
    return new String(algorithm+": "+byteArrayToHex(digest));
    }

    /**
    * This method converts a byte array to a hex formatted string.
    *
    * @param byteData In: The data to be converted.
    *
    * @return: The formatted string.
    */

    private String byteArrayToHex(byte[] byteData) {
    StringBuffer sb=new StringBuffer();
    for (int i = 0; i < byteData.length; i++) {
    if (i != 0) sb.append(":");
    int b = byteData[i] & 0xff;
    String hex = Integer.toHexString(b);
    if (hex.length() == 1) sb.append("0");
    sb.append(hex);
    }
    return sb.toString();
    }

    /**
    * The listener for the 'Yes', 'No' buttons.
    */

    public void actionPerformed(ActionEvent e) {
    Object entry =e.getSource();
    if(entry.equals(yes)==true) {
    isTrusted=true;
    trust.dispose();
    } else if(entry.equals(certChain)==true) {
    int selIndex=certChain.getSelectedIndex();
    if(selIndex >=0 && selIndex < chain.length) {
    chainIdx=selIndex;
    int oldSelIdx=certItems.getSelectedIndex();
    certItems.clearSelection();
    certItems.setSelectedIndex(oldSelIdx);
    }
    } else {
    trust.dispose();
    }
    }
    }
    }


    -----------------------------------------------end------------------------------------------------

    然后我们在主类中通过以下语句调用:
      SSLSocketFactory factory = null;
    KeyManager[] km = null;
    TrustManager[] tm = {new StoreCertTrustManager()};
    SSLContext sslContext = SSLContext.getInstance("SSL","SunJSSE");
    sslContext.init(null, tm, new java.security.SecureRandom());
    factory = sslContext.getSocketFactory();
    SSLSocket socket =
    (SSLSocket)factory.createSocket("localhost", 7002);
  • 相关阅读:
    HANDLER进行堆叠注入
    CDUT第一届信安大挑战Re-wp
    Nu1LBook第一章wp
    Linux和VMWare
    [MRCTF]Xor
    IDA 调整栈帧 (411A04:positive sp value has been found)
    [BUU] 简单注册器
    2020年“安洵杯”四川省大学生信息安全技术大赛 部分WRITEUP
    关于我的pip不听话,总是说【Fatal error in launcher: Unable to create process using '"'】这件事
    C语言的PELode编写记录
  • 原文地址:https://www.cnblogs.com/diyunpeng/p/1652538.html
Copyright © 2011-2022 走看看