我是在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::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_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 = 0i <sourceDataLeni += 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 = 0i <rawText.lengthi += 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 = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData)
if (myCertificate == nil) {
LErr(@"无法读取公钥内容")
return nil
}
SecPolicyRef myPolicy = SecPolicyCreateBasicX509()
SecTrustRef myTrust
OSStatus status = SecTrustCreateWithCertificates(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=0i
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
}
}
1. 如果后台是java,就将私钥转成PKCS8格式的,转换方法网上一大把,最简单就是用支付宝官网的 RSA签名验签工具 咯,诺,直接把私钥弄进来点转PKCS8带走
2. 如果后台是非java,就将不能这么干了,这个时候就得把支付宝demo里的RSADataSigner工具里formatPrivateKey方法改改,改成-----BEGIN RSA PRIVATE KEY-----\n 支付宝demo里是-----BEGIN PRIVATE KEY-----\n,END同理
OK 解决