Java服务端使用RSA私钥加密,客户端使用公钥解密(C#和C语言)

Java服务端使用私钥加密信息,然后C#和C使用公钥解密确认信息。数据的传输使用base64编码。

生成密钥

可以使用支付宝的工具生成公私钥,可以同时生成常规公私钥及pkcs8编码私钥(java需要)。

服务端使用私钥加密信息(java/kotlin

import sun.misc.BASE64Decoder
import sun.misc.BASE64Encoder
import java.io.IOException
import java.security.KeyFactory
import java.security.NoSuchAlgorithmException
import java.security.PrivateKey
import java.security.PublicKey
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher

object RSATool {

    private fun decrypt(encrypted: ByteArray): String {
        val cipher = Cipher.getInstance(RSA)
        cipher.init(Cipher.DECRYPT_MODE, loadPublicKey(PUB_KEY))
        var offset = 0;
        var resultArray = ByteArray(0)
        while (offset < encrypted.size) {
            val needDecryptCount = encrypted.size - offset
            val count = if (needDecryptCount > 128) 128 else needDecryptCount
            resultArray += cipher.doFinal(encrypted, offset, count)
            offset += count
        }
        return resultArray.toString(Charsets.UTF_8)
    }

    fun encrypt(msg: String): String {
        val cipher = Cipher.getInstance(RSA)
        cipher.init(Cipher.ENCRYPT_MODE, loadPrivateKey(PRIVATE_KEY))
        val array = msg.toByteArray()
        var offset = 0
        var result = ByteArray(0)
        while (offset < array.size) {
            val needEncryptCount = array.size - offset
            val count = if (needEncryptCount > 117) 117 else needEncryptCount
            result += cipher.doFinal(array, offset, count)
            offset += count
        }
        return result.toBase64String()
    }

    @Throws(Exception::class)
    private fun loadPublicKey(publicKeyStr: String): PublicKey {
        try {
            val buffer = base64Decode(publicKeyStr)
            val keyFactory = KeyFactory.getInstance(RSA)
            val keySpec = X509EncodedKeySpec(buffer)
            return keyFactory.generatePublic(keySpec)
        } catch (e: NoSuchAlgorithmException) {
            throw RuntimeException(e)
        } catch (e: InvalidKeySpecException) {
            throw RuntimeException(e)
        }
    }

    @Throws(Exception::class)
    private fun loadPrivateKey(privateKeyStr: String): PrivateKey {
        try {
            val buffer = base64Decode(privateKeyStr)
            val keySpec = PKCS8EncodedKeySpec(buffer)
            val keyFactory = KeyFactory.getInstance(RSA)
            return keyFactory.generatePrivate(keySpec)
        } catch (e: NoSuchAlgorithmException) {
            throw RuntimeException(e)
        } catch (e: InvalidKeySpecException) {
            throw RuntimeException(e)
        }
    }

    public fun base64Encode(data: ByteArray): String {
        return BASE64Encoder().encode(data)
    }

    @Throws(IOException::class)
    public fun base64Decode(data: String): ByteArray {
        return BASE64Decoder().decodeBuffer(data)
    }

    fun ByteArray.toBase64String(): String {
        return base64Encode(this)
    }

    private const val RSA = "RSA"

    // 公私钥都去除BEGIN和END行,私钥使用pkcs8格式。
    // 避免泄密问题,这里的公私钥是错误的
    private const val PRIVATE_KEY = "MSdsdfasSDADASDADADASDsdaSDSA\n" +
            "V8znUasdfasw=ASDASDASDASDASDASDASDAS"

    private const val PUB_KEY = "MJibP\n" +
            "SDAASDFAWDASDASDDADASD"

}

客户端使用公钥解密验证信息(C#

使用了BouncyCastle库。

/// <summary>
/// 使用RSA公钥解密
/// </summary>
/// <param name="encrypted">经过base64编码的字符串</param>
/// <returns>解密信息</returns>
private static string Decrypt(string encrypted)
{
    // base64解密
    var bytesToDecrypt = Convert.FromBase64String(encrypted);

    var decryptEngine = new Pkcs1Encoding(new RsaEngine());

    using (var txtreader = new StringReader(PUBLIC_KEY))
    {
        var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();

        decryptEngine.Init(false, keyParameter);
    }

    int length = bytesToDecrypt.Length;
    int blockSize = decryptEngine.GetInputBlockSize();
    List<byte> plainTextBytes = new List<byte>();
    for (int chunkPosition = 0;
        chunkPosition < length;
        chunkPosition += blockSize)
    {
        int chunkSize = Math.Min(blockSize, length - chunkPosition);
        plainTextBytes.AddRange(decryptEngine.ProcessBlock(
            bytesToDecrypt, chunkPosition, chunkSize
        ));
    }
    return Encoding.UTF8.GetString(plainTextBytes.ToArray());
}

private const string PUBLIC_KEY =  @"-----BEGIN PUBLIC KEY-----
            asdfasfasfasfasdfasdasfafasdasfasfasdfasfasdfasfasd
            -----END PUBLIC KEY-----";

底层使用公钥解密验证信息(C

解密使用openssl的库,在Windows上需要先编译,在VS中设置库引用和包含引用,可以参考知乎CSDN

Base64工具类

测试过几个版本,但二进制数据解析不正确。本版本主要基于苹果开源的代码,修复了换行符导致解析失败的问题。

#include <stdlib.h>
#include <string.h>

char * RemoveCharFromStr(char * str, char target);
int Base64DecodeLen(const char *bufcoded);
int Base64Decode(char *bufplain, const char *bufcoded);
int Base64EncodeLen(int len);
int Base64Encode(char *encoded, const char *string, int len);
#include "Base64.h"

static const unsigned char pr2six[256] =
{
    /* ASCII table */
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  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, 64, 64, 64, 64, 64,
    64, 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, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};

char * RemoveCharFromStr(char * str, char target) {
    int i, j, L, n = 0;
    L = strlen(str);
    char * result = malloc(L);
    int index = 0;
    for (i = 0;i <= L - n;i++) {
        if (str[i] != target) {
            result[index] = str[i];
            index++;
        }
    }
    return result;
}

int Base64DecodeLen(const char *bufcoded)
{
    int nbytesdecoded;
    register const unsigned char *bufin;
    register int nprbytes;

    bufin = (const unsigned char *)bufcoded;
    while (pr2six[*(bufin++)] <= 63);

    nprbytes = strlen(bufcoded) - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    return nbytesdecoded + 1;
}

int Base64Decode(char *bufplain, const char *bufcoded)
{
    int nbytesdecoded;
    register const unsigned char *bufin;
    register unsigned char *bufout;
    register int nprbytes;

    bufin = (const unsigned char *)bufcoded;
    while (pr2six[*(bufin++)] <= 63);
    int a = *(bufin);
    nprbytes = strlen(bufcoded) - 1;

    nbytesdecoded = ((nprbytes + 3) / 4) * 3;

    bufout = (unsigned char *)bufplain;
    bufin = (const unsigned char *)bufcoded;

    while (nprbytes > 4) {
        *(bufout++) =
            (unsigned char)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
        *(bufout++) =
            (unsigned char)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
        *(bufout++) =
            (unsigned char)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    /* Note: (nprbytes == 1) would be an error, so just ingore that case */
    if (nprbytes > 1) {
        *(bufout++) =
            (unsigned char)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
    }
    if (nprbytes > 2) {
        *(bufout++) =
            (unsigned char)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
    }
    if (nprbytes > 3) {
        *(bufout++) =
            (unsigned char)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
    }

    *(bufout++) = '\0';
    nbytesdecoded -= (4 - nprbytes) & 3;
    return nbytesdecoded;
}

static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int Base64EncodeLen(int len)
{
    return ((len + 2) / 3 * 4) + 1;
}

int Base64Encode(char *encoded, const char *string, int len)
{
    int i;
    char *p;

    p = encoded;
    for (i = 0; i < len - 2; i += 3) {
        *p++ = basis_64[(string[i] >> 2) & 0x3F];
        *p++ = basis_64[((string[i] & 0x3) << 4) |
            ((int)(string[i + 1] & 0xF0) >> 4)];
        *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
            ((int)(string[i + 2] & 0xC0) >> 6)];
        *p++ = basis_64[string[i + 2] & 0x3F];
    }
    if (i < len) {
        *p++ = basis_64[(string[i] >> 2) & 0x3F];
        if (i == (len - 1)) {
            *p++ = basis_64[((string[i] & 0x3) << 4)];
            *p++ = '=';
        }
        else {
            *p++ = basis_64[((string[i] & 0x3) << 4) |
                ((int)(string[i + 1] & 0xF0) >> 4)];
            *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
        }
        *p++ = '=';
    }

    *p++ = '\0';
    return p - encoded;
}

公钥解码

RSADecoder.h

#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>

void PrintLastError(char* msgTitle);
int DecryptByPublicKey(unsigned char* enc_data, int data_len, unsigned char* key, unsigned char*decrypted);
RSA * CreateRSA(unsigned char* key, int public);
int Test(const char * base64Str, unsigned char * decrypted);

RSADecoder.c

#include "RSADecoder.h"
#include "Base64.h"

RSA * CreateRSA(unsigned char* key, int public)
{
    RSA *rsa = NULL;
    BIO *keybio;
    keybio = BIO_new_mem_buf(key, -1);
    if (keybio == NULL)
    {
        printf("创建RSA失败(BIO)。");
        return 0;
    }

    if (public)
    {
        rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
    }
    else
    {
        rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
    }

    if (rsa == NULL)
    {
        printf("创建RSA失败。");
    }
    return rsa;
}

int DecryptByPublicKey(unsigned char* enc_data, int data_len, unsigned char* key, unsigned char*decrypted)
{
    RSA * rsa = CreateRSA(key, 1);
    int length = RSA_public_decrypt(data_len, enc_data, decrypted, rsa, RSA_PKCS1_PADDING);
    return length;
}

void PrintLastError(char* msgTitle)
{
    char* err = malloc(130);;
    ERR_load_crypto_strings();
    ERR_error_string(ERR_get_error(), err);
    printf("%s: %s\n", msgTitle, err);
    free(err);
}

int Test(const char * base64Str,unsigned char * decrypted) {
    char * publicKey = "-----BEGIN PUBLIC KEY-----\n"
        "asdfadfasfafasdfasfasdfsfsadfasdafasdfasfas"
        "-----END PUBLIC KEY-----\n";
    unsigned char encrypted[4096];
    memset(encrypted, 0, 4096);

    char* encData = RemoveCharFromStr(base64Str, '\n');
    int len = Base64Decode(encrypted, encData);
    int length = DecryptByPublicKey(encrypted, len, publicKey, decrypted);
    return length;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容