I'm looking to convert some C# code to the equivalent in Java.
The C# code takes some string content, and a signature (generated using the private key, on a seperate machine) and combined with the public key it verifies the signature matches, providing a level of assurance that the request has not been tampered with.
public bool VerifySignature(string content, byte[] signatureBytes, AsymmetricAlgorithm publicKey)
{
var hash = new MD5CryptoServiceProvider();
byte[] dataBuffer = Encoding.ASCII.GetBytes(content);
var cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write);
cs.Write(dataBuffer, 0, dataBuffer.Length);
cs.Close();
var deformatter = new RSAPKCS1SignatureDeformatter(publicKey);
deformatter.SetHashAlgorithm("MD5");
return deformatter.VerifySignature(hash, signatureBytes);
}
The public key itself is an X509 Certificate - constructed from a .cer file, stored as assembly resource i.e.
byte[] data; // data is read from a resource stream.
var publicKey = new X509Certificate2(data, "", X509KeyStorageFlags.MachineKeySet).PublicKey.Key
What I'm looking to do is emulate this functionality in Java, so I can verify the signature generated by some code in C#... I've started investigating the crypto functionality of Java, but I'm a bit of a java noob. Here's what I've come up with so far:
byte[] certContents=null;
byte[] signature=null;
String contents = "abc";
// load cert
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certContents));
// grab public key
RSAPublicKey publicKey = (RSAPublicKey)cert.getPublicKey();
// get sha1 hash for contents
Mac mac = Mac.getInstance("HmacSHA1");
mac.update(contents.getBytes());
byte[] hash = mac.doFinal();
// get cipher
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
// verify signature of contents matches signature passed to method somehow (and this is where I'm stuck)
Can anyone provide any insight into how I can verify the signature - or provide links to some resources which might explain the java.crypto and java.security.cert usage better then the run of the mill java docs.
That C# code looks really confusing to me. It use SHA1CryptoServiceProvider but uses MD5 hash so I can't tell which hashing algorithm it's using. I assume it's MD5.
The signature verification process involves padding so your code wouldn't work. Following is some snippet from my code and you can use it to verify the signature. data is the bytes to sign and sigBytes holds the signature.
String algorithm = "MD5withRSA";
// Initialize JCE provider
Signature verifier = Signature.getInstance(algorithm);
// Do the verification
boolean result=false;
try {
verifier.initVerify(cert); // This one checks key usage in the cert
verifier.update(data);
result = verifier.verify(sigBytes);
}
catch (Exception e) {
throw new VerificationException("Verification error: "+e, e);
}
Related
I have some code that creates a digital signature. The message as well as the signature is passed from one system to another. When its received, the signature is verified. This code has been run through Fortify, a service that analyzes code for security vulnerabilities. Fortify is reporting that "The method CreateDigitalSignature() in RSACryptography.cs performs public key RSA encryption without OAEP padding".
I see a parameter on the RSACryptoServiceProvider.Encrypt() method that if true, means to use OAEP padding. But I'm not using Encrypt(). I'm using a RSAPKCS1SignatureFormatter to generate and a RSAPKCS1SignatureDeformatter to verify the signature. So my question is how do I add the padding? Am I supposed to Encrypt the signature before sending it back? See my code where I have marked "IS WHAT I NEED TO DO" where I have added Encrypt and Decrypt calls. Is that what I need to do or something else?
// create a digital signature
// returns true if successful. Also, the public key (as an xml string) that can be sent to the other party to verify messages sent
public bool CreateDigitalSignature(string msgToSend, out string publicKey, out string signature)
{
bool rc = false;
publicKey = null;
signature = null;
try
{
// get the hash of the message to send
byte[] hashValue = GetHashedBytes(msgToSend);
// Load or generate a public/private key pair.
// If it already exists in the key container it will be loaded, otherwise, a new key pair is created
CspParameters cp = new CspParameters();
cp.KeyContainerName = KeyStoreContainerName;
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(cp);
// get some info about the key:
CspKeyContainerInfo info = new CspKeyContainerInfo(cp);
//Create an RSAPKCS1SignatureFormatter object and pass it the RSACryptoServiceProvider to transfer the private key.
RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
// Set the hash algorithm
RSAFormatter.SetHashAlgorithm(hashAlgorithm); // "SHA256", "SHA1", etc.
//Create a signature for the hashed value of the data
byte[] signedHashValue = RSAFormatter.CreateSignature(hashValue);
// fortify says I need to use OAEP padding
// IS THIS WHAT I NEED TO DO? Encrypt the signature before I convert it to a string?
signedHashValue = RSA.Encrypt(signedHashValue, true);
// convert the signature to a string
signature = Convert.ToBase64String(signedHashValue);
// get the public key to return so it can be pased to the receiver and used to verify the signature
// There are two ways - either export the parameters or create an xml string
// Using export: This gets public key inforation only (specify true to get both public and private keys)
// RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
// get a string value of the public key
publicKey = RSA.ToXmlString(false);
// demonstration only. how to get the private key
//string privateKey = RSA.ToXmlString(true);
rc = true;
}
catch (Exception ex)
{
throw ex;
}
return rc;
}
And then to verify the signature:
public bool VerifySignature(string origintalData, string publicKeyXml, string signature)
{
bool verified = false;
try
{
// get the hashed value of the original data. used the specified algoritm stored in the this class
byte[] hashValue = GetHashedBytes(origintalData);
// get a byte array of the signature
byte[] signaturebytes = Convert.FromBase64String(signature);
// create a crypto provider
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
// set the public key of the crypto service provider
RSA.FromXmlString(publicKeyXml);
// create a deformatter
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);
// set the hash algorithm. The sender must use the same algorithm
RSADeformatter.SetHashAlgorithm(hashAlgorithm);
// As per Fortify, need to use OAEP padding
// IS THIS WHAT i NEED TO DO - decrypt the signature before veryfying it?
signaturebytes = RSA.Decrypt(signaturebytes, true);
// verify the signature
verified = RSADeformatter.VerifySignature(hashValue, signaturebytes);
}
catch (Exception ex)
{
throw ex;
}
return verified;
}
Update:
After upgrading from .Net 4.5.1 to 4.6.1. I can use
byte[] signedHashValue = RSA.SignData(bytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
and the corresponding method
RSA.VerifyData(System.Text.Encoding.UTF8.GetBytes(originalData), signaturebytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
but I can't specify RSASignaturePadding.Pss. If I do I get an exception "Specified padding mode is not valid for this algorithm."
Also I get the same signature as before, so I feel I haven't really accomplished anything. No way to use AOEP padding?
I am using the following code to successfully produce a signature for my SAML. I have the POST sorted using XML sign, however REDIRECT is completely different. I am unable to produce the same signature as https://www.samltool.com/sign_logout_req.php and when I try and validate a genuine signature it is failing.
I took it back to basics and tried to see if I could sign it in the same manner and I cannot which suggests something is wrong with the way I am forming the data.
The following details (into samltool.com):
<saml:LogoutRequest ID="_02380F63816E0E92D6537758C37FE05F" Version="2.0" IssueInstant="2017-06-21T15:34:59.911Z" Destination="https://myteststs.net/appname/auth/" xmlns:saml="urn:oasis:names:tc:SAML:2.0:protocol"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://w.sp-app.com</saml:Issuer><saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">1869374313</saml:NameID></saml:LogoutRequest>
Private key (testcert)
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL13oqZ+pNa/fYIc+GYBrENOQ9rKWMeQEL9iDJyj7DFrQA40jOCY1UiOT2uLYvIwfqTHMuPmmlOLSyqCumZbKYP6KIM0pe+vJcJO6Nxg81gmN3jx3GbnDsmhi54oAmj3JC/Z/WbliqUXjlIAUlzLmMll7/vy2V5ec/gxHBpuRWBjAgMBAAECgYAWiWn/+vV5k9qGhRKo8479jIw2tLX9uu27Dhso8LiyIitC5U5Skutfz4mz5acV3t3ZlNZBVJdL07hTrKwma7aSx1r6UwTtW002ZZzytEVn7G7ytOIXkT+q/TuooCR8aa88vwhUFPqCSOuZgOPH9ytqAkzDCaNgVKdhQgRgjxfOBQJBAOSu4t5AgFJUBOcYOEOm6+v8R1CqedfyOgya3g1gsA4VnuG+ms233ZxWSPkiMnoUpEh8gBnZyk6ZZSlk668rwBcCQQDUGYg7wVLqhPAyjfM74oaJgohyQfQK6rPnzlKoGbdDR0QRN545ATBsETi2GSIYAHgkgLDJw3/lw1wX1dXzFuWVAkABmR9IwlajPKcUHl02S9JWQdsVuztCwRSaxfJLUaOpVYlYtoZKbcCEuS2lYBHOPJqxTv1uMNFzHytP0L686KddAkART6gr4GKJG6KTLbzNhXafoJTMZo+pmHBomhFrAPZROm7WzOhQFMXD/D/ZtQFwXhFwQUSsoxU8Ro6sr1pQBe1lAkBlXndo3Bm6AITDDsJZYg10XiBMNj4743t0pV6jayf9UTRZHu2GI9AWoU3/FTQt34zbPz6TjlNuJnwMHwfCFk1F
x.509
MIICMTCCAZqgAwIBAgIQcuFBQn5d27JBvbkCO+utKjANBgkqhkiG9w0BAQUFADBXMVUwUwYDVQQDHkwAewAyAEYAOAA3ADkANQA4ADUALQA3AEMANQA0AC0ANAA1ADAARAAtADgAOABGAEIALQBBADMARgA3ADEAMwA2ADQANgBFAEMANgB9MB4XDTE2MDEwODExMTU0OFoXDTE3MDEwNzE3MTU0OFowVzFVMFMGA1UEAx5MAHsAMgBGADgANwA5ADUAOAA1AC0ANwBDADUANAAtADQANQAwAEQALQA4ADgARgBCAC0AQQAzAEYANwAxADMANgA0ADYARQBDADYAfTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvXeipn6k1r99ghz4ZgGsQ05D2spYx5AQv2IMnKPsMWtADjSM4JjVSI5Pa4ti8jB+pMcy4+aaU4tLKoK6Zlspg/oogzSl768lwk7o3GDzWCY3ePHcZucOyaGLnigCaPckL9n9ZuWKpReOUgBSXMuYyWXv+/LZXl5z+DEcGm5FYGMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBDXaccjXBrBhxp1fcEDm7MotKvgh8DxQAACk/Uxb4r2R6+LcePUxQTcxWVmyCQO0NR017FRf/fLFHmM9HZI3lwx5ka4xBnSOu8mejQ0KOYt4yf2VQG6pWGa046Ntip+KB/yDQKXQ3RHprsshe33MFlEWpDJyo6jyDpDUqLjPBvtg==
RelayState:
RELAYTEST
SigAlg:
#rsa-sha1
So... using the ssotool it produces the signature :
IG4VDmVwQRZWa75NmwjtqKlPVdCx6tm73gL7j3xvrqXsfirunUtr626SBmQJ4mke77bYzXg8D1hAy5EREOhz2QH23j47XexqbVSNTtAkZV7KP1/lO8K01tiQr8SGJqzdFor/FZZscIDFlw3cBLXhGSwWK9i0qO/e55qkgxJS9OA=
However.. using the code below (and many.. many.. variations) I cannot get it to produce that same signature. Please note the samlrequest is base64encoded with compression as recommended (but you can deflate it to the same output). I have followed this specification too (3.4.4.1):https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
static byte[] Sign(string data, string certSubject)
{
// Access Personal (MY) certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("No valid cert was found");
}
string certAlgorithm = csp.SignatureAlgorithm;
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] dataRaw = encoding.GetBytes(data);
byte[] hash = sha1.ComputeHash(dataRaw);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
static bool Verify(string text, byte[] signature, string certPublic)
{
// Load the certificate we’ll use to verify the signature from a file
X509Certificate2 appSigningX509Certificate = null;
var appSigningCertificateBytes = Convert.FromBase64String(certPublic);
appSigningX509Certificate = new X509Certificate2(appSigningCertificateBytes);
// Get its associated CSP and public key
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)appSigningX509Certificate.PublicKey.Key;
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
static void Main(string[] args)
{
// Usage sample
try
{
string cert = "MIICMTCCAZqgAwIBAgIQcuFBQn5d27JBvbkCO+utKjANBgkqhkiG9w0BAQUFADBXMVUwUwYDVQQDHkwAewAyAEYAOAA3ADkANQA4ADUALQA3AEMANQA0AC0ANAA1ADAARAAtADgAOABGAEIALQBBADMARgA3ADEAMwA2ADQANgBFAEMANgB9MB4XDTE2MDEwODExMTU0OFoXDTE3MDEwNzE3MTU0OFowVzFVMFMGA1UEAx5MAHsAMgBGADgANwA5ADUAOAA1AC0ANwBDADUANAAtADQANQAwAEQALQA4ADgARgBCAC0AQQAzAEYANwAxADMANgA0ADYARQBDADYAfTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvXeipn6k1r99ghz4ZgGsQ05D2spYx5AQv2IMnKPsMWtADjSM4JjVSI5Pa4ti8jB+pMcy4+aaU4tLKoK6Zlspg/oogzSl768lwk7o3GDzWCY3ePHcZucOyaGLnigCaPckL9n9ZuWKpReOUgBSXMuYyWXv+/LZXl5z+DEcGm5FYGMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBDXaccjXBrBhxp1fcEDm7MotKvgh8DxQAACk/Uxb4r2R6+LcePUxQTcxWVmyCQO0NR017FRf/fLFHmM9HZI3lwx5ka4xBnSOu8mejQ0KOYt4yf2VQG6pWGa046Ntip+KB/yDQKXQ3RHprsshe33MFlEWpDJyo6jyDpDUqLjPBvtg==";
string samlRequestCompressed = "nZFPS8QwEMW/Ssl906TZ/gttQWwLhdWDyh68SKjBLbRJ7ExQv71traA38TiPee/9hilATaM82Rfr8U6/eg0YdHVJnlgkMtYmIuNJw5o8qpNYpGmcXYu0bVjckuCsZxisKUlEGQk6AK87A6gMLhLj6YElh4g/8FiKo4xzmnP+SIJ6aRiMws15QXQgwxC1Uab/MGrSdFBTP1r/TI3GUDm3iqE7KI+XkATv02hArtAl8bORVsEAct0Bib28v7o5yYVHutmi7e1IqmK7cMOb/2xXAHpeGUn1zfhGYaFwjvZ2KsIfoXvD7RLS1f9p4FmSi/QouNhzv6Kqffr1nOoT";
string relaystate = "RELAYTEST";
string algorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
string data = String.Empty;
if (String.IsNullOrEmpty(relaystate))
{
data = String.Format("SAMLRequest={0}&SigAlg={1}", HttpUtility.UrlEncode(samlRequestCompressed), HttpUtility.UrlEncode(algorithm));
}
else
{
data = String.Format("SAMLRequest={0}&RelayState={1}&SigAlg={2}", HttpUtility.UrlEncode(samlRequestCompressed,Encoding.UTF8), HttpUtility.UrlEncode(relaystate,Encoding.UTF8), HttpUtility.UrlEncode(algorithm,Encoding.UTF8));
}
// Sign text
byte[] signature = Sign(data, "{2F879585-7C54-450D-88FB-A3F713646EC6}");
string b64encodedSig = Convert.ToBase64String(signature);
string expectedSig = "IG4VDmVwQRZWa75NmwjtqKlPVdCx6tm73gL7j3xvrqXsfirunUtr626SBmQJ4mke77bYzXg8D1hAy5EREOhz2QH23j47XexqbVSNTtAkZV7KP1/lO8K01tiQr8SGJqzdFor/FZZscIDFlw3cBLXhGSwWK9i0qO/e55qkgxJS9OA=";
if (b64encodedSig != expectedSig)
{
Console.WriteLine("Not what i expected");
Environment.Exit(0);
}
// Verify signature. Testcert.cer corresponds to “cn=my cert subject”
if (Verify(data, signature, cert))
{
Console.WriteLine("Signature verified");
}
else
{
Console.WriteLine("ERROR: Signature not valid!");
}
}
catch (Exception ex)
{
Console.WriteLine("EXCEPTION: " +ex.Message);
}
Console.ReadKey();
}
I cannot seem to understand how to produce the sign data in the same way. I have also confirmed the locally installed certificate is exactly the same as the one above.
I have resolved this now.
The solution was that the HttpUtility.URLEncode is not encoding it to the same standard as the SAML standards (or OneLogin). I figured it out by looking at the compressed data and that matched but the URL Encoded one different.
The answer was to use Uri.EscapeString.
In order to get the same signature as OneLogin, you have to use the same URL encoding as them. Other URL encodings will result in different signatures, but they are perfectly valid as well.
See the SAML specification (3.4.4.1):
Further, note that URL-encoding is not canonical; that is, there are multiple legal encodings for a given value. The relying party MUST therefore perform the verification step using the original URL-encoded values it received on the query string. It is not sufficient to re-encode the parameters after they have been processed by software because the resulting encoding may not match the signer's encoding.
As noted in another answer to this question, OneLogin seems to use URL encoding matching System.Net.WebUtility.UrlEncode() in .NET -- but note also that they do not URL encode the base64-encoded signature itself, even though it can contain characters such as [+/=]. The SAML specification is not very clear, but seems to suggest that this is wrong,
Note that some characters in the base64-encoded signature value may themselves require URL-encoding before being added.
This last bit is crucial to be able to validate your signature with their tools.
I have a keypair generated using openSSL in C++ which i am using to sign a message for authentication over a C# server which strictly uses RSACryptoServiceProvider(no BouncyCastle etc.). I have used PKCS#1 SHA256 to generate signature.The signature is then transported in hexadecimal form along with the public key. The problem is the signature fails to verify on the server.
I have tried removing the header which says "-----BEGIN RSA PUBLIC KEY-----" and so. But no results yet.The c++ code for generating keypair and signature is:
RSA *keypair = NULL;
bne = BN_new();
int ret = 0;
ret = BN_set_word(bne, ea);
keypair = RSA_new();
RSA_generate_key_ex(keypair , 2048, bne, NULL);
SHA256((unsigned char*)msg, strlen(msg) + 1, hash);
RSA_sign(NID_sha256, hash, SHA256_DIGEST_LENGTH, sign,&signLen, keypair);
I have collected the public key in a character buffer using BIO and converted the signature data to hex string for transporting to c#. I however tried to replicate the server by writing a sample form application and the signature and public key which are verified on c++,i have passed them hardcoded as inputs to c# sample.But they also fail.
Can anyone help me out what are the possible reasons or areas of fallacy?
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.ImportParameters(rsParam);
UTF8Encoding encoding = new UTF8Encoding();
byte[] datax = encoding.GetBytes(data);
byte[] sigx = encoding.GetBytes(sig);
SHA256Managed sha256 = new SHA256Managed();
byte[] hash = sha256.ComputeHash(datax);
iRes= RSA.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), sigx);
The 'data' is passed and also the 'sig' which is a hard-coded value,'rsParam have been created using hard-coded public key used to generate 'sig'. The code for conversion of public key to rsParam is:
pubkey = Convert.FromBase64String(pemString);
RSAParameters rsParam = RSA.ExportParameters(false);
rsParam .Modulus = pubkey;
RSA.ImportParameters(rsParam );
I wrote this code just to know how the basic C# cryptography system works,so that it can be of some help to research on the actual issue.
I found the answer,instead of signing with RSA_Sign,i used RSA_private_encrypt with RSA_PKCS1_PADDING as a parameter to sign the SHA256 hash and the signature got verified on the server end,the server code as i mentioned earlier was not transparent to me so this was just a brute-force approach and it worked. The sample C# code i had written still doesnt verify it but it is not of any concern to me as for now.
I'm having trouble finding a good example in encrypting / decrypting strings in C# using a certificate. I was able to find and implement an example of signing and validating a signature, as shown below. Could someone point me to an easy, similar example for encryption?
private static string Sign(RSACryptoServiceProvider privateKey, string content)
{
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding ();
byte[] data = encoding.GetBytes(content);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
var signature = privateKey.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
return Convert.ToBase64String(signature);
}
public static bool Verify(RSACryptoServiceProvider publicKey, string content, string hashString)
{
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding ();
byte[] data = encoding.GetBytes(content);
byte[] hash = sha1.ComputeHash(data);
return publicKey.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), Convert.FromBase64String(hashString));
}
Per the .NET Framework team's guidance (have to search for "Cryptography Updates", there doesn't seem to be an anchor nearby -- or, just look at the code samples).
public static byte[] EncryptDataOaepSha1(X509Certificate2 cert, byte[] data)
{
// GetRSAPublicKey returns an object with an independent lifetime, so it should be
// handled via a using statement.
using (RSA rsa = cert.GetRSAPublicKey())
{
// OAEP allows for multiple hashing algorithms, what was formermly just "OAEP" is
// now OAEP-SHA1.
return rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA1);
}
}
Decrypt would thus be
public static byte[] DecryptDataOaepSha1(X509Certificate2 cert, byte[] data)
{
// GetRSAPrivateKey returns an object with an independent lifetime, so it should be
// handled via a using statement.
using (RSA rsa = cert.GetRSAPrivateKey())
{
return rsa.Decrypt(data, RSAEncryptionPadding.OaepSHA1);
}
}
Caveats:
RSA.Encrypt(byte[], RSAEncryptionPadding) was added in .NET Framework 4.6 (and .NET Core 1.0 / .NET Standard 1.3), so make sure you are building a project with a high enough target version.
RSA encryption is mainly used to encrypt symmetric keys, not actual data payloads, because it is expensive and has a size limit (always lower than the keysize (in bytes), the different padding modes consume different amounts of available space).
While the RSA base class talks about OaepSHA256 (etc) only Pkcs1 and OaepSHA1 are supported by all providers in .NET Core. (OaepSHA256+ is limited to RSACng)
I got a module which RSA encrypts the data and passes on to the C#.
C# needs to decrypt it based on the public key (64 bit encoded) and the passed token.
I have token , 64 bit encoded public key, can some help me get with the sample to get started.
All I know from Java end is, it is using. I have got the result from Java end and need to write a parser in C# to decrypt this. I get both public key and token as a string value.
Cipher cipher = Cipher.getInstance(ALGORITHM); //Algorithm = "RSA"
cipher.init(Cipher.DECRYPT_MODE, key);
Thanks
To get started, you'll need the private key to decrypt the message. By "public key (64 bit encoded)", I'm guessing what you really have is a Base-64–encoded certificate, with a header line that says "----- BEGIN CERTIFICATE-----" and a footer that says "-----END CERTIFICATE-----".
If that's correct, you'll need to find the private key. This is sometimes stored in a PKCS #12 format file, with a ".p12" or ".pfx" extension. You'll need a password to access the private key if it is stored in such a file.
Alternatively, OpenSSL and other utilities use private key files that can be Base-64–encoded or binary. These have a variety of extensions, and may or may not be password-protected. If the file that you have has a header line of "-----BEGIN RSA PRIVATE KEY-----" or "-----BEGIN PRIVATE KEY-----", that is actually the private key.
Finally, Windows can store private keys in its internal key store.
When you clarify the location of the private key, please update your question.
If the private key is used on the Java side, it may be an attempt to perform a digital signature. While all of several Java providers I've tested produce correct results when (ab)used this way, if you are doing a signature, the Signature class should be used. The C# code should use a signature object to "verify" the signature as well.
Encryption is performed with the private key. Since the public key is public, anyone can decrypt the message; i.e., the message is not confidential. Public keys are used by recipients to verify signed messages.
Check this code out.
public static string Decrypt(string inputText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] encryptedData = Convert.FromBase64String(inputText.Replace(" ","+"));
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(ENCRYPTION_KEY, SALT);
using (ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
{
using (MemoryStream memoryStream = new MemoryStream(encryptedData))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainText = new byte[encryptedData.Length];
int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
return Encoding.Unicode.GetString(plainText, 0, decryptedCount);
}
}
}