posts - 10, comments - 16, trackbacks - 0, articles - 3
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          在Radius服務中使用MS-CHAP-V1協議進行通訊

          Posted on 2009-07-04 11:42 青果 閱讀(4033) 評論(3)  編輯  收藏 所屬分類: 技術點滴

          最近一個項目“無線網接入動態密碼驗證” ,使用思科的ACS作為Radius客戶端,自己實現Radius服務端進行密碼驗證,步驟如下:(握手過程Radius已經封裝,無須管它)
          1,    客戶端:發送報文,接受返回報文并解析,然后進行相應的處理(客戶端由思科的ACS處理);
          2,    服務端:接受報文,解析報文并驗證(密碼之類),然后響應相應的結果(需要java實現)

          使用MS-CHAP-V1協議通訊過程簡介

             1,客戶端和服務端需要一個共享密鑰,這個可以自己約定并隨意設置,只要保持兩邊一致就行;
             2,客戶端傳過來的報文包括用戶名和加密密碼,密碼是通過挑戰數并利用DES算法和MD4算法進行加密的;
             3,服務端收到報文后,需要解析報文,并取出用戶名和加密密碼;由于用戶傳過來的加密密碼是通過不可逆算法加密的,所以,如果利用保存在服務器或動態生成的明文密碼來進行驗證的話,需要使用與客戶端同樣的步驟同樣的算法來對明文密碼進行加密,然后才能與傳遞過來的加密密碼進行匹配驗證(重點就在這里了);
             4,如果驗證失敗,只需要寫回RadiusPacket.ACCESS_REJECT 狀態字即可,否則,需要寫回RadiusPacket.ACCESS_ACCEPT,并返回通過算法計算生成的報文(這一塊也比較煩瑣)

              我使用tinyradius 開源框架做為服務端框架,由于tinyradius 只處理了pap、chap協議進行了處理,沒有特別針對微軟的ms-chap-v1和ms-chap-v2進行處理,所以我稍微改裝了一下tinyradius,修改了org.tinyradius.packet.AccessRequest.java,在其中的153行插入了RadiusAttribute msChapSpecial = getAttribute(VENDOR_ID,VENDOR_TYPE); 這樣,我就取出了ms-chap-v1的報文屬性, 其中VENDOR_ID=311代表微軟的協議,VENDOR_TYPE = 25 是ms-chap-v1協議類型。當然,在163行也加如了else if(msChapSpecial != null){}的處理,要不它會報 "Access-Request: User-Password or CHAP-Password/CHAP-Challenge missing" 錯誤,整個tinyradius修改就這么簡單,下面我會附上修改了的AccessRequest.java;
          接下來分三步介紹處理過程:

          1,接收報文
             服務端接收客戶端傳過來的報文格式如下:

             0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |  Vendor-Type  | Vendor-Length |     Ident     |     Flags     |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                            LM-Response
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       LM-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       LM-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       LM-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       LM-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       LM-Response(cont)                     |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                           NT-Response
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       NT-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       NT-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       NT-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       NT-Response (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       NT-Response (cont)                    |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           
             由以上報文分析可得到:
             Vendor-Type (一個8位字節)
                Vendor協議類型
                1 for MS-CHAP-Response.
           
             Vendor-Length (一個8位字節)
                  報文長度
                  52
           
             Ident (一個8位字節)
                  唯一標志位
                  Identical to the PPP CHAP Identifier.
           
             Flags (一個8位字節)
              如果flags為0x01,則要優先使用NT-Response來進行驗證,如果flags為0,則NT-Response必須被忽略掉

                The Flags field is one octet in length.  If the Flags field is one
                (0x01), the NT-Response field is to be used in preference to the
                LM-Response field for authentication.  The LM-Response field MAY
                still be used (if non-empty), but the NT-Response SHOULD be tried
                first.  If it is zero, the NT-Response field MUST be ignored and
                the LM-Response field used.
           
             LM-Response
                (LM-Response)域是長度為24位并由密碼和挑戰數通過函數LmChallengeResponse()加密后的,如果這個域是空的,那它肯定是被0填充的
                The LM-Response field is 24 octets in length and holds an encoded
                function LmChallengeResponse() of the password and the received challenge.  If this  field is empty, it SHOULD be zero-filled.
           

          2,加密本地明文密碼,并驗證密碼的正確性

             以上是對接收報文的分析,具體使用tinnyRadius接收代碼如下:
             byte[] authenticatorChallenge = accessRequest.getAttribute(311, 11).getAttributeData();
             byte[] vendorSpecial = accessRequest.getAttribute(211,1).getAttributeData();
             int flag = vendorSpecial.length < 2 ? 0 : vendorSpecial[1];
             authenticatorChallenge 是驗證挑戰數,vendorSpecial包含了以上報文中從indent開始到最后的數據
              然后從vendorSpecial中取出
             byte[] lmPassword = RadiusServerHelper    .getSubbytes(vendorSpecial, 2, 24);
             byte[] ntPassword = RadiusServerHelper.getSubbytes(vendorSpecial, 26,24);
             byte[] username = accessRequest.getUserName();
             byte[] localPassword = “”;//本地密碼,從數據庫中或者文件或者其他地方取出或生成,用來驗證客戶端傳過來的密碼是否正確。

              好了,所有的數據準備好了,接下來就是認證了,認證需要將本地密碼使用相應的算法加密后與客戶端傳過來的密碼進行比較。
              具體算法如下:
           byte[]  encryPassword = new byte[24];
           if (flag == 0) {
              encryPassword = MSCHAP.LmChallengeResponse(authenticatorChallenge,
                              localPassword);
              //如果flag=0,則使用lmPasswor
              if (RadiusServerHelper.byteEquels(encryPassword, lmPassword)) {
                      System.out.println("--success--");
                      return encryPassword;
                  }
          //否則,如果falg=1則先使用ntPassword,如果沒通過,再使用lmPassword
          } else if (flag == 1) {
              encryPassword = MSCHAP.NtChallengeResponse(authenticatorChallenge,
                              localPassword);

              if (RadiusServerHelper.byteEquels(encryPassword, ntPassword)) {
                      System.out.println("--success--");
                      return encryPassword;
                  }else{
                      encryPassword = MSCHAP.LmChallengeResponse(
                          authenticatorChallenge, localPassword);
                                           if(RadiusServerHelper.byteEquels(encryPassword,lmPassword)){
                           System.out.println("--success--");
                          return encryPassword;
                       }
                  }
              }
          使用函數如下:
           public static byte[] LmChallengeResponse(byte[] Challenge, byte[] Password) {
                  byte[] PasswordHash = LmPasswordHash(Password);
                  return ChallengeResponse(Challenge, PasswordHash);
              }

          public static byte[] NtChallengeResponse(byte[] Challenge, byte[] Password) {
                  byte[] PasswordHash = NtPasswordHash(Password);
                  return ChallengeResponse(Challenge, PasswordHash);
              }
          //使用DES算法對本地密碼加密
          private static byte[] LmPasswordHash(byte[] Password) {
                  String pString = (new String(Password)).toUpperCase();
                  byte[] PasswordHash = new byte[16];
                  byte[] pByte = new byte[14];

                  for (int i = 0; i < 14; i++)
                      pByte[i] = 0;

                  Password = pString.getBytes();
                  for (int i = 0; i < 14 && i < Password.length; i++)
                      pByte[i] = Password[i];

                  DesHash(pByte, 0, PasswordHash, 0);
                  DesHash(pByte, 7, PasswordHash, 8);

                  return PasswordHash;
              }
          //使用MD4算法對本地密碼進行加密
          private static byte[] NtPasswordHash(byte[] Password) {
                  byte PasswordHash[] = new byte[16];
                  byte uniPassword[] = unicode(Password);
                  IMessageDigest md = HashFactory.getInstance("MD4");
                  md.update(uniPassword, 0, uniPassword.length);
                  System.arraycopy(md.digest(), 0, PasswordHash, 0, 16);
                  return PasswordHash;
              }
          其中DES算法可以通過“gnu-crypto-2.0.1.jar” 包中的
          IBlockCipher cipher = CipherFactory.getInstance("DES");進行實現

          具體算法見 http://www.ietf.org/rfc/rfc2433.txt PAGE 10

          3,返回報文
          如果驗證失敗,只需要返回 RadiusPacket.ACCESS_ACCEPT 即可,如果驗證成功,則需要返回使用算法計算出來的響應報文

          返回的Access-Accept報文里需要包含MS-CHAP-MPPE-Keys屬性
          The MS-CHAP-MPPE-Keys Attribute contains two session keys for use
                by the Microsoft Point-to-Point Encryption Protocol (MPPE).  This
                Attribute is only included in Access-Accept packets.
           
             A summary of the MS-CHAP-MPPE-Keys Attribute format is given below.
             The fields are transmitted left to right.
           
              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |  Vendor-Type  | Vendor-Length |           Keys
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                       Keys (cont)
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                        Keys (cont)          |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           
             Vendor-Type
                12 for MS-CHAP-MPPE-Keys.
           
             Vendor-Length
                34
           
             Keys
                The Keys field consists of two logical sub-fields: the LM-Key and
                the NT-Key.  The LM-Key is eight octets in length and contains the
                first eight bytes of the output of the function LmPasswordHash(P,
                This hash is constructed as follows: let the plain-text password
                be represented by P.
           
                The NT-Key sub-field is sixteen octets in length and contains the
                first sixteen octets of the hashed Windows NT password.  The
                format of the plaintext Keys field is illustrated in the following
                diagram:
           
                0                   1                   2                   3
                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                           LM-Key
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                         LM-Key (cont)                        |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                           NT-Key
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                         NT-Key (cont)
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                         NT-Key (cont)
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                         NT-Key (cont)                        |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                          Padding
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                        Padding (cont)                        |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           
                The Keys field MUST be encrypted by the RADIUS server using the
                same method defined for the User-Password Attribute [3].  Padding
                is required because the method referenced above requires the field
                to be encrypted to be a multiple of sixteen octets in length.
           
             Implementation Note
                   This attribute should only be returned in response to an
                   Access-Request packet containing MS-CHAP attributes.
           
          返回報文需要將明文密碼進行LmPasswordHash 加密成LM-key,將明文密碼使用2次MD4加密成NT-Key,然后將LM-key + NT-key + 8位padding組成32位key,
          再將key 通過客戶端傳來的16位加密挑戰數(SA)進行加密(即接收整體報文的前16位),這個加密算法為:
          1,    將key 分成兩個16位P1,P2
          2,    將共享密鑰(S)與SA進行MD5加密得到B1
          3,    將B1與P1進行異或操作得到C1
          4,    將C1與S進行MD5加密得到B2
          5,    將B2與P2進行異或操作得到C2
          6,    將C1+C2得到返回報文

          具體參見http://www.ietf.org/rfc/rfc2548.txt

          返回報文具體代碼如下:
          byte[] lmPass = MSCHAP.LmPasswordHash(generatePassword);
          byte[] ntPass = MSCHAP.NtPasswordHash(generatePassword);
                     
          ntPass = MSCHAP.HashNtPasswordHash(ntPass);
          byte[] keys = MSCHAP.getKeysForMSCHAPv1(lmPass, ntPass);

          byte[] p1 = new byte[16];
          byte[] p2 = new byte[16];
          byte[] b1 = null;
          byte[] b2 = null;
          byte[] secret = getSecret()==null?null:getSecret().getBytes();
          byte[] challenge  accessRequest.getAuthenticator();           
                     
          System.arraycopy(keys, 0, p1, 0, 16);
          System.arraycopy(keys, 16, p2, 0, 16);
                     
          b1 = MSCHAP.HashKeys(secret, challenge);
          byte[] c1 = MSCHAP.Xor(b1, p1);
                     
          b2 = MSCHAP.HashKeys(secret, c1);
          byte[] c2 = MSCHAP.Xor(b2, p2);
                     
          byte[] response = new byte[32];
          System.arraycopy(c1, 0, response, 0, 16);
          System.arraycopy(c2, 0, response, 16, 16);
                     
          RadiusAttribute attribute = new RadiusAttribute(    MS_CHAP_MPPE_KEYS_TYPE, response);
          attribute.setVendorId(VENDOR_SPECIAL_ID);

              RadiusAttribute attribute_Encryption_Policy = new RadiusAttribute(
                              MS_MPPE_ENCRYPTION_POLICY_TYPE, MSCHAP
                                      .toByteArray("00000001"));
              attribute_Encryption_Policy.setVendorId(VENDOR_SPECIAL_ID);

              RadiusAttribute attribute_Encryption_Types = new RadiusAttribute(
                              MS_MPPE_ENCRYPTION_TYPES_TYPE, MSCHAP
                                      .toByteArray("00000006"));
              attribute_Encryption_Types.setVendorId(VENDOR_SPECIAL_ID);
                     
              int ident = accessRequest.getPacketIdentifier();
              packet = new RadiusPacket(type, accessRequest.getPacketIdentifier());
              copyProxyState(accessRequest, packet);

          其中,加密算法和異或算法可以按照算法步驟自己實現,有些算法比如說DES可以直接從jar包中引入對象實現。

          相關附件:gnu-crypto-2.0.1.jar
                  
                   




          ---------------------------------
          假到真時真亦假,真到假時假亦真
          ---------------------------------

          評論

          # re: 在Radius服務中使用MS-CHAP-V1協議進行通訊  回復  更多評論   

          2009-07-04 16:57 by 凡客誠品
          眼周的肌膚比較敏感,很多美白成分中都含有維生素C

          # re: 在Radius服務中使用MS-CHAP-V1協議進行通訊  回復  更多評論   

          2009-07-04 17:00 by 凡客誠品
          而如今的發展真是太神速了,這不能不說是社會的進步。

          # re: 在Radius服務中使用MS-CHAP-V1協議進行通訊[未登錄]  回復  更多評論   

          2015-04-17 15:34 by larry
          能請教您一個問題嗎?EAP wifi的認證怎么弄?

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 邛崃市| 铜鼓县| 罗甸县| 普洱| 澄江县| 姚安县| 手机| 大石桥市| 册亨县| 长春市| 长子县| 曲麻莱县| 金华市| 高安市| 桦南县| 西平县| 卫辉市| 荆州市| 莱芜市| 龙井市| 原阳县| 固镇县| 山丹县| 会同县| 遂溪县| 多伦县| 清水县| 百色市| 吉林省| 岳池县| 娄烦县| 永寿县| 铜山县| 洛扎县| 满洲里市| 滁州市| 秭归县| 台南县| 灵宝市| 伊春市| 诸暨市|