php與java關於RSA加密解密的實踐

由於最近公司與第三方平臺要做兌換券的銷售,於是需要寫接口對接,對方文檔寫到,需要通過RSA非對稱加密,對兌換碼進行加密再傳輸,然而對方並沒有SDK提供,提供了個JAVA的類,但是我們平臺是使用php開發web,沒辦法,只能自己研究,當然也從這個過程中學到不少東西,特此記錄一些關鍵點。

公鑰私鑰的格式

關於RSA加密的原理,可以自己搜索,RSA只能用於加密一些長度不長的數據。當然也有補充方案,就是分段加密。

查看了對方java代碼,可以知道是 X.509 證書資源,然後一般平臺給出去的公鑰是16進制字符串,php關於RSA加密、解密的函數 openssl_pkey_get_public 可以解密的證書:

  1. 一個 X.509 證書資源
  2. 一個file://path/to/file.pem格式的字符串。文件名必須包含一個PEM編碼的證書或者密鑰(也許二者都有).
  3. 一個 PEM 格式的公鑰。

所以首先我們需要將對方給的16進制數轉爲PEM格式的公鑰內容。下面是一個公鑰的示例:

-----BEGIN PUBLIC KEY-----  
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3//sR2tXw0wrC2DySx8vNGlqt  
3Y7ldU9+LBLI6e1KS5lfc5jlTGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2kl  
Bd6h4wrbbHA2XE1sq21ykja/Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o  
2n1vP1D+tD3amHsK7QIDAQAB  
-----END PUBLIC KEY-----

PEM格式的公鑰有開頭和結尾的兩個標示,然後每64位進行換行

php 轉換十六進制公鑰:

// 將十六進制,轉換回普通字符串
$publicStr = base64_encode(hex2bin($public_key));
// 每64位進行換行
$key = (wordwrap($publicStr, 64, "\n", true))."\n";
// 添加pem格式頭和尾
$pem_key = "-----BEGIN PUBLIC KEY-----\n" . $key . "-----END PUBLIC KEY-----\n";

這樣,我們就得到了正常PEM證書中的字符串格式

RSA加密和解密

// public key encrypt
public function encryptByPublicKey($data, $public_key)
{
	// 從證書中得到資源
	$publicKeyPEM = openssl_pkey_get_public($public_key);
	// 加密
	$result = openssl_public_encrypt($data, $encrypted, $publicKeyPEM, OPENSSL_PKCS1_PADDING); 
	// 返回數據
	return $result ? base64_encode($encrypted) : false;
}

public function decryptByPublicKey($data, $public_key)
{
	// 從證書中得到資源
	$publicKeyPEM = openssl_pkey_get_public($public_key);
	// 解密
	$result = openssl_public_decrypt(base64_decode($data), $decrypted, $publicKeyPEM, OPENSSL_PKCS1_PADDING);
	// 返回數據
	return $result ? $decrypted : false;
}

私鑰同理,我們項目只用到了公鑰

  1. 加密後需要進行 base64_encode,因爲加密後的數據直接輸出是亂碼,保證數據傳輸過程中不會丟失和出錯
  2. OPENSSL_PKCS1_PADDING 其實可以不寫,是函數的默認值,這個是數據的填充方式,還有其他的值,這個需要詢問對方使用的方式,一般默認這個,可以查看php文檔:openssl_public_encrypt
  3. 這裏沒有進行明文長度的限制,因爲我們加密的東西是定長12位字符串,不會存在超過117字節的情況,如果你不是,需要分段加密,可以參考php文檔官方的例子:openssl_public_decrypt,下面的 notes 詳細的解釋瞭如何計算明文的長度限制,還舉例了分段加密

如果你使用我的代碼成功完成了你的項目,恭喜你,如果不能,你可以自己根據具體項目情況進行研究改造,面對不知道的東西,克服就是你的學習過程

最後附上JAVA的RSA實現代碼,不得感慨php真的方便,但同樣,也是使你很多時候不知道底層的實現,這也是爲什麼很多人推薦,學一門動態和一門靜態的語言的原因,對你很有幫助

Cipherimport java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * RSA加密,解密, 加簽 ,解籤 算法集合
 * 
 * RSA 公私鑰非對稱性加密算法
 * 
 * 
 */
public class RSAUtil {
	/** 加密算法RSA */
	public static final String KEY_ALGORITHM = "RSA";
	/** 簽名算法 */
	public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";// SHA1withRSA_MD5withRSA
	/** RSA最大加密明文大小 */
	private static final int MAX_ENCRYPT_BLOCK = 117;
	/* RSA最大解密密文大小 */
	private static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 利用公鑰對數據進行加密
	 * @param data 待加密的數據字節數組
	 * @param publicKey base64 encode後的公鑰
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		// 對數據加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 對數據分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}


   

	public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
		byte[] keyBytes = Base64Util.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 對數據分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	public static void main(String[] args) throws Exception {
	 
		byte[] d = HexUtil.decode("16進制處理的公鑰,對應業務運營人員提供");
		String encryStr = Base64Util.encode(encryptByPublicKey("abcsdsddsd".getBytes(), Base64Util.encode(d)));
		System.out.println(encryStr);
		//encryStr  爲接口報文加密傳輸的內容
		
		//如下爲作廢接口解密獲取明文卡密
		new String(decryptByPublicKey(Base64Util.decode("券碼作廢時壹錢包傳遞過來的加密券碼"), Base64Util.encode(d)));
     	}

}


/**
 * 
 */
public class HexUtil {
    private HexUtil() {
    }

    public static String encode(byte[] src) {
        StringBuffer hs = new StringBuffer();

        for (int i = 0; i < src.length; ++i) {
            String stmp = Integer.toHexString(src[i] & 255).toUpperCase();
            if (stmp.length() == 1) {
                hs.append("0").append(stmp);
            } else {
                hs.append(stmp);
            }
        }

        return hs.toString();
    }

    public static byte[] decode(String hex) throws SecurityException {
        if (hex.length() % 2 != 0) {
            throw new SecurityException("invalid hex string");
        } else {
            char[] arr = hex.toCharArray();
            byte[] b = new byte[hex.length() / 2];
            int i = 0;
            int j = 0;

            for (int l = hex.length(); i < l; ++j) {
                String swap = "" + arr[i++] + arr[i];
                int byteint = Integer.parseInt(swap, 16) & 255;
                b[j] = (new Integer(byteint)).byteValue();
                ++i;
            }

            return b;
        }
    }
}




import org.apache.log4j.Logger;

import java.io.ByteArrayOutputStream;

public class Base64Util {

    private static final Logger log = Logger.getLogger(Base64Util.class);

    private static final char[] base64EncodeChars = new char[]{'A', 'B', 'C',
            'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
            'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
            'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
            '3', '4', '5', '6', '7', '8', '9', '+', '/'};
    private static byte[] base64DecodeChars = new byte[]{-1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59,
            60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1,
            -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
            38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
            -1, -1};

    private Base64Util() {
    }

    /**
     * @param encode(編碼的字符)
     * @return 將編碼的字符竄解碼成可用字符竄
     */

    public static String getFromBase64(String encode) {
        byte[] b = null;
        String result = null;
        if (encode != null) {
            BASE64Decoder decoder = new BASE64Decoder();
            try {
                b = decoder.decodeBuffer(encode);
                result = new String(b, "utf-8");
            } catch (Exception e) {
                log.error("編碼異常", e);
            }
        }
        return result;
    }

    /**
     * <code>encode</code>
     *
     * @param data
     * @return
     * @since 2012-2-9 liaoyp
     */
    public static String encode(byte[] data) {
        StringBuilder sb = new StringBuilder();
        int len = data.length;
        int i = 0;
        int b1;
        int b2;
        int b3;
        while (i < len) {
            b1 = data[i++] & 0xff;
            if (i == len) {

                sb.append(base64EncodeChars[b1 >>> 2]);
                sb.append(base64EncodeChars[(b1 & 0x3) << 4]);

                sb.append("==");
                break;
            }
            b2 = data[i++] & 0xff;
            if (i == len) {
                sb.append(base64EncodeChars[b1 >>> 2]);
                sb.append(base64EncodeChars[((b1 & 0x03) << 4)
                        | ((b2 & 0xf0) >>> 4)]);
                sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
                sb.append("=");
                break;
            }
            b3 = data[i++] & 0xff;
            sb.append(base64EncodeChars[b1 >>> 2]);
            sb.append(base64EncodeChars[((b1 & 0x03) << 4)
                    | ((b2 & 0xf0) >>> 4)]);

            sb.append(base64EncodeChars[((b2 & 0x0f) << 2)
                    | ((b3 & 0xc0) >>> 6)]);
            sb.append(base64EncodeChars[b3 & 0x3f]);
        }
        return sb.toString();
    }

    /**
     * <code>decode</code>
     *
     * @param str
     * @return
     * @since 2012-2-9 liaoyp
     */
    public static byte[] decode(String str) {
        byte[] data = str.getBytes();
        int len = data.length;
        ByteArrayOutputStream buf = new ByteArrayOutputStream(len);
        int i = 0;
        int b1;
        int b2;
        int b3;
        int b4;
        while (i < len) { /* b1 */
            do {
                b1 = base64DecodeChars[data[i++]];
            } while (i < len && b1 == -1);
            if (b1 == -1) {
                break;
            } /* b2 */
            do {
                b2 = base64DecodeChars[data[i++]];
            } while (i < len && b2 == -1);
            if (b2 == -1) {
                break;
            }
            buf.write((int) ((b1 << 2) | ((b2 & 0x30) >>> 4))); /* b3 */
            do {
                b3 = data[i++];
                if (b3 == 61) {
                    return buf.toByteArray();
                }
                b3 = base64DecodeChars[b3];
            } while (i < len && b3 == -1);
            if (b3 == -1) {
                break;
            }
            buf.write((int) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); /* b4 */
            do {
                b4 = data[i++];
                if (b4 == 61) {
                    return buf.toByteArray();
                }
                b4 = base64DecodeChars[b4];
            } while (i < len && b4 == -1);
            if (b4 == -1) {
                break;
            }
            buf.write((int) (((b3 & 0x03) << 6) | b4));
        }
        return buf.toByteArray();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章