Java & PHP RSA 互通密鑰、簽名、驗簽、加密、解密


RSA加密算法是一種非對稱加密算法。在公開密鑰加密和電子商業中RSA被廣泛使用。RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。

RSA是第一個比較完善的公開密鑰算法,它既能用於加密,也能用於數字簽名。這個算法經受住了多年深入的密碼分析,雖然密碼分析者既不能證明也不能否定RSA的安全性,但這恰恰說明該算法有一定的可信性,目前它已經成為最流行的公開密鑰算法。

RSA的安全基於大數分解的難度。其公鑰和私鑰是一對大素數(100到200位十進制數或更大)的函數。從一個公鑰和密文恢復出明文的難度,等價於分解兩個大素數之積(這是公認的數學難題)。

RSA的公鑰、私鑰的組成,以及加密、解密的公式可見於下表:

版本

語言 版本
PHP 7.3.11
Java 1.8.0_231

密鑰生成

PHP

RSAUtils::resetGenKeyPair

public static function resetGenKeyPair()
{
	$config = array(
		"private_key_bits" => self::PRIVATE_KEY_BITS,
		"private_key_type" => self::KEY_ALGORITHM,
	);

	$openssl = openssl_pkey_new($config);
	openssl_pkey_export($openssl, $privateKey);
	$publicKey = openssl_pkey_get_details($openssl);
	$publicKey = $publicKey["key"];

	return [
		'publicKey'     => $publicKey,
		'privateKey'    => $privateKey,
		'publicKeyStr'  => self::key2str($publicKey),
		'privateKeyStr' => self::key2str($privateKey)
	];
}

Java

RSAUtils.resetGenKeyPair

static Map<String, Object> resetGenKeyPair() throws Exception {
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
    keyPairGen.initialize(1024);

    KeyPair keyPair            = keyPairGen.generateKeyPair();
    RSAPublicKey publicKey     = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey privateKey   = (RSAPrivateKey) keyPair.getPrivate();
    Map<String, Object> keyMap = new HashMap<String, Object>(2);

    keyMap.put(PUBLIC_KEY, publicKey);
    keyMap.put(PRIVATE_KEY, privateKey);
    return keyMap;
}

簽名

私鑰加簽。

有時簽名可能會因為數據編碼不同而導致不一致,所以要求 PHP 和 Java 都對數據進行編碼處理。

PHP

RSAUtils::sign

public static function sign($dataStr, $privateKey)
{
    $dataStr = self::str2utf8($dataStr);
    $privateKeyId = openssl_get_privatekey($privateKey);
    openssl_sign($dataStr, $sign, $privateKeyId, self::SIGNATURE_ALGORITHM);
    openssl_free_key($privateKeyId);
    return base64_encode($sign);
}

Java

RSAUtils.sign

static String sign(String data, String privateKey) throws Exception {
    byte[] keyBytes = Base64.decode(privateKey);
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    PrivateKey privateK   = keyFactory.generatePrivate(pkcs8KeySpec);
    Signature signature   = Signature.getInstance(SIGNATURE_ALGORITHM);

    signature.initSign(privateK);
    signature.update(data.getBytes(ENCODING));
    return Base64.encodeToString(signature.sign());
}

驗簽

公鑰驗簽。

PHP

RSAUtils::verifySign

public static function verifySign($dataStr, $publicKey, $sign)
{
    $dataStr     = self::str2utf8($dataStr);
    $publicKeyId = openssl_get_publickey($publicKey);
    return (boolean) openssl_verify($dataStr, base64_decode($sign), $publicKeyId, self::SIGNATURE_ALGORITHM);
}

Java

RSAUtils.verifySign

static boolean verifySign(String data, String publicKey, String sign) throws Exception {
    try {
        byte[] keyBytes = Base64.decode(publicKey);

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory      = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK          = keyFactory.generatePublic(keySpec);
        Signature signature        = Signature.getInstance(SIGNATURE_ALGORITHM);

        signature.initVerify(publicK);
        signature.update(data.getBytes(ENCODING));

        return signature.verify(Base64.decode(sign));
    } catch (Exception e) {
        throw e;
    }
}

加密

公鑰加密

PHP

RSAUtils::encryptByPublicKey

public static function encryptByPublicKey($dataStr, $publicKey)
{
    $dataStr     = self::str2utf8($dataStr);
    $publicKeyId = openssl_get_publickey($publicKey);
    $data        = "";

    $dataArray = str_split($dataStr, self::PRIVATE_KEY_BITS / 8 - 11);
    foreach ($dataArray as $value) {
        openssl_public_encrypt($value,$encryptedTemp, $publicKeyId,self::EN_DE_ALGORITHM);
        $data .= $encryptedTemp;
    }
    openssl_free_key($publicKeyId);
    return base64_encode($data);
}

Java

RSAUtils.encryptByPublicKey

static String encryptByPublicKey(String dataStr, String publicKey) throws Exception {
    byte[] data     = dataStr.getBytes(ENCODING);
    byte[] keyBytes = Base64.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 Base64.encodeToString(encryptedData);
}

私鑰加密

PHP

RSAUtils::encryptByPrivateKey

public static function encryptByPrivateKey($dataStr, $privateKey)
{
    $dataStr      = self::str2utf8($dataStr);
    $privateKeyId = openssl_get_privatekey($privateKey);
    $data         = "";

    $dataArray = str_split($dataStr, self::PRIVATE_KEY_BITS / 8 - 11);
    foreach ($dataArray as $value) {
        openssl_private_encrypt($value,$encryptedTemp, $privateKeyId,self::EN_DE_ALGORITHM);
        $data .= $encryptedTemp;
    }
    openssl_free_key($privateKeyId);
    return base64_encode($data);
}

Java

RSAUtils.encryptByPrivateKey

static String encryptByPrivateKey(String dataStr, String privateKey) throws Exception {
    byte[] data     = dataStr.getBytes(ENCODING);
    byte[] keyBytes = Base64.decode(privateKey);

    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    Key privateK          = keyFactory.generatePrivate(pkcs8KeySpec);
    Cipher cipher         = Cipher.getInstance(keyFactory.getAlgorithm());

    cipher.init(Cipher.ENCRYPT_MODE, privateK);
    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 Base64.encodeToString(encryptedData);
}

解密

公鑰解密

PHP

RSAUtils::decryptByPublicKey

public static function decryptByPublicKey($encryptData, $publicKey) {
    $decrypted   = "";
    $decodeStr   = base64_decode($encryptData);
    $publicKeyId = openssl_get_publickey($publicKey);

    $enArray = str_split($decodeStr, self::PRIVATE_KEY_BITS / 8);

    foreach ($enArray as $value) {
        openssl_public_decrypt($value,$decryptedTemp, $publicKeyId,self::EN_DE_ALGORITHM);
        $decrypted .= $decryptedTemp;
    }
    openssl_free_key($publicKeyId);
    return $decrypted;
}

Java

RSAUtils.decryptByPublicKey

static String decryptByPublicKey(String encryptedDataStr, String publicKey) throws Exception {
    byte[] encryptedData = Base64.decode(encryptedDataStr);
    byte[] keyBytes      = Base64.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 new String(decryptedData, ENCODING);
}

私鑰解密

PHP

RSAUtils::decryptByPrivateKey

public static function decryptByPrivateKey($encryptData, $private) {
    $decrypted    = "";
    $decodeStr    = base64_decode($encryptData);
    $privateKeyId = openssl_get_privatekey($private);

    $enArray = str_split($decodeStr, self::PRIVATE_KEY_BITS / 8);

    foreach ($enArray as $value) {
        openssl_private_decrypt($value,$decryptedTemp, $privateKeyId,self::EN_DE_ALGORITHM);
        $decrypted .= $decryptedTemp;
    }
    openssl_free_key($privateKeyId);
    return $decrypted;
}

Java

RSAUtils.decryptByPrivateKey

static String decryptByPrivateKey(String encryptedDataStr, String privateKey) throws Exception {
    byte[] encryptedData = Base64.decode(encryptedDataStr);
    byte[] keyBytes      = Base64.decode(privateKey);

    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    Key privateK          = keyFactory.generatePrivate(pkcs8KeySpec);
    Cipher cipher         = Cipher.getInstance(keyFactory.getAlgorithm());

    cipher.init(Cipher.DECRYPT_MODE, privateK);
    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 new String(decryptedData, ENCODING);
}

Demo

完整 Demo 源碼:BNDong/demo/PhpJavaRsa


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM