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

来源:互联网 发布:软件被劫持 编辑:程序博客网 时间:2024/05/29 19:17
      有了之前三个终端进行AES加解密的经验,实现RSA加解密倒不是太复杂。两个关键点:1)RSA需要密钥文件,且密钥文件的格式很重要;2)padding方式需要一致,比较被广泛支持且被很多RAS实现设置为默认padding方式的是PKCS1PADDING V1.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 -outpkcs8_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::stringEvpHelper::rsaEncryptUsingPublicKeyFile(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_PUBKEYfailed.");
}
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_initfailed.");
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <=0)
{
throw std::runtime_error("EVP_PKEY_CTX_set_rsa_paddingfailed.");
}
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 staticRSAPublicKey loadPublicKeyFromFile(String keyPath)
           throwsException {
       BufferedReader br = null;
       StringBuilder sb = new StringBuilder();
       try {
           br = newBufferedReader(new InputStreamReader(new FileInputStream(
                 keyPath)));
           Stringline = 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 staticRSAPublicKey loadPublicKey(String publicKeyStr)
           throwsException {
       Base64 decoder = new Base64();
       byte[] buffer =decoder.decode(publicKeyStr);
       KeyFactory keyFactory =KeyFactory.getInstance("RSA");
       X509EncodedKeySpec keySpec = newX509EncodedKeySpec(buffer);
       return (RSAPublicKey)keyFactory.generatePublic(keySpec);
    }

    public static voidmain(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 = newFileOutputStream("java_out.bin");
           intBLOCK_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 = [NSDatadataWithContentsOfFile:filePath];
       SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault,(__bridge CFDataRef)certificateData);
       if (myCertificate == nil) {
          LErr(@"无法读取公钥内容");
           returnnil;
       }
       SecPolicyRef myPolicy =SecPolicyCreateBasicX509();
       SecTrustRef myTrust;
       OSStatus status =SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
       SecTrustResultType trustResult;
       if (status == noErr) {
           status =SecTrustEvaluate(myTrust, &trustResult);
       }else{
           returnnil;
       }
       _public_key =SecTrustCopyPublicKey(myTrust);
       CFRelease(myCertificate);
       CFRelease(myPolicy);
       CFRelease(myTrust);
    }
    return_public_key;
}

- (NSData*) rsaEncrypt:(NSData*) data{
    SecKeyRef key = [selfgetPublicKey];
    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,[datalength] - i * blockSize);
       NSData *buffer = [datasubdataWithRange: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);
           returnnil;
       }
    }
    if (cipherBuffer)free(cipherBuffer);
    returnencryptedData;
}
======================服务端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 staticRSAPrivateKey loadPrivateKeyFromFile(String keyPath)
           throwsException {
       BufferedReader br = null;
       StringBuilder sb = new StringBuilder();
       try {
           br = newBufferedReader(new InputStreamReader(new FileInputStream(
                 keyPath)));
           Stringline = 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 staticRSAPrivateKey loadPrivateKey(String privateKeyStr)
           throwsException {
       Base64 decoder = new Base64();
       byte[] buffer =decoder.decode(privateKeyStr);
       PKCS8EncodedKeySpec keySpec = newPKCS8EncodedKeySpec(buffer);
       KeyFactory keyFactory =KeyFactory.getInstance("RSA");
       return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
    }

    public static Stringdecrypt(Cipher cipher, byte[] encryptData)
           throwsException {
       ByteArrayOutputStream out = newByteArrayOutputStream(
              encryptData.length);
       int BLOCK_SIZE = 128;
       for (int i = 0; i <encryptData.length; i += BLOCK_SIZE) {
           intleftBytes = 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 voiddecryptToFile(Cipher cipher, byte[] encryptData,
           StringoutFile) throws Exception {
       ByteArrayOutputStream out = newByteArrayOutputStream(
              encryptData.length);
       int BLOCK_SIZE = 128;
       for (int i = 0; i <encryptData.length; i += BLOCK_SIZE) {
           intleftBytes = 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 voidmain(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();
       }
    }
}


0 0
原创粉丝点击