Package org.snmp4j.security

Source Code of org.snmp4j.security.AuthGeneric

/*_############################################################################
  _##
  _##  SNMP4J - AuthGeneric.java 
  _##
  _##  Copyright (C) 2003-2009  Frank Fock and Jochen Katz (SNMP4J.org)
  _## 
  _##  Licensed under the Apache License, Version 2.0 (the "License");
  _##  you may not use this file except in compliance with the License.
  _##  You may obtain a copy of the License at
  _## 
  _##      http://www.apache.org/licenses/LICENSE-2.0
  _## 
  _##  Unless required by applicable law or agreed to in writing, software
  _##  distributed under the License is distributed on an "AS IS" BASIS,
  _##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  _##  See the License for the specific language governing permissions and
  _##  limitations under the License.
  _## 
  _##########################################################################*/

package org.snmp4j.security;

import java.security.MessageDigest;
import org.snmp4j.log.*;
import org.snmp4j.smi.OctetString;

/**
* The abstract class AuthGeneric implements common operations for
* SNMP authentication protocols, such as MD5 and SHA.
*
* @author Jochen Katz & Frank Fock
* @version 1.0
*/

public abstract class AuthGeneric
    implements AuthenticationProtocol {

  private static final LogAdapter logger = LogFactory.getLogger(AuthGeneric.class);
  private int digestLength;
  private String protoName;

  public AuthGeneric(String protoName, int digestLength) {
    this.protoName = protoName;
    this.digestLength = digestLength;
  }

  public int getDigestLength() {
    return digestLength;
  }

  /**
   * Get a fresh MessageDigest object of the Algorithm specified in the
   * constructor.
   *
   * @return a new, fresh Message Digest object.
   */
  protected MessageDigest getDigestObject() {
    MessageDigest md;
    try {
      md = MessageDigest.getInstance(protoName);
    }
    catch (java.security.NoSuchAlgorithmException e) {
      throw new InternalError(protoName + " not supported in this VM.");
    }
    // not needed the first time: md.reset();
    return md;
  }

  public boolean authenticate(byte[] authenticationKey,
                              byte[] message,
                              int messageOffset,
                              int messageLength,
                              ByteArrayWindow digest) {
    MessageDigest md = getDigestObject();

    byte[] newDigest;
    byte[] k_ipad = new byte[64]; /* inner padding - key XORd with ipad */
    byte[] k_opad = new byte[64]; /* outer padding - key XORd with opad */

    // clear the bytes for the digest (12 bytes only!)
    for (int i = 0; i < MESSAGE_AUTHENTICATION_CODE_LENGTH; ++i) {
      digest.set(i, (byte)0);
    }

    /*
     * the HMAC_MD transform looks like:
     *
     * MD(K XOR opad, MD(K XOR ipad, msg))
     *
     * where K is an n byte key
     * ipad is the byte 0x36 repeated 64 times
     * opad is the byte 0x5c repeated 64 times
     * and text is the data being protected
     */
    /* start out by storing key, ipad and opad in pads */
    for (int i = 0; i < authenticationKey.length; ++i) {
      k_ipad[i] = (byte) (authenticationKey[i] ^ 0x36);
      k_opad[i] = (byte) (authenticationKey[i] ^ 0x5c);
    }
    for (int i = authenticationKey.length; i < 64; ++i) {
      k_ipad[i] = 0x36;
      k_opad[i] = 0x5c;
    }

    /* perform inner MD */
    md.update(k_ipad); /* start with inner pad      */
    md.update(message, messageOffset, messageLength); /* then text of msg  */
    newDigest = md.digest(); /* finish up 1st pass        */
    /* perform outer MD */
    md.reset(); /* init md5 for 2nd pass     */
    md.update(k_opad); /* start with outer pad      */
    md.update(newDigest); /* then results of 1st hash  */
    newDigest = md.digest(); /* finish up 2nd pass        */

    // copy the digest into the message (12 bytes only!)
    for (int i = 0; i < 12; ++i) {
      digest.set(i, newDigest[i]);
    }
    return true;
  }

  public boolean isAuthentic(byte[] authenticationKey,
                             byte[] message,
                             int messageOffset,
                             int messageLength,
                             ByteArrayWindow digest) {
    // copy digest from message
    ByteArrayWindow origDigest =
        new ByteArrayWindow(new byte[MESSAGE_AUTHENTICATION_CODE_LENGTH], 0,
                            MESSAGE_AUTHENTICATION_CODE_LENGTH);
    System.arraycopy(digest.getValue(), digest.getOffset(),
                     origDigest.getValue(), 0,
                     MESSAGE_AUTHENTICATION_CODE_LENGTH);

    // use the authenticate() method to recalculate the digest
    if (!authenticate(authenticationKey, message, messageOffset,
                      messageLength, digest)) {
      return false;
    }
    return digest.equals(origDigest, 12);
  }

  public byte[] changeDelta(byte[] oldKey,
                            byte[] newKey,
                            byte[] random) {
    // algorithm according to USM-document textual convention KeyChange
    // works with SHA and MD5
    // modifications needed to support variable length keys
    // algorithm according to USM-document textual convention KeyChange
    MessageDigest hash = getDigestObject();

    int digestLength = hash.getDigestLength();

    if (logger.isDebugEnabled()) {
      logger.debug(protoName + "oldKey: " +
                   new OctetString(oldKey).toHexString());
      logger.debug(protoName + "newKey: " +
                   new OctetString(newKey).toHexString());
      logger.debug(protoName + "random: " +
                   new OctetString(random).toHexString());
    }
    int iterations = (oldKey.length - 1) / hash.getDigestLength();

    OctetString tmp = new OctetString(oldKey);
    OctetString delta = new OctetString();
    for (int k = 0; k < iterations; k++) {
        tmp.append(random);
        hash.update(tmp.getValue());
        tmp.setValue(hash.digest());
        delta.append(new byte[digestLength]);
        for (int kk=0; kk < digestLength; kk++) {
          delta.set(k * digestLength + kk,
                    (byte)(tmp.get(kk) ^ newKey[k * digestLength + kk]));
        }
    }

    tmp.append(random);
    hash.update(tmp.getValue());
    tmp = new OctetString(hash.digest(), 0, oldKey.length - delta.length());
    for (int j = 0; j < tmp.length(); j++) {
      tmp.set(j, (byte) (tmp.get(j) ^ newKey[iterations * digestLength + j]));
    }
    byte[] keyChange = new byte[random.length + delta.length() + tmp.length()];
    System.arraycopy(random, 0, keyChange, 0, random.length);
    System.arraycopy(delta.getValue(), 0, keyChange,
                     random.length, delta.length());
    System.arraycopy(tmp.getValue(), 0, keyChange,
                     random.length + delta.length(), tmp.length());

    if (logger.isDebugEnabled()) {
      logger.debug(protoName + "keyChange:" +
                   new OctetString(keyChange).toHexString());
    }
    return keyChange;
  }

  public byte[] passwordToKey(OctetString passwordString, byte[] engineID) {

    MessageDigest md = getDigestObject();

    byte[] digest;
    byte[] buf = new byte[64];
    int password_index = 0;
    int count = 0;
    byte[] password = passwordString.getValue();

    /* Use while loop until we've done 1 Megabyte */
    while (count < 1048576) {
      for (int i = 0; i < 64; ++i) {
        /* Take the next octet of the password, wrapping */
        /* to the beginning of the password as necessary.*/
        buf[i] = password[password_index++ % password.length];
      }
      md.update(buf);
      count += 64;
    }
    digest = md.digest();
    if (logger.isDebugEnabled()) {
      logger.debug(protoName + "First digest: " +
                   new OctetString(digest).toHexString());
    }

    /*****************************************************/
    /* Now localize the key with the engine_id and pass  */
    /* through MD to produce final key                   */
    /*****************************************************/
    md.reset();
    md.update(digest);
    md.update(engineID);
    md.update(digest);
    digest = md.digest();
    if (logger.isDebugEnabled()) {
      logger.debug(protoName + "localized key: " +
                   new OctetString(digest).toHexString());
    }

    return digest;
  }

  public byte[] hash(byte[] data) {
    MessageDigest md = getDigestObject();
    md.update(data);
    return md.digest();
  }

  public byte[] hash(byte[] data, int offset, int length) {
    MessageDigest md = getDigestObject();
    md.update(data, offset, length);
    return md.digest();
  }

}
TOP

Related Classes of org.snmp4j.security.AuthGeneric

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.