在服务器上,我有一个PHP脚本(Laravel),可以生成RSA密钥对,将它们刷新到会话中,并返回base64编码的公共密钥,该密钥将在我的Windows中使用应用程序来加密密码,并通过调用 openssl_pkey_new 函数创建密钥:
// get RSA key pairs
public function generateRSAKeyPairTest(Request $request)
{
$t = $this->microtime_float();
$res = openssl_pkey_new();
openssl_pkey_export($res, $privkeyraw);
$d= openssl_pkey_get_details($res);
$pubkeyraw = $d['key'];
$pubkey = base64_encode($pubkeyraw);
$privkey = base64_encode($privkeyraw);
$request->session()->flash('privkey', $privkey);
$request->session()->flash('pubkey', $pubkey);
$tdiff = $this->microtime_float() - $t;
$keypair = array('pubkey' => $pubkey,'diff' => $tdiff);
return response()->json($keypair);
}
// decode the encryted string
public function decryptRSATest(Request $request)
{
if ($request->session()->has('privkey'))
{
$privkey = $request->session()->get('privkey', 'default value');
$resPriv = openssl_pkey_get_private(base64_decode($privkey));
$encrypted_text_base64 = $request->encrypted_text;
$outval = '-';
$encrypted_text = base64_decode($encrypted_text_base64);
openssl_private_decrypt($encrypted_text, $outval, $resPriv);
return response()->json(
[
'decoded_text_from_clit'=>$outval
]
);
}
return response()->json(["error"=>"private key does not exist!"]);
}
然后在我的 Windows应用程序(C#)中,我从服务器获取json并检索公钥
var data = Convert.FromBase64String(publicKeyStringBase64);
var publicKeyRaw = Encoding.UTF8.GetString(data);
var pkStr = publicKeyRaw.Replace("-----END PUBLIC KEY-----", "").Replace("-----BEGIN PUBLIC KEY-----", "");
var publicKey = Encoding.UTF8.GetBytes(pkStr);
publicKeyRaw 如下(PKCS#8格式):
----- BEGIN公钥-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwg6U1EET7OSbLO7UUZh 7p8ODYY4kXUd5S1Z / qexG5IqpNflrdQbpVh + 8KWNi83oidAUjWEb050Rl3AuY / E6 7hYlEdUvI9pevmBpjjU1GktzQsDsbva3THHSpTZXPlctnFnuz0b5hVu1nUETmbGF fSbslZet3pbKcK5KGnJpm6v6OQGpvgQjyNWF16HjUD4 / x1rAL2aDNOZZED + FNJcC hNmdK1A8nECk1JoTTdiK7r0EXMWxdVjEaSkAsvi7ywKi7ZESWwS1JmRuIJ5ZPiRx Fvur1tgaiomEZ + 9oDpk1 + bwDenrERYgBxw2L6Rw0CwuyinhwYfIbWkNmy4cAiZVx 科威达 ----- END公钥-----
我尝试使用RSAParameters,但是无论Modulus是原始字节数组(数据)还是从剥离字符串转换后的数组( publicKey ),加密文本都被加密使用RSACryptoServiceProvider的Encrypt方法通过服务器上的 openssl_private_decrypt 不能解密。
var rsaInfo = new RSAParameters()
{
Exponent = new byte[] { 1, 0, 1 },
Modulus = data, // publicKey
};
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaInfo);
var password = "testing-passsword!";
var hash = csp.Encrypt(Encoding.UTF8.GetBytes(password), false);
var encryptedText = Convert.ToBase64String(hash);
我猜想RSACryptoServiceProvider不能正确加密密码,那么将这个密钥字符串(publicKeyRaw)与RSACryptoServiceProvider一起使用的正确方法是什么?
在Google上进行了大量搜索工作后,问题就变得清晰了(与@James Reinstate Monica Polk提到的内容一样): 默认情况下,使用openssl_pkey_new()生成的密钥为 PKCS#8 格式,而RSACryptoServiceProvider仅接受 PKCS#1 v1.5 格式的密钥。这种不兼容导致了这个问题。
那么,您对此有任何解决方案吗?
答案 0 :(得分:0)
我从这篇文章中找到了解决方案:Correctly create RSACryptoServiceProvider from public key
使用 RSAPrameters 类时,我们需要注意很多事情。
-----BEGIN RSA PUBLIC KEY----- base64 encoded data -----END RSA PUBLIC KEY-----
public static RSAParameters GetRSAParametersFromPublicKey(string publicKeyString)
{
// publicKeyString should have following format:
// -----BEGIN RSA PUBLIC KEY---- -
// base64 encoded data
// -----END RSA PUBLIC KEY---- -
var pubicKeyContentString = publicKeyString.Replace("-----BEGIN RSA PUBLIC KEY-----", "")
.Replace("-----END RSA PUBLIC KEY-----", "")
.Replace("\r", "")
.Replace("\n", "")
.Replace(@"\/", "/");
var publicKeyArray = Convert.FromBase64String(pubicKeyContentString);
var mask = 0x7F;
var skipCount = 0;
var rsaParameters = new RSAParameters();
for (int i = 0; i < publicKeyArray.Length; i=skipCount)
{
var tag = publicKeyArray[i];
var lengthLength = publicKeyArray[i + 1];
var length = Convert.ToInt32(lengthLength);
skipCount += 2;
if (lengthLength > mask)
{
var lengthBit = lengthLength & mask;
var lengthBytes = publicKeyArray.Skip(skipCount).Take(lengthBit).ToArray();
skipCount += lengthBit;
length = BitConverter.ToInt16(lengthBytes.Reverse().ToArray(),0);
}
if (tag == 0x02)
{
// both modulus and public exponent start with 0x02: integer
// therefore, 0x02 is the only tag we are interested in
var valueBytes = publicKeyArray.Skip(skipCount).Take(length).ToArray();
if (valueBytes[0] == 0x00)
{
// a valid DER has a leading byte 0x00
rsaParameters.Modulus = valueBytes.Skip(1).ToArray();
}
else
{
rsaParameters.Exponent = valueBytes;
}
skipCount += length;
}
}
return rsaParameters;
}
这是使用输入的base64编码的公钥字符串对输入的plainText进行加密的函数
public static string Cryptography(string plainText, string publicKeyStringBase64)
{
var publicKey = Convert.FromBase64String(publicKeyStringBase64);
var publicKeyRaw = Encoding.UTF8.GetString(publicKey);
var rsaInfo = GetRSAParametersFromPublicKey(publicKeyRaw);
if (rsaInfo.Modulus != null && rsaInfo.Exponent != null)
{
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaInfo);
var hash = csp.Encrypt(Encoding.Unicode.GetBytes(plainText), false);
var encryptedText = HttpUtility.UrlEncode(Convert.ToBase64String(hash));
return encryptedText;
}
return "!!error!!";
}
use phpseclib\Crypt\RSA;
public function decryptRSATest(Request $request)
{
if ($request->session()->has('privkey'))
{
$privkeybase64 = $request->session()->get('privkey', 'default value');
str_replace(['\/', '\n'], ['/', ''], $privkeybase64);
$privkey = base64_decode($privkeybase64);
$rsa = new RSA();
$received = str_replace(['\/', '\n',"\0",'\\'], ['/', '', '',""], $request->encrypted_text);
$rsa->loadKey($privkey);
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
$decrypt_text2 = $rsa->decrypt(base64_decode($received));
$decrypt_text2 = str_replace("\0",'',$decrypt_text2);
return response()->json(
[
'decoded_text_from_clit' => $decrypt_text2
]
);
}
return response()->json(["error"=>"private key does not exist!"]);
}
此外,如果我们只需要模数和指数而不是完整的公钥,则可以将模数和指数传递给客户端(base64_encoded):
// PHP - Server end
$modulus_base64 = base64_encode($rsa->modulus);
$exponent_base64 = base64_encode($rsa->exponent);
在C#中,正确解码它们可能有些困难,这是一个代码段:
// C# - Client end
var modulus = Convert.FromBase64String(modulusStringBase64);
var exponent = Convert.FromBase64String(exponentStringBase64);
var modulusRaw = Encoding.UTF8.GetString(modulus);
var exponentRaw = Encoding.UTF8.GetString(exponent);
var bigIntegerModulus = BigInteger.Parse(modulusRaw);
var bigIntegerExponent = BigInteger.Parse(exponentRaw);
var exponentArray = bigIntegerExponent.ToByteArray();
var modulusArray = bigIntegerModulus.ToByteArray();
和一些有用的在线工具:
希望这对您有帮助。