一段JAVA签名算法的PHP改写

源代码是这样的:

public class AuthorizationSignature {
    public static String createSignature(String verb, String contentMD5, String contentType, String date,
            String canonicalizedSALHeaders, String canonicalizedResource) {
        String signatureStr = verb + "\\n" + contentMD5 + "\\n" + contentType + "\\n" + date + "\\n"
                + canonicalizedSALHeaders + canonicalizedResource;
        signatureStr = java.net.URLDecoder.decode(signatureStr);
        HmacSHA1Signature hss = new HmacSHA1Signature();
        String toSignature = hss.computeSignature(AuthConfiguration.getInstance().getAuthConfigurationDTO()
                .getSecretKey(), signatureStr);
        String authorization = AuthConfiguration.getInstance().getAuthConfigurationDTO().getEnterpriseHeader()
                + AuthConfiguration.getInstance().getAuthConfigurationDTO().getAccessKey() + ":" + toSignature;
        return authorization;
    }
}

  

public class HmacSHA1Signature extends ServiceSignature {
    private static final Logger LOG = LoggerFactory.getLogger(HmacSHA1Signature.class);

    private static final String DEFAULT_CHARSET = "UTF-8";
    private static final String ALGORITHM = "HmacSHA1";

    @Override
    public String getAlgorithm() {
        return ALGORITHM;
    }

    @Override
    public String computeSignature(String key, String data) {
        byte[] signData = null;
        try {
            signData = signature(key.getBytes(DEFAULT_CHARSET), data.getBytes(DEFAULT_CHARSET));
        } catch (UnsupportedEncodingException ex) {
            LOG.debug(ex.getMessage());
        }
        return BinaryUtil.toBase64String(signData);
    }

    private byte[] signature(byte[] key, byte[] data) {
        try {
            Mac mac = Mac.getInstance(ALGORITHM);
            mac.init(new SecretKeySpec(key, ALGORITHM));
            return mac.doFinal(data);
        } catch (NoSuchAlgorithmException e1) {
            // throw new RuntimeException("Unsupported algorithm: HmacSHA1");
            LOG.error("Unsupported algorithm: HmacSHA1", e1);
            return null;
        } catch (InvalidKeyException e) {
            // throw new RuntimeException();
            LOG.debug(e.getMessage());
            return null;
        }
    }
}

  

public class BinaryUtil {
    private static final Logger LOG = LoggerFactory.getLogger(BinaryUtil.class);

    public static String toBase64String(byte[] binaryData) {
        String toBase64Result = null;
        try {
            toBase64Result = new String(Base64.encodeBase64(binaryData), ContentUtil.BYTE_CODE);
        } catch (UnsupportedEncodingException e) {
            LOG.debug(e.getMessage());
        }
        return toBase64Result;
    }

    public static byte[] fromBase64String(String base64String) {
        byte[] fromBase64Result = null;
        try {
            fromBase64Result = Base64.decodeBase64(base64String.getBytes(ContentUtil.BYTE_CODE));
        } catch (UnsupportedEncodingException e) {
            LOG.debug(e.getMessage());
        }
        return fromBase64Result;
    }
}

  需要改写为PHP代码, 一步步分析, 首先是核心的MAC_SHA1签名算法

            Mac mac = Mac.getInstance(ALGORITHM);
            mac.init(new SecretKeySpec(key, ALGORITHM));
            return mac.doFinal(data);

  等效的PHP代码是调用内建函数hash_hmac, JAVA代码是针对字节数组签名返回字节数组, 所以这里对返回值需要处理一下, 处理成字节数组

<?php
require_once DIR_SAL . 'util/BytesUtil.php';
require_once DIR_SAL . 'util/Base64Util.php';
class HmacSHA1Signature {

	public function computeSignature($key, $data) {
		$hash = $this->hmac ( $data, $key );
		$hash = str_split ( $hash );
		foreach ( $hash as $index => $value ) {
			$asc = ord ( $value );
			if ($asc > 128) {
				$hash [$index] = ord ( $value ) - 128 * 2;
			} else {
				$hash [$index] = ord ( $value );
			}
		}
		$bytes = Base64Util::encodeBase64($hash);
		return BytesUtil::toStr($bytes);
	}

	private function hmac($data, $key, $hashFunc = 'sha1', $rawOutput = true) {
		if (! in_array ( $hashFunc, hash_algos () )) {
			$hashFunc = 'sha1';
		}
		return hash_hmac ( $hashFunc, $data, $key, $rawOutput );
	}
}

  JAVA代码中还将结果的字节数组进行base64转换

    public static String toBase64String(byte[] binaryData) {
        String toBase64Result = null;
        try {
            toBase64Result = new String(Base64.encodeBase64(binaryData), ContentUtil.BYTE_CODE);
        } catch (UnsupportedEncodingException e) {
            LOG.debug(e.getMessage());
        }
        return toBase64Result;
    }

这个转码方式符合base64的定义, 即将3个8位表示数据的方式转变为4个6位标识数据, 即3*8=4*6, 这样会多出若干字节值, 具体算法实现通过bing搜到的2篇文章中的JAVA代码综合起来实现 (直接使用base64对字符串编码的结果和预期不符):

<?php
class Base64Util {

	public static function encodeBase64($data) {
		$encodes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		require_once DIR_VENTOR . 'sal/util/BytesUtil.php';
		$encodes = BytesUtil::getBytes_10 ( $encodes );
		$dataLength = intval ( count ( $data ) );
		$modulus = intval ( $dataLength % 3 );
		//计算结果应有位数
		if ($modulus == 0) {
			//byte位数能被3整除
			$sbLength = intval ( (4 * $dataLength) / 3 );
		} else {
			$sbLength = intval ( 4 * (intval ( ($dataLength / 3) ) + 1) );
		}
		$sb = array ();
		$pos = 0;
		$val = 0;
		foreach ( $data as $i => $byte ) {
			$val = ($val << 8) | ($data [$i] & 0xFF);
			$pos += 8;
			while ( $pos > 5 ) {
				$index = $val >> ($pos -= 6);
				$sb [] = $encodes [$index];
				$val &= ((1 << $pos) - 1);
			}
		}
		if ($pos > 0) {
			$index = $val << (6 - $pos);
			$sb [] = $encodes [$index];
		}
		//位数不够的用=字符(ascII值为61)填充
		$real = count ( $sb );
		if ($real < $sbLength) {
			for($i = 0; $i < $sbLength - $real; $i ++) {
				$sb [] = 61;
			}
		}
		return $sb;
	}
}

  

posted @ 2015-05-12 09:35  魔芋红茶  阅读(1176)  评论(0编辑  收藏  举报