?? jfmauth.java
字號:
/* * Created on 2004.08.17 * JFreeMail - Java mail component * Copyright (C) 2004 Dalibor Krleza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */package org.jfreemail.core;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.BufferedOutputStream;import java.io.OutputStream;import java.net.Socket;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Vector;/** * Class used for server authentication. For now covered authentications * are PLAIN,LOGIN,APOP and CRAM-MD5. If you insist I can add other * authentications like DIGEST-MD5. I couldn't test CRAM-MD5 and * DIGEST-MD5, as I couldn't setup Sendmail with SASL autentication. * I'll apperciate if anybody could test CRAM-MD5 for SMTP. */public class JfmAuth { /** * Constructor for constructing authentication object. This could be done * with static procedure. I realized authentication this way for future use. * @param client_socket Socket object for client connection. * @param auth_type Authentication type. Look in JfmConsts static attributes. * @param username User's username. * @param password User's secret. Deleted immediately after authentication. * @param challenge Passed challenge. If authentication type requires challenge. * @throws AuthException Signaling authentication exception. */ public JfmAuth(Socket client_socket,int auth_type, String username,String password,String challenge) throws AuthException { if (username==null || password==null) throw new AuthException("AUTH_001:No defined username or password"); if (client_socket==null) throw new AuthException("AUTH_002:No active connection"); int retry=JfmConsts.AUTH_RETRY; while(retry>=0) { switch(auth_type) { case JfmConsts.POP3_PLAIN: { if (!pop3_plain(client_socket,username,password)) { if (retry<1) { try { client_socket.close(); } catch(IOException exc1) { } throw new AuthException("AUTH_003:Authentication failed"); } } else return; break; } case JfmConsts.POP3_APOP: { try { if (!pop3_apop(client_socket,challenge,username,password)) { if (retry<1) { try { client_socket.close(); } catch(IOException exc1) { } throw new AuthException("AUTH_003:Authentication failed"); } } else return; } catch(NoSuchAlgorithmException exc) { try { client_socket.close(); } catch(IOException exc1) { } throw new AuthException("AUTH_004:No MD5 algorithm"); } break; } case JfmConsts.POP3_CRAM_MD5: { try { if (!pop3_cram_md5(client_socket,username,password)) { if (retry<1) { try { client_socket.close(); } catch(IOException exc1) { } throw new AuthException("AUTH_003:Authentication failed"); } } else return; } catch(NoSuchAlgorithmException exc) { throw new AuthException("AUTH_004:No MD5 algorithm"); } break; } case JfmConsts.SMTP_LOGIN: { if (!smtp_login(client_socket,username,password)) { if (retry<1) { try { client_socket.close(); } catch(IOException exc1) { } throw new AuthException("AUTH_003:Authentication failed"); } } else return; break; } case JfmConsts.SMTP_CRAM_MD5: { try { if (!smtp_cram_md5(client_socket,username,password)) { if (retry<1) { try { client_socket.close(); } catch(IOException exc1) { } throw new AuthException("AUTH_003:Authentication failed"); } } else return; } catch(NoSuchAlgorithmException exc) { throw new AuthException("AUTH_004:No MD5 algorithm"); } break; } } retry--; if (retry>=0) { auth_type--; if (auth_type==JfmConsts.POP3_NONE || auth_type==JfmConsts.SMTP_NONE) throw new AuthException("AUTH_000:Exhaused all authentication types"); } } } /* * Authentication for POP3 protocol. Plain authentication using username * and password. Doesn't require any encoding or challenge calculation! */ private boolean pop3_plain(Socket client_socket,String username, String password) { try { OutputStream os=client_socket.getOutputStream(); String out1="USER "+username+String.valueOf((char)13)+String.valueOf((char)10); os.write(out1.getBytes()); String in1=JfmSocketCore.getLine(client_socket)[0]; if (in1.indexOf(JfmConsts.POP3_OK)<0) return false; String out2="PASS "+password+String.valueOf((char)13)+String.valueOf((char)10); os.write(out2.getBytes()); in1=JfmSocketCore.getLine(client_socket)[0]; if (in1.indexOf(JfmConsts.POP3_OK)<0) return false; return true; } catch(IOException exc) { return false; } } /* * APOP authentication for POP3. Need challenge from POP3 server * for authentication. POP3 transmits challenge at the beginning of * communication. Challenge looks like <3873.2878733@server>. * We are adding password at the end of challenge and calculating * message digest. Message digest is then converted into hexadecimal * format and added like follows: * APOP username message-md5-digest */ private boolean pop3_apop(Socket client_socket,String challenge, String username, String password) throws NoSuchAlgorithmException { try { OutputStream os=client_socket.getOutputStream(); MessageDigest md=MessageDigest.getInstance("MD5"); String out="APOP "+username+" "; String temp_challenge=challenge; out+=apop_md5(temp_challenge,password); out+="\r\n"; os.write(out.getBytes()); String in=JfmSocketCore.getLine(client_socket)[0]; if (in.indexOf(JfmConsts.POP3_OK)<0) return false; return true; } catch(IOException exc) { return false; } } /* * CRAM-MD5 authentication for POP3. With AUTH CRAM-MD5 we * are requesting challenge from POP3 server. Challenge is base64 * coded. After base64 decoding, challenge is in same format as for * APOP authentication. We are performing two complicated calculation * of MD5. Ending message digest i combined with username and base64 * encoded. So encoded it's being transmited to POP3 server. * * Same procedure is for SMTP CRAM-MD5 autentication! */ private boolean pop3_cram_md5(Socket client_socket,String username, String password) throws NoSuchAlgorithmException { try { OutputStream os=client_socket.getOutputStream(); MessageDigest md=MessageDigest.getInstance("MD5"); String out="AUTH CRAM-MD5\r\n"; os.write(out.getBytes()); String challenge=JfmSocketCore.getLine(client_socket)[0]; challenge=challenge.substring(2,challenge.length()-2); byte[] byte_challenge=JfmCore.base64decode(new String[] {challenge}); challenge=""; for(int i=0;i<byte_challenge.length;i++) if (byte_challenge[i]!=0) challenge+=(char)byte_challenge[i]; out=username+" "+cram_md5(challenge,password); String[] out_list=JfmCore.base64encode(out.getBytes()); out=out_list[0]+"\r\n"; os.write(out.getBytes()); String in=JfmSocketCore.getLine(client_socket)[0]; if (in.indexOf(JfmConsts.POP3_OK)<0) return false; return true; } catch(IOException exc) { return false; } } /* * Login authentication for SMTP. Username and password are transmited * only base64 encoded. */ private boolean smtp_login(Socket client_socket,String username, String password) { try { OutputStream os=client_socket.getOutputStream(); String out="AUTH LOGIN\r\n"; os.write(out.getBytes()); String in=JfmSocketCore.getLine(client_socket)[0]; if (in.charAt(0)=='4' || in.charAt(0)=='5') return false; out=JfmCore.base64encode(username.getBytes())[0]+"\r\n"; os.write(out.getBytes()); in=JfmSocketCore.getLine(client_socket)[0]; if (in.charAt(0)=='4' || in.charAt(0)=='5') return false; out=JfmCore.base64encode(password.getBytes())[0]+"\r\n"; os.write(out.getBytes()); in=JfmSocketCore.getLine(client_socket)[0]; if (in.charAt(0)=='4' || in.charAt(0)=='5') return false; return true; } catch(IOException exc) { return false; } } private String apop_md5(String challenge,String secret) throws NoSuchAlgorithmException { MessageDigest md=MessageDigest.getInstance("MD5"); byte[] end_buffer=md.digest((challenge+secret).getBytes()); String end_string=new String(""); for(int i=0;i<16;i++) { int ix=(int)(end_buffer[i] & 0x00FF); if (ix>15) end_string+=Integer.toHexString(ix); else end_string+="0"+Integer.toHexString(ix); } return end_string; } /* * CRAM-MD5 authentication for SMTP. Not very different from POP3 * CRAM-MD5. Only difference is in server responses. */ private boolean smtp_cram_md5(Socket client_socket,String username, String password) throws NoSuchAlgorithmException { try { OutputStream os=client_socket.getOutputStream(); String out="AUTH CRAM-MD5\r\n"; os.write(out.getBytes()); String in=JfmSocketCore.getLine(client_socket)[0]; if (in.charAt(0)=='4' || in.charAt(0)=='5') return false; in=in.substring(4,in.length()-2); byte[] buffer=JfmCore.base64decode(new String[]{in}); out=new String(); for(int i=0;i<buffer.length;i++) out+=(char)buffer[i]; out=username+" "+cram_md5(out,password); out=JfmCore.base64encode(out.getBytes())[0]+"\r\n"; os.write(out.getBytes()); in=JfmSocketCore.getLine(client_socket)[0]; if (in.charAt(0)=='4' || in.charAt(0)=='5') return false; return true; } catch(IOException exc) { return false; } } /* * Basic CRAM-MD5 algorithm. RFC based. Looks like * MD5(secret XOR opad,MD(secret XOR ipad,challenge)). */ private String cram_md5(String challenge,String secret) throws NoSuchAlgorithmException { byte[] secret_buffer1=expand128(secret); byte[] secret_buffer2=expand128(secret); for(int i=0;i<64;i++) { secret_buffer1[i]^=(byte)0x36; //ipad secret_buffer2[i]^=(byte)0x5C; //opad } for(int i=0;i<challenge.length();i++) secret_buffer1[i+64]=(byte)challenge.charAt(i); MessageDigest md=MessageDigest.getInstance("MD5"); byte[] in1=new byte[64+challenge.length()]; for(int i=0;i<(64+challenge.length());i++) in1[i]=secret_buffer1[i]; byte[] mid_buffer=md.digest(in1); for(int i=0;i<16;i++) secret_buffer2[i+64]=mid_buffer[i]; byte[] in2=new byte[80]; for(int i=0;i<80;i++) in2[i]=secret_buffer2[i]; byte[] end_buffer=md.digest(in2); String end_string=new String(""); for(int i=0;i<16;i++) { int ix=(int)(end_buffer[i] & 0x00FF); if (ix>15) end_string+=Integer.toHexString(ix); else end_string+="0"+Integer.toHexString(ix); } return end_string; } /* * Procedure for expanding String buffer upto 128 bytes. * Expanded bytes are set to 0! */ private byte[] expand128(String input) { byte[] buffer=new byte[128]; for(int i=0;i<128;i++) buffer[i]=0; int l=input.length(); if (l>64) l=64; for(int i=0;i<l;i++) buffer[i]=(byte)input.charAt(i); return buffer; } /* * Procedure for expanding byte buffer upto 128 bytes. * Expanded bytes are set to 0! */ private byte[] expand128(byte[] input) { byte[] buffer=new byte[128]; for(int i=0;i<128;i++) buffer[i]=0; int l=input.length; if (l>64) l=64; for(int i=0;i<l;i++) buffer[i]=input[i]; return buffer; } }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -