I've seen a lot of questions, none really provide good answers, or if they try, they assume the one asking the question isn't asking what he actually IS asking. And just for the record, I am NOT asking about signing, encryption or PEM files!
In my case, I'll be receiving a small file of encrypted data. It has been encrypted using the private key of an RSA key pair.
The public key I have available is a DER file in the ASN.1 format, it is not a certificate as such, and it is not signed.
In Java, this is easy, in c# it is a nightmare from what I can tell.
X509Certificate2 claims "Object not found", when I try to parse it the byte array of the key.
The keys were generated with the following openSSL commands:
>openssl genrsa -out private_key.pem 2048
Generating RSA private key, 2048 bit long modulus
...........+++
....................................................................................................
...........................+++
e is 65537 (0x10001)
>openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
>openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
writing RSA key
Edit: The code I'm trying:
private byte[] ReadBytesFromStream(string streamName)
{
using (Stream stream = this.GetType().Assembly.GetManifestResourceStream(streamName))
{
byte[] result = new byte[stream.Length];
stream.Read(result, 0, (int) stream.Length);
return result;
}
}
public void LoadPublicKey()
{
byte[] key = ReadBytesFromStream("my.namespace.Resources.public_key.der");
X509Certificate2 myCertificate;
try
{
myCertificate = new X509Certificate2(key);
}
catch (Exception e)
{
throw new CryptographicException(String.Format("Unable to open key file. {0}", e));
}
}
The ReadBytesFromStream method does return the correct byte stream, the public_key.der file is an embedded resource.
Just to polish this one off. The code below is still a bit messy though, but it does seem to be what I could get to work.
The reason we can't use public keys to decrypt a message in Windows is explained here:
http://www.derkeiler.com/Newsgroups/microsoft.public.dotnet.security/2004-05/0270.html
Next, you need the public key modulus and exponent in the c# code, somewhere, be it in a file or embedded is really not the matter here. I do use Base64 to wrap up the binary keys though. I gave up on generating a key that Windows would actually import. Besides, for what I need, the public key will be an embedded resource.
The last problem I had were that the modulus generated from the key weren't working on Windows (c#). This StackOverflow post did hold the key to solve that one, the modulus in Windows can not contain leading zeroes.
Android in-app billing Verification of Receipt in Dot Net(C#)
The modulus generated from Java didn't work on c#. Use the stripLeadingZeros code from that post to create a working Public key modulus:
private static byte[] stripLeadingZeros(byte[] a) {
int lastZero = -1;
for (int i = 0; i < a.length; i++) {
if (a[i] == 0) {
lastZero = i;
}
else {
break;
}
}
lastZero++;
byte[] result = new byte[a.length - lastZero];
System.arraycopy(a, lastZero, result, 0, result.length);
return result;
}
For starters, this is how I generated the key files:
$ openssl genrsa -out private_key.pem 2048
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
$ openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
The Java Code:
package com.example.signing;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import jcifs.util.Base64;
public class SigningExample {
private static String PRIVATE_KEY_FILE = "private_key.der";
private static String PUBLIC_KEY_FILE = "public_key.der";
/**
* #param args
*/
public static void main(String[] args) {
SigningExample.sign();
}
private static void sign() {
try {
String text = new String(SigningExample.loadFromFile("message.xml"));
String message = Base64.encode(text.getBytes());
RSAPrivateKey privateKey = PrivateRSAKeyReader.getKeyFile(SigningExample.PRIVATE_KEY_FILE);
RSAPublicKey publicKey = PublicRSAKeyReader.getKeyFile(SigningExample.PUBLIC_KEY_FILE);
byte[] modulusBytes = publicKey.getModulus().toByteArray();
byte[] exponentBytes = publicKey.getPublicExponent().toByteArray();
modulusBytes = SigningExample.stripLeadingZeros(modulusBytes);
String modulusB64 = Base64.encode(modulusBytes);
String exponentB64 = Base64.encode(exponentBytes);
System.out.println("Copy these two into your c# code:");
System.out.println("DotNet Modulus : " + modulusB64);
System.out.println("DotNet Exponent: " + exponentB64);
// Testing
byte[] signature = SigningExample.sign(message.getBytes(), privateKey, "SHA1withRSA");
String signedMessage = message + "\n" + Base64.encode(signature);
SigningExample.saveToBase64File("message.signed", signedMessage.getBytes());
System.out.println("\nMessage :\n" + signedMessage);
String[] newkey = new String(SigningExample.loadFromBase64File("message.signed")).split("\\n");
System.out.println("Verified : " + SigningExample.verify(newkey[0].getBytes(), publicKey, "SHA1withRSA", Base64.decode(newkey[1])));
} catch (Exception e) {
e.printStackTrace();
}
}
private static byte[] stripLeadingZeros(byte[] a) {
int lastZero = -1;
for (int i = 0; i < a.length; i++) {
if (a[i] == 0) {
lastZero = i;
} else {
break;
}
}
lastZero++;
byte[] result = new byte[a.length - lastZero];
System.arraycopy(a, lastZero, result, 0, result.length);
return result;
}
private static byte[] sign(byte[] data, PrivateKey prvKey,
String sigAlg) throws Exception {
Signature sig = Signature.getInstance(sigAlg);
sig.initSign(prvKey);
sig.update(data, 0, data.length);
return sig.sign();
}
private static boolean verify(byte[] data, PublicKey pubKey,
String sigAlg, byte[] sigbytes) throws Exception {
Signature sig = Signature.getInstance(sigAlg);
sig.initVerify(pubKey);
sig.update(data, 0, data.length);
return sig.verify(sigbytes);
}
public static void saveToBase64File(String fileName, byte[] data) throws IOException {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));
try {
String b64 = Base64.encode(data);
int lineLength = 64;
int idx = 0;
int len = b64.length();
while (len - idx >= lineLength) {
out.write(b64.substring(idx, idx + lineLength).getBytes());
out.write('\n');
idx += lineLength;
}
out.write(b64.substring(idx, len).getBytes());
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
out.close();
}
}
public static byte[] loadFromBase64File(String fileName) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fileName));
try {
StringBuffer sb = new StringBuffer();
String buffer;
while ((buffer = br.readLine()) != null) {
sb.append(buffer);
}
return Base64.decode(sb.toString());
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
br.close();
}
}
public static void saveToFile(String fileName, byte[] data) throws IOException {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));
try {
out.write(data);
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
out.close();
}
}
public static byte[] loadFromFile(String fileName) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fileName));
try {
StringBuffer sb = new StringBuffer();
String buffer;
while ((buffer = br.readLine()) != null) {
sb.append(buffer);
}
return sb.toString().getBytes();
} catch (Exception e) {
throw new IOException("Unexpected error", e);
} finally {
br.close();
}
}
}
class PrivateRSAKeyReader {
public static RSAPrivateKey getKeyFile(String filename) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
return PrivateRSAKeyReader.generateKey(keyBytes);
}
public static RSAPrivateKey getKey(String base64) throws Exception {
byte[] keyBytes = Base64.decode(base64);
return PrivateRSAKeyReader.generateKey(keyBytes);
}
private static RSAPrivateKey generateKey(byte[] keyBytes) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(spec);
}
}
class PublicRSAKeyReader {
public static RSAPublicKey getKeyFile(String filename) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
return PublicRSAKeyReader.generateKey(keyBytes);
}
public static RSAPublicKey getKey(String base64) throws Exception {
byte[] keyBytes = Base64.decode(base64);
return PublicRSAKeyReader.generateKey(keyBytes);
}
private static RSAPublicKey generateKey(byte[] keyBytes) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
}
}
In c# this code fragment should do the tricks:
String signedMessage = ""; // load Base64 coded the message generated in Java here.
byte[] message = Convert.FromBase64String(signedMessage);
String messageString = Encoding.ASCII.GetString(message);
String[] lines = Regex.Split(messageString, "\n");
byte[] content = Convert.FromBase64String(lines[0]); // first line of the message were the content
byte[] signature = Convert.FromBase64String(lines[1]); // second line were the signature
RSACryptoServiceProvider rsaObj = new RSACryptoServiceProvider(2048);
//Create a new instance of the RSAParameters structure.
RSAParameters rsaPars = new RSAParameters();
rsaPars.Modulus = Convert.FromBase64String("insert your modulus revealed in the Java example here");
rsaPars.Exponent = Convert.FromBase64String("AQAB"); // Exponent. Should always be this.
// Import key parameters into RSA.
rsaObj.ImportParameters(rsaPars);
// verify the message
Console.WriteLine(rsaObj.VerifyData(Encoding.ASCII.GetBytes(lines[0]), "SHA1", signature));
The code is using :
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
I freely admit that packing the final message into another layer of Base64 is a little overkill and should be changed.
Related
I am working on a bluetooth based application where I create a string in Swift and sign it with RSA private key. The string is sent to a WPF app via bluetooth. The wpf app already has its public key. I want to verify the string the in WPF app. But I am not able to do it. I am getting error for algorithm type.
Following is my Swift code:
func signWithPrivateKey(textToEncrypt: String) -> Data? {
guard let privateKey: SecKey = privateKey else {
return nil
}
let algorithm: SecKeyAlgorithm = .rsaSignatureMessagePSSSHA512
guard SecKeyIsAlgorithmSupported(privateKey, .sign, algorithm) else {
return nil
}
var error: Unmanaged<CFError>?
let data = textToEncrypt.data(using: .utf8)!
guard let signature = SecKeyCreateSignature(privateKey,
algorithm,
data as CFData,
&error) as Data? else {
return nil
}
return signature
}
C# code to verify the string:
var deferral = args.GetDeferral();
var request = await args.GetRequestAsync();
var reader = DataReader.FromBuffer(request.Value);
byte[] fileContent = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(fileContent);
string text = Encoding.UTF8.GetString(fileContent, 0, fileContent.Length);
Debug.WriteLine(Convert.ToBase64String(fileContent));
String EncryptedMessage = Convert.ToBase64String(fileContent);
Debug.WriteLine(text);
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes("Guten Morgan");
//byte[] signedBytes = Convert.FromBase64String(text);
try
{
bool success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA-512"), fileContent);
Debug.WriteLine(success);
}catch (CryptographicException e)
{
Debug.WriteLine(e.Message);
}
}
Getting following error:
System.ArgumentNullException: 'Value cannot be null. (Parameter 'hashAlg')'
I have mentioned the algorithm type as rsaSignatureMessagePSSSHA512 in Swift app and trying to verify it with SHA-512 but getting error.
Can someone please help me how to verify the signature in C#.
Edit:
I have modified the WPF code as following and it is not crashing now, but I am still not able to verify it
Credential credential = CredentialManager.ReadCredential("CredentialManagerTests");
String publicKey = credential.Password;
Debug.WriteLine(publicKey);
var deferral = args.GetDeferral();
var request = await args.GetRequestAsync();
var reader = DataReader.FromBuffer(request.Value);
byte[] fileContent = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(fileContent);
string text = Encoding.UTF8.GetString(fileContent, 0, fileContent.Length);
Debug.WriteLine(Convert.ToBase64String(fileContent));
byte[] privateKeyPkcs1DER = ConvertPKCS1PemToDer(publicKey);
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes("Guten Morgan");
rsa.ImportRSAPublicKey(privateKeyPkcs1DER, out _);
try
{
bool success = rsa.VerifyData(bytesToVerify, fileContent, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
Debug.WriteLine(success);
}
catch (CryptographicException e)
{
Debug.WriteLine(e.Message);
}
}
Edit2:
String to sent: "Guten Morgan"
Signature: "G8ePlCyPeivAHPygMHXeCTszTy9EV4y6Gsku9GQJV71t+M7ATFZjxkh9u0DVjR0ei76a4dixzgXKLZLQNy/mxwrvUvolPHE0HUFAErkzfeHcrB8/QlHlEoEdt9vf34L8KNy6yVOHWC2sytgvw009EsKUr3LYb8kSkCeaCVEEEAwAELt3z/YzfnbwR0GXatlNpmaoLOXh0mEep8Xh5AHf6RhbgS0VBFSXSbz3ALbwu5DtlaIv4P1nFOP80w2NBs7HrHOMdR0PKdgiD+suErCYWKINzToKDsdO8XNaKdw3jekYBRXl6GNfAwQeADCq0MDsvptCRVZ//fzU2fXRcaYsRA=="
Algo: SHA512
**Public Key: **
MIIBCgKCAQEA4lS13W+TXZhUXy5yB2NpeulCVb1ZSaReopeKrahjKmUx4NQxVXruEYCY3LpjZcSy8xiudVG3GBIMnPLtaMbc5WAYDj1M2OwnpNHdQ8SKtZ1tdA6iRjfOXGUa1n8FMIMKU5ynTSAiSoh+8gGrY0L6jTsCSdLO5ZU53LQFHSESM8JuBeNZozolb/cKb38ylercVeVpo8egoA8UqHezK23VUJ23faxMmMZDJxVn5pfFedxBTLxwU65KQY8Z4izaFjuPLoGe5JZkXyYNMcrYloDCBG5m9BFiXVkoLEDmGFGfWLMJcqL+D2CszqJ4h712ZR6LYRNtIbo/HG9KR7XCtap3QwIDAQAB.
Private Key:
MIIEowIBAAKCAQEAuAoCUOejL7EBI0P4nfZQkAr8/a37SR/umJB5sUOyy2IyaWuSX1tBOco4XThTkCYj2ubgbf0fGsQ7FRgF6bwbeynhDYS7ptPkvUHF4BaSUsu2KtKkoslvl67M83ESKtOSm0s0CImw60MDlPDr/1SPhkYgSfoQ81CXytrZPkWtB/YekT2ph3PRqyOeMNl0O88Clt2mdR4dZeZWv6WGaaoP1kr8RY9iB6hrg2RdOFXixIOdo7cP3+3w9s4svnIX7RNLIIrUxJHQUYA0oxkiZ9y0peWR5mElywjkg/AFm8RxZsXjHowrqlYGkFKzPTq2/dhwId0Yit95aYVIDE5Dj9n3nQIDAQABAoIBAAFiDd9mxjqrBVuq/JjPS46xjnInlw5XH5dk6o0y+Yp+u+s/5DM0P9q70s2ciUA5kSZpesFI2C1+0QTZD95QTBKSX38XAsP/rqXfcym6cbIOltleiN8yTVTh+udPb7gDrAPfvk3cHwi9ka7SWquqCoQTTdXQe8UgU2uyVlSZ+HFpXFE55ltp4CbnFBH39Sa8pqbpwIwb2+PCFkx7xVUIJ2t1vJd2yBUjEBFJv0SiUy9DWwbXiyoqtZXtVft8Vm7xhimSDN2o6KcnXYEq2eb3UhOKI09tRsYLA3bF66bvVxvcnykuUpLHFOxhBKfD8Vsh+SWHdv6zT52gkF7KDUxDYLECgYEA25iGkCcYx+cfzIdVmIbqa662IzkgUlCdpX37tBNHjyE+72Ehw6odZ0e3iTewm/vYBI6hyCW+T/G6ksl2enjOxBH0DhaQEYLqhSGq63/XOsFsmW7tH4YmInURv4RL4KEIB8HNGLfhllJt92KsrBNzei+SjdtElQhULhw4Mle6PWUCgYEA1ox8DxPTE8HQ5Jw+7D0ut+FyYkysciMSFYpmFX5qlbMlfl0WRL4mVFv/tOBerahsCDdaaOKJKafQVbJIU8G9U0m8PAttcM4OMjexwHzT0EBdf9cNACAXQP/arJEbf8YkPr7/IwI6YGiMFzDKRqcrYMNzl9rZXQEyJGj6u4x/6dkCgYBYIWiv5eD+KXYLoazqoAro2J9kl4KvRodeaadg5/PqL4+Qhs0EN/vA/XldaqpIj9RsT8oCB5PPhdY5Hv2bvWxOKF5oYQnE3WO9tntgNFhuzj4Ffg1Qf4hCf/V1hWTma/pLEq57YyD4MXDMvh9KmCvaN8l7gSqPHV6betva6HZoOQKBgQCwZMxSsR/nrIAMlRF+tUbF08txWkylgoQJxcHshgUnkySOYgY++n8U+JahpZ7x8/juQGRKu4W+A8Tb0Dp68lywL31deJ/AEQnG69duxLJ5E5JL2wlLQxcbT8AABUWwpb2DARFPPTO1s/8JyglkUWjuo4NUJJB1UNhi6xTKQdeg8QKBgESvEWQKqvk3YCpVwCNv5C+1aAVS6Fq/sGmLlVsbQ5W6EENYDg4pq2fgghH9lcRb1BdHjFm25ikADbGWgBOG1fvrD9KsR9RY98x8XM3EQ5EJ2Y2vjQfWBA2da8nr9ETRzY00IjHCzb5AhISWIit7Itaa9H202v1zZCIUY+O/YGGX
Edit3:
My below method was wrong:
private byte[] ConvertPKCS1PemToDer(string pemContents)
{
return Convert.FromBase64String(pemContents
.TrimStart("-----BEGIN PUBLIC KEY-----".ToCharArray())
.TrimEnd("-----END PUBLIC KEY-----".ToCharArray())
.Replace("\r\n", ""));
}
Instead of BEGIN PUBLIC KEY it was BEGIN PRIVATE KEY.
I have fixed it as mentioned above but getting following error on Convert line in above function:
System.FormatException: 'The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.'
Edit4:
I have now created a new project only to test my code and have added following code:
Private key for reference:
"MIIEogIBAAKCAQEArMO9cJHbKYKkozTtqPI9fhecyiibJrvsv5KLLPlQcIi0uo6yfLD4MUFS5UbwaGATnCrLRKr27iQLWwWKxsJdEQMZDMF6EiCQSXEB2AmSEPW4NOh5kbj05cJoDrvSeac3lllsjPlOaVQSrgmiOFoJ1t98utfOJ6FCzam9DZZR7r/OwTZ0754rAp4dqwkamcTaSkW2zQhr++mw4WZZiKGJCPYKKXguGJbRTtr+dcZ4JwrvdYudCguuCVfa+1D6k0NswWEu58DELZ0XPuLu8qFiIA9MI5K/ECHXwozi8UxmDhG9NJuh+uRZZ4s7LVnkNDN8np6vhuwKNL5rP0msUciItwIDAQABAoIBAAgEaSZoyepbD2fVC3TMPB/2df9dpSUbSxrixhto541UBIPtVzQVkoi6ne2uqMu3LpAB3mKfcYvAkJDImqrkQc/tWNHbG4lCXtGIxrZoMtjKFJQ824/UAknQzpCeP7UjpHRYapEb+qW2/JJDU9oDRd2B4S+FZXdVAPJXjPutCy40IziykyhU9Fu0fwCN/ry764EyI4gtN/WgDHwc2C8B7sriW81KtB8vJW10waoot3MRBfXxxhk3wyiNputBg1KDz+O2+iUrO4p1OKdvL9S9u65MN9Klm+i03aexNrjbMHu/lxDUa6buSgfXh5+Na6PLHur/3aCqfZdpJ/OO7AxarqECgYEA4884gGubJmiq8OayGdpnaZa4L/2NEwOEnN2G09ySXuUhIyOzOJmeiw3R0BTOB0ZRzrk/Ozq3/T2z1ScUGr3a7hmgGBm3azNjJCbYZ51/XJPE7B5UhDFejZPGRT8vVnUzjYah4Eipp4hSIe79Ja17vTFTtY7bGGl11a6VaPvHuCECgYEAwiTAs60JqUIFw8qmBzWwIHybbKSS0Pc8AWdMYLVSnqiQKZVDnyypUDv14+VEMbCdW21ShMCrNK0QTWnqkV7ln91jxPNvQbSoLBTXgpxbvoxg57jg6sdtiUuPjdBBc74kV9oW1zX2saLvPOO4fjUw3z1b+6/v1SjRlhIqtQrsRdcCgYBeDgYz7zmFaB17jKPnzKZ5j8LH/ZUrTn6IDWZHPoAoMc22plyud65flvsTQCO4GS5ZfV4/5ARmx/zhelrwl4Y2W9ofWS7DUdoS6P7b+MjGvjPFkNgwI/n31hU8LdQrjAQW4IkhAp8ZDk1quTNHRRMbj6wR/8MxlwkRih0h1SImQQKBgGnuQdMH9ICNDLYzKXo/mhVvyCJ0fcNVU0F0yqDt7uGxGdAGqLn+VXf474bkvtvaAVI0iVT0B7abQ4zp4NpnDCW5V8nMBgW0/BnpWVnj1M9YqztkjhysqiDCwNZhLoVn1060KchNooh0XdM8cZszjLISOdFPwy3ssscOrIzSI+9LAoGAMDcGC5P9gVBCNd8ox5OlozNwAmHmHwpqFJcOxNx4lCE5DkT+6DEscoLMZ4LkNVzMHb0LmyhDnF24eIn/v7FRb6ZF5YzlpIdQC2tvXaJoyPwLPee7XGGr9IRkupbrNzPWVhTUYK9kJvI7SWPF2DBFK8fiC5S96USVp++kOjp/faE="
String publicKey = "MIIBCgKCAQEArMO9cJHbKYKkozTtqPI9fhecyiibJrvsv5KLLPlQcIi0uo6yfLD4MUFS5UbwaGATnCrLRKr27iQLWwWKxsJdEQMZDMF6EiCQSXEB2AmSEPW4NOh5kbj05cJoDrvSeac3lllsjPlOaVQSrgmiOFoJ1t98utfOJ6FCzam9DZZR7r/OwTZ0754rAp4dqwkamcTaSkW2zQhr++mw4WZZiKGJCPYKKXguGJbRTtr+dcZ4JwrvdYudCguuCVfa+1D6k0NswWEu58DELZ0XPuLu8qFiIA9MI5K/ECHXwozi8UxmDhG9NJuh+uRZZ4s7LVnkNDN8np6vhuwKNL5rP0msUciItwIDAQAB";
String signature = "ntGm9Vx38l26ZSEitc1L+G6YZw3Dt4dmXoQQUjSAcKD1d4bFm8QI6CIncTBQ0+rGWUh7fr2DWQBB1OPgoZRoDs23SGvl+KTqUBo6Ym+kIK4KXwBrMt3YXNFpXuXVk36+4+oBPFhcMAwG4fIG5MnMEHqv/F+NNA71a10irB0frMpwpU/FZDGEbCnvOxGe6qIUsYt/dMbf1UT1AjKqQA0EmNqvIsU5DuHcpScb5xuV8aPJg6vPPKafLa8LbBiCA4dyxUcUYSjSaTQ6D9YoZ3Ich/dRBCyIdw9vESBSrOUGvwOmNl+5X+r2UyFN8RgYwW61M3iYdmkwJYYVUbMA69vCOA==";
byte[] signatureBytes = Encoding.UTF8.GetBytes(signature);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportRSAPublicKey(Convert.FromBase64String(publicKey), out _);
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes("Guten Morgen");
try
{
bool success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signatureBytes);
Debug.WriteLine(success);
}
catch (CryptographicException e)
{
Debug.WriteLine(e.Message);
}
}
But success variable is still false.
here is my code.get error in encryption method
encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
The method throws "Key not valid for use in specified state."
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
private void button1_Click(object sender, EventArgs e)
{
var rsa = new RSACryptoServiceProvider();
var rsaKeyPair = DotNetUtilities.GetRsaKeyPair(rsa);
var writer = new StringWriter();
var writer2 = new StringWriter();
var pemWriter = new PemWriter(writer);
var pemWriter2 = new PemWriter(writer2);
pemWriter.WriteObject(rsaKeyPair.Public);
pemWriter2.WriteObject(rsaKeyPair.Private);
string Pub = writer.ToString();
string Prv = writer2.ToString();
plaintext = ByteConverter.GetBytes(txtplain.Text);
encryptedtext = Encryption(plaintext, Pub, false);
}
get error here when encrypt data using by RSA encryption method.here pass DATA as a byte and RSA public key as a string.after that convert it to RSAParanetrs "RSA.ImportParameters(GetRSAParameters(RSAKey));"
then encrypt data get this error.
static public byte[] Encryption(byte[] Data, string RSAKey, bool DoOAEPPadding)
{
try
{
byte[] encryptedData;
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.ImportParameters(GetRSAParameters(RSAKey));
encryptedData = RSA.Encrypt(Data, DoOAEPPadding); // get error "Key not valid for use in specified state"
}
return encryptedData;
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
private static RSAParameters GetRSAParameters(string pPublicKey)
{
byte[] lDer;
int lBeginStart = "-----BEGIN PUBLIC KEY-----".Length;
int lEndLenght = "-----END PUBLIC KEY-------".Length;
string KeyString = pPublicKey.Substring(lBeginStart, (pPublicKey.Length - lBeginStart - lEndLenght));
lDer = Convert.FromBase64String(KeyString);
RSAParameters lRSAKeyInfo = new RSAParameters();
lRSAKeyInfo.Modulus = GetModulus(lDer);
lRSAKeyInfo.Exponent = GetExponent(lDer);
return lRSAKeyInfo;
}
private static byte[] GetModulus(byte[] pDer)
{
string lModulus = BitConverter.ToString(pDer).Replace("-", "").Substring(58, 256);
return StringHexToByteArray(lModulus);
}
private static byte[] GetExponent(byte[] pDer)
{
int lExponentLenght = pDer[pDer.Length - 3];
string lExponent = BitConverter.ToString(pDer).Replace("-", "").Substring((pDer.Length * 2) - lExponentLenght * 2, lExponentLenght * 2);
return StringHexToByteArray(lExponent);
}
public static byte[] StringHexToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
I have a signature generator in Java:
private static String getPvtKeyFromConfig = "merchantPvtKey";
private static String getPubKeyFromConfig = "merchantPubKey";
private static String getSaltFromConfig = "merchant_salt";
public static void main(String[] args) throws Exception {
// Generate Signature
String uniqueId="ab123";
byte[] data = Base64.decodeBase64(uniqueId);
java.security.Signature sig =java.security.Signature.getInstance("SHA1WithRSA");
sig.initSign(getPrivateFromSpec(getPvtKeyFromConfig));
sig.update(data);
byte[] signatureBytes = sig.sign();
System.out.println("Signature for uniqueId - "+uniqueId+": "+ Base64.encodeBase64String(signatureBytes));
}
How can I do it in C#?
I think this is what you are looking for:
static byte[] Sign(string text, string certSubject)
{
// Access a store
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
// Find the certificate used to sign
RSACryptoServiceProvider provider = null;
foreach (X509Certificate2 cert in store.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// Get its associated CSP and private key
provider = (RSACryptoServiceProvider)cert.PrivateKey;
break;
}
}
if (provider == null)
throw new Exception("Certificate not found.");
// Hash the data
var hash = HashText(text);
// Sign the hash
var signature = provider.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
return signature;
}
}
static bool Verify(string text, byte[] signature, string certPath)
{
// Load the certificate used to verify the signature
X509Certificate2 certificate = new X509Certificate2(certPath);
// Get its associated provider and public key
RSACryptoServiceProvider provider = (RSACryptoServiceProvider)certificate.PublicKey.Key;
// Hash the data
var hash = HashText(text);
// Verify the signature with the hash
var result = provider.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
return result;
}
static byte[] HashText(string text)
{
SHA1Managed sha1Hasher = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1Hasher.ComputeHash(data);
return hash;
}
Sample usage:
var signature = Sign("To be or not to be, that is the question.", "CN=some_cert");
var result = Verify("To be or not to be, that is the question.", signature, "C:\\temp\\some_cert.cer");
Console.WriteLine("Verified: {0}", result);
Below C# code works for me for the exact java code mentioned in the question.
Few Notes :
.) Your project should have Target frameworks as 4.8
.) You should have existing private key
.) Using this Private key we can generate the .pfx certificate by using the OpenSSL commands.( we will have to generate the .crt first and then .pfx)
static string GenerateSignatureUsingCert(string dataToSign)
{
X509Certificate2 certificate = new X509Certificate2(#"C:\PFX\MyCertificate.pfx", "****", X509KeyStorageFlags.Exportable);
RSA privateKey1 = certificate.GetRSAPrivateKey();
Encoding unicode = Encoding.UTF8;
byte[] bytesInUni = unicode.GetBytes(dataToSign);
return Convert.ToBase64String(privateKey1.SignData(bytesInUni, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1));
}
Usage
string canonicalizeData = CanonicalizeHeaders(headers);
String data = null;
try
{
data = GenerateSignatureUsingCert(canonicalizeData);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Signature: " + data);
I have file which I am signing using RSA algorithm using two methods:
bouncycastle
openssl
both results in two different output and openssl results in valid file. I don't know why my c# code is giving wrong output.
Here is my code for bouncy castle in c#
private void GenerateSignatureFile(string sourceFile)
{
try
{
var stringToSign = ReadText(sourceFile).ToString();
var sig = Sign(stringToSign);
var fileContent = Encoding.UTF8.GetString(sig);
using (var sw = File.CreateText(Path.Combine(_projectLocation, _sigFileName)))
{
sw.WriteLine(fileContent);
}
}
catch (Exception ex)
{
LoggingService.Log(ex.Message);
}
}
public byte[] Sign(String data)
{
var key = readPrivateKey();
/* Make the key */
var keyParameter = new RsaKeyParameters(key.IsPrivate, ((RsaPrivateCrtKeyParameters)key).Modulus, ((RsaPrivateCrtKeyParameters)key).Exponent);
/* Init alg */
ISigner sig = SignerUtilities.GetSigner("SHA256withRSA");
/* Populate key */
sig.Init(true, key);
/* Get the bytes to be signed from the string */
var bytes = Encoding.UTF8.GetBytes(data);
/* Calc the signature */
sig.BlockUpdate(bytes, 0, bytes.Length);
return sig.GenerateSignature();
}
public static IEnumerable<string> ReadText(string scriptPath)
{
var buffer = new StringBuilder();
foreach (var line in File.ReadLines(scriptPath))
{
if (line == "GO")
{
yield return buffer.ToString();
buffer.Clear();
}
else
{
buffer.AppendLine(line);
}
}
}
private AsymmetricKeyParameter readPrivateKey()
{
AsymmetricCipherKeyPair keyPair;
using (var reader = new StringReader(_privateKey))
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
return keyPair.Private;
}
and in openssl I use following command to sign data
openssl dgst -sha256 -sign content_private_key.pem -out content.zip.sig content.zip
I don't know why my c# code is resulting in different output.
I don't think that ReadText will always return the text identical to the binary text that openssl processes. To be sure, just write the bytes variable to file after the following line was executed:
var bytes = Encoding.UTF8.GetBytes(data);
For instance you could compare the two files using sha256sum.
If this is indeed the culprit then simply read in the file as binary and sign that.
we have existing encryption code in java and which is working absolutely fine.I am trying to create same encryption method in .net which is failing java decryption method saying bad padding exception. See the code details below:
Working Java Code:
Encryption:
private static byte[] doThis(String message) {
byte[] messageCrypte = null;
try {
// Certificate Input Stream
// LA SSL Certificate to be passed.
InputStream inStream = new FileInputStream(certificate);
// X509Certificate created
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
// Getting Public key using Certficate
PublicKey rsaPublicKey = (PublicKey) cert.getPublicKey();
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE");
encryptCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
byte[] messageACrypter = message.getBytes();
// Encrypted String
messageCrypte = encryptCipher.doFinal(messageACrypter);
} catch (Exception e) {
// TODO: Exception Handling
e.printStackTrace();
}
return messageCrypte;
}
Equivalent c# .Net code I am trying to use but I am getting bad padding exception form java decryption code.
static byte[] doThis(string message)
{
X509Certificate cert = new X509Certificate(#"C:\Data\abc-rsa-public-key-certificate.cer");
byte[] aa = cert.GetPublicKey();
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
byte[] Exponent = { 1, 0, 1 };
RSAKeyInfo = RSA.ExportParameters(false);
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = aa;
//RSAKeyInfo.Exponent = Exponent;
RSA.ImportParameters(RSAKeyInfo);
byte[] bb = RSA.Encrypt(GetBytes(message), false);
return bb;
}
Java code for decryption
private String getDecryptedString(byte[] credentials, PrivateKey secretKey) throws NoSuchAlgorithmException,
NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException {
String decryptedString;
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE");
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] messageDecrypte = decryptCipher.doFinal(credentials);
decryptedString = new String(messageDecrypte);
return decryptedString;
}
Here is the .net code:
public static string EncrypIt(string inputString, X509Certificate2 cert)
{
RSACryptoServiceProvider rsaservice = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] plaintext = Encoding.UTF8.GetBytes(inputString);
byte[] ciphertext = rsaservice.Encrypt(plaintext, false);
string cipherresult = Convert.ToBase64String(ciphertext);
return cipherresult;
}