Java、IOS、C之间进行RSA密文传输

本文详细介绍了如何在不同平台(C++客户端、Java客户端、iOS客户端和服务端Java)上使用RSA算法进行加解密操作。重点讲解了密钥生成、公钥加密及私钥解密的过程,并提供了具体的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        有了之前三个终端进行AES加解密的经验,实现RSA加解密倒不是太复杂。两个关键点:1)RSA需要密钥文件,且密钥文件的格式很重要;2) padding方式需要一致,比较被广泛支持且被很多RAS实现设置为默认padding方式的是 PKCS1PADDING  V 1.5,建议采用。
        我是在linux下用openssl生成公钥、私钥文件的,分三个步骤,命令如下:
        1、生成公钥public_key.der 和 私钥private_key.pem(加密)
openssl req -x509 -out public_key.der -outform der -new -newkey rsa:1024 -keyout private_key.pem
      按照提示,填入私钥的密码,签名证书的组织名、邮件等信息之后,就会生成包含有公钥的证书文件public_key.der和私钥文件private_key.pem。public_key.der文件用于分发到ios客户端进行公钥加密。
        2、生成公钥public_key.pem。
openssl rsa -in private_key.pem -pubout -out public_key.pem   ,这步生成的public_key.pem用于分发到c++客户端和安卓客户端进行公钥加密。
        3、将私钥 private_key.pem(加密)转换为PKCS#8编码格式(且不加密)。
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
        这步生成的pkcs8_private_key.pem用于在Java服务端进行私钥解密。
       RSA的一般用法是在客户端用公钥加密,在网络上传输密文,然后服务端用私钥解密获取原文。所以RSA实现都会支持公钥加密、私钥解密。反过来用私钥加密然后公钥解密,理论上也是可行的。不过我没有试过。如果决定要这么做,祝君好运。
       注意:RSA提供的API不像AES,不会自动分块处理。需要手动将原文切割为128-11的块去进行加密 (128是因为采用RSA1024,128=1024/8, 11是因为padding采用版本1.5的PKCS1PADDING。这种分块方式是多个RSA实现的默认方式,推荐使用),每次加密输出的密文长度均是128字节(RSA1024),把这些密文顺序拼装起来。在解密的时候,则是将密文按128字节进行切割,解密后再拼装在一起即可。
======================C++客户端公钥加密=======================
      使用大名鼎鼎openssl类库实现,感慨下其API设计得真烂。。。。
      注意:虽然输入和输出都是std::string,但不要理解成字符串,实际上都是二进制数据。另外公钥得保存在文件中,我还没找到使用内存中公钥的方法,但对我已经够用了。
    std::string EvpHelper::rsaEncryptUsingPublicKey File(const std::string& source, const std::string& keyFile)
{
std::string result;
BIO* bp = NULL;
EVP_PKEY* key = NULL;
RSA* rsa = NULL;
EVP_PKEY_CTX* ctx = NULL;
unsigned char* encryptedData = NULL;
try
{
// load public key
OpenSSL_add_all_algorithms();
bp = BIO_new(BIO_s_file());
if (bp == NULL)
{
throw std::runtime_error("BIO_new failed.");
}
if (BIO_read_filename(bp, keyFile.c_str()) <= 0)
{
throw std::runtime_error("BIO_read_filename failed.");
}
rsa = PEM_read_bio_RSA_PUBKEY(bp, NULL, NULL, NULL);
if (rsa == NULL)
{
throw std::runtime_error("PEM_read_bio_RSA_PUBKEY failed.");
}
key = EVP_PKEY_new();
if (key == NULL)
{
throw std::runtime_error("EVP_PKEY_new failed.");
}
EVP_PKEY_assign_RSA(key, rsa);
rsa = NULL;
// encrypt
OpenSSL_add_all_ciphers();
ctx = EVP_PKEY_CTX_new(key, NULL);
if (ctx == NULL)
{
throw std::runtime_error("EVP_PKEY_CTX_new failed.");
}
if (EVP_PKEY_encrypt_init(ctx) <= 0)
{
throw std::runtime_error("EVP_PKEY_encrypt_init failed.");
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
{
throw std::runtime_error("EVP_PKEY_CTX_set_rsa_padding failed.");
}
encryptedData = new unsigned char[source.size() * 2];
size_t encryptedDataLen = 0;
size_t BLOCK_SIZE = 128 - 11;
size_t sourceDataLen = (int)source.size();
for (size_t i = 0; i < sourceDataLen; i += BLOCK_SIZE) 
{
size_t leftBytes = sourceDataLen - i;
size_t sourceBlockLen = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
size_t encryptedBlockLen = 128;
if(EVP_PKEY_encrypt(ctx, (encryptedData + encryptedDataLen), &encryptedBlockLen, 
(const unsigned char *)(source.data() + i), sourceBlockLen) <= 0)
{
throw std::runtime_error("EVP_PKEY_encrypt failed.");
}
encryptedDataLen += encryptedBlockLen;
}
result = std::string((char*)encryptedData, encryptedDataLen);
}
catch (const std::exception& e) 
{
LErr(e.what());
}
if (bp != NULL) 
{
LInfoCMD(BIO_free(bp));
}
if(rsa != NULL)
{
LInfoCMD(RSA_free(rsa));
}
if (ctx != NULL) 
{
LInfoCMD(EVP_PKEY_CTX_free(ctx));
}
if (key != NULL) 
{
LInfoCMD(EVP_PKEY_free(key));
}
if (encryptedData != NULL)
{
delete[] encryptedData;
}
return result;
}

======================Java客户端公钥加密=======================
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

public class RSAEncrypt {

    public static RSAPublicKey loadPublicKeyFromFile(String keyPath)
            throws Exception {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream(
                    keyPath)));
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(line);
                    sb.append('\r');
                }
            }
        } finally {
            if (br != null) {
                br.close();
            }
        }
        return loadPublicKey(sb.toString());
    }

    public static RSAPublicKey loadPublicKey(String publicKeyStr)
            throws Exception {
        Base64 decoder = new Base64();
        byte[] buffer = decoder.decode(publicKeyStr);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }

    public static void main(String[] args) throws Exception {
        RSAPublicKey publicKey = loadPublicKeyFromFile("public_key.pem");
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] rawText = CommonUtil.readFileContents("from.txt");

        FileOutputStream out = null;
        try {
            out = new FileOutputStream("java_out.bin");
            int BLOCK_SIZE = 128 - 11;
            for (int i = 0; i < rawText.length; i += BLOCK_SIZE) {
                int leftBytes = rawText.length - i;
                int length = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
                out.write(cipher.doFinal(rawText, i, length));
            }
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }
}
======================IOS客户端公钥加密=======================
static SecKeyRef _public_key = nil;
- (SecKeyRef) getPublicKey{
    if (_public_key == nil){
NSString* filePath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];
        NSData* certificateData = [NSData dataWithContentsOfFile:filePath];
        SecCertificateRef myCertificate =   SecCertificateCreateWith Data(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
        if (myCertificate == nil) {
            LErr(@"无法读取公钥内容");
            return nil;
        }
        SecPolicyRef myPolicy = SecPolicyCreateBasicX509 ();
        SecTrustRef myTrust;
        OSStatus status = SecTrustCreateWithCertif icates(myCertificate,myPolicy,&myTrust);
        SecTrustResultType trustResult;
        if (status == noErr) {
            status = SecTrustEvaluate(myTrust, &trustResult);
        }else{
            return nil;
        }
        _public_key = SecTrustCopyPublicKey(myTrust);
        CFRelease(myCertificate);
        CFRelease(myPolicy);
        CFRelease(myTrust);
    }
    return _public_key;
}

- (NSData*) rsaEncrypt:(NSData*) data{
    SecKeyRef key = [self getPublicKey];
    if (key == nil) {
        return nil;
    }
    size_t cipherBufferSize = SecKeyGetBlockSize(key);
    uint8_t* cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    size_t blockSize = cipherBufferSize - 11;
    size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
    NSMutableData *encryptedData = [[NSMutableData alloc] init];
    for (int i=0; i
        int bufferSize = (int)MIN(blockSize,[data length] - i * blockSize);
        NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
        OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes],
                                        [buffer length], cipherBuffer, &cipherBufferSize);
        if (status == noErr){
            NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
            [encryptedData appendData:encryptedBytes];
        }else{
            if (cipherBuffer) free(cipherBuffer);
            return nil;
        }
    }
    if (cipherBuffer) free(cipherBuffer);
    return encryptedData;
}
======================服务端Java私钥解密=======================

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

public class RSADecrpyt {

    public static RSAPrivateKey loadPrivateKeyFromFile(String keyPath)
            throws Exception {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream(
                    keyPath)));
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(line);
                    sb.append('\r');
                }
            }
        } finally {
            if (br != null) {
                br.close();
            }
        }
        return loadPrivateKey(sb.toString());
    }

    public static RSAPrivateKey loadPrivateKey(String privateKeyStr)
            throws Exception {
        Base64 decoder = new Base64();
        byte[] buffer = decoder.decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    }

    public static String decrypt(Cipher cipher, byte[] encryptData)
            throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream(
                encryptData.length);
        int BLOCK_SIZE = 128;
        for (int i = 0; i < encryptData.length; i += BLOCK_SIZE) {
            int leftBytes = encryptData.length - i;
            int length = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
            out.write(cipher.doFinal(encryptData, i, length));
        }
        return new String(out.toByteArray(), "UTF-8");
    }

    public static void decryptToFile(Cipher cipher, byte[] encryptData,
            String outFile) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream(
                encryptData.length);
        int BLOCK_SIZE = 128;
        for (int i = 0; i < encryptData.length; i += BLOCK_SIZE) {
            int leftBytes = encryptData.length - i;
            int length = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
            byte[] result = cipher.doFinal(encryptData, i, length);
            out.write(result);
        }
        CommonUtil.writeFileContents(outFile, out.toByteArray());
    }

    public static void main(String[] args) throws Exception {
        RSAPrivateKey key = loadPrivateKeyFromFile("pkcs8_private_key.pem");
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, key);
        try {
            byte[] encryptedText = CommonUtil.readFileContents("java_out.bin");
            System.out.println(decrypt(cipher, encryptedText));
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("=============================");
        try {
            byte[] encryptedText = CommonUtil.readFileContents("ios_out.bin");
            System.out.println(decrypt(cipher, encryptedText));
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("=============================");
        try {
            byte[] encryptedText = CommonUtil.readFileContents("qt_out.bin");
            decryptToFile(cipher, encryptedText, "qt_after.txt");
            System.out.println(decrypt(cipher, encryptedText));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值