encrypting a string using bouncycastle c# - c#

Currently learning about reading in the certificate from the windows store.
then using the private and public keys to encrypt a string then decrypt it.
I have a certificate in my store which has the public and private key assigned to it.
I'm reading in the public key fine using
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certificate in store.Certificates)
{
//TODO's
byte[] encodedPublicKey = certificate.PublicKey.EncodedKeyValue.RawData;
File.WriteAllLines(filePath, new[] {
"-----BEGIN PUBLIC KEY-----",
Convert.ToBase64String(encodedPublicKey, Base64FormattingOptions.InsertLineBreaks),
"-----END PUBLIC KEY-----"
});
publicKey = System.IO.File.ReadAllText(filePath);
encryptedWithPublic = encryption.EncryptWithPublic(input, publicKey);
}
store.Close();
i then call this method to encrypt
var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
var encryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(publicKey))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
Note - when i create my own public key the above method works, but when i use a public key from the keystore it is falling with
Additional information: Unknown object in GetInstance:
Org.BouncyCastle.Asn1.DerInteger
clearly i'm doing something wrong when i'm reading in from the store but i've not managed to find any help on here with this issue

Related

C# AES-GCM and RSA Encryption using Sytem.Secuirty.Cryptography

I am developing the post data request in c# and using AES GCM encryption and RSA encryption. As compared to the C# code with Node.js / Python for encryption, we found that C# AES GCM encryption does not have tag value in the method.
e.g How do I return below data in C# after encryption.
return {
key: aesKey,
cipher_text: encrypted,
tag,
iv
}
In Python and Node.js using JsEncrypt, forge.js.... and implementations are working fine. But, now developing the same client in the C#, do not find such libraries which I can use directly.
However, I found two classes in C# AesGcm and AesManaged for AES Encryption. But I think AesManages is only for AES not GCM encryption. Therefore, I am using AesGcm class in below example, but not sure.
The code executes the following steps:
Request client certificate from DSG.
Extract the public key from the certificate.
Encrypt PII with AES key.
Encrypt AES key with RSA public key.
Send wrapped key and PII payload to DSG tokenization.
class Program
{
static void Main(string[] args)
{
string _ApiKey = "--- YOUR API KEY ----";
string _SecretKey = " ---- YOUR SECRET KEY --------";
string jsonPlainText = "<JSON TExT>";
#region Certificate
string certificate = GetCertificate();
Certificate rawSigningCert = JsonConvert.DescrializeObject<Certificate>(certificate);
var certBytes = Encoding.UTF8.GetBytes(rawSigningCert.crt);
// X509Certificate2
var x509Certificate2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certBytes);
byte[] publicKey = x509Certificate2.GetPublicKey();
#endregion
#region AES GCM Encryption
byte[] aesGcmKey = new byte[16];
RandomNumberGenerator.Fill(aesGcmKey);
byte[] nonce = new byte[12];
RandomNumberGenerator.Fill(nonce);
byte[] tag = new byte[16];
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(jsonPlainText);
byte[] cipherText = new byte[dataToEncrypt.Length];
using (AesGcm aesGcm = new AesGcm(aesGcmKey))
{
aesGcm.Encrypt(nonce, dataToEncrypt, cipherText, tag);
}
string encryptedCipher = Convert.ToBase64String(cipherText);
var keyString = Convert.ToBase64String(aesGcmKey);
var iVString = Convert.ToBase64String(nonce);
var tagString = Convert.ToBase64String(tag);
string aesGcmEncryptionString = string.Format("{0}:{1}:{2}", keyString, iVString, tagString);
#endregion
#region RSA Encryption
var rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = rsa.ExportParameters(false);
RSAKeyInfo.Modulus = publicKey;
rsa.ImportParameters(RSAKeyInfo);
byte[] encryptedSymmetricKey = RSAEncrypt(Encoding.UTF8.GetBytes(aesGcmEncryptionString), RSAKeyInfo, false);
var enc_key = Convert.ToBase64String(encryptedSymmetricKey);
var requestBodyObject = new { message = encryptedCipher, enckey = enc_key };
string jsonRequestBody = JsonConvert.SerializeObject(requestBodyObject);
#endregion
#region Calculating Message Signature
Random random = new Random();
int ClientRequestId = random.Next(0, 99999999);
var time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var rawSignature = _ApiKey + ClientRequestId + time + jsonRequestBody;
HMACSHA256 hashObject = new HMACSHA256(Encoding.UTF8.GetBytes(_SecretKey));
var signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(rawSignature));
var computedHmac = Convert.ToBase64String(signature);
#endregion
#region Make Request
try
{
var client = new RestClient("< API URL>");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("contentType", "application/json");
request.AddHeader("clientRequestId", ClientRequestId.ToString());
request.AddHeader("apiKey", _ApiKey);
request.AddHeader("timestamp", time.Tostring);
request.AddHeader("messageSignature", computedHmac);
request.AddJsonBody(jsonRequestBody);
request.RequestFormat = DataFormat.Json;
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
#endregion
}
static string GetCertificate()
{
string cer = string.Empty;
// Code to get certicate .crt from the service.
//"<GET CERTICATE HERE>";
return cer;
}
static public byte[] RSAEncrpt(byte[] data, RSAParameters keyInfo, bool doOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(keyInfo);
encryptedData = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
}
return encryptedData;
}
}
class Certificate
{
public string crt { get; set; }
}
How I can trace each steps as service is responding with incorrect message signature. Any way to verify and trace the above code steps?
Help!
Thanks in advance

Get byte array of ECC encryption certificate

I'm using self signed certificate ECDH_secP384r1 for signing token. Here is the PowerShell that I create the certificate:
$Cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname $Certname -NotAfter $ExpireDate -KeyAlgorithm ECDH_secP384r1
Now in my .net core application first I load the certificate:
private readonly string _certificateSubjectName;
public X509Certificate2 GetSigningCertificate()
{
using (var store = new X509Store(StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, _certificateSubjectName, false);
return certificates[0];
}
}
And also I can Get the ECDsa private key like
ECDsa privateKey = signingCertificate.GetECDsaPrivateKey();
ECDsa publicKey = signingCertificate.GetECDsaPublicKey()
But how could I have byte array of these keys?
For Rsa I could use:
public byte[] GetPrivateKey(X509Certificate2 certificate)
{
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
MemoryStream memoryStream = new MemoryStream();
TextWriter streamWriter = new StreamWriter(memoryStream);
PemWriter pemWriter = new PemWriter(streamWriter);
AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(rsa);
pemWriter.WriteObject(keyPair.Private);
streamWriter.Flush();
byte[] byteArray = memoryStream.GetBuffer();
return byteArray;
}
But how about ECDsa? any idea?

How can I create a SHA1WithRSA signature

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);

How can I verify signature for open PGP using BouncyCastle

How can I verify signature for open PGP using BouncyCastle?
I am using C#
I have pulic key http://itransact.com/support/toolkit/html-connection/pgp.php
I am using BouncyCastle as open pgp library
I have signature that I recieve in query string.
According to instruction (http://itransact.com/downloads/PCFullDocument-4.4.pdf p.145) algorithm is RSA.
I checked a lot of resource but no success. As I understood I need to pass public key and signature to some Verify method.
It is also not clear if I have to convert given public key in string format to some appropriate public key object. If I have to what is the type? I have tried to convert it to RsaKeyParameters but got error message about inappropriate block on public key.
At the moment I have the following code
private bool VerifyWithPublicKey(string data, byte[] sig)
{
RSACryptoServiceProvider rsa;
using (var keyreader = new StringReader(publicKey))
{
var pemReader = new PemReader(keyreader);
var y = (RsaKeyParameters)pemReader.ReadObject();
rsa = (RSACryptoServiceProvider)RSA.Create();
var rsaParameters = new RSAParameters();
rsaParameters.Modulus = y.Modulus.ToByteArray();
rsaParameters.Exponent = y.Exponent.ToByteArray();
rsa.ImportParameters(rsaParameters);
// compute sha1 hash of the data
var sha = new SHA1CryptoServiceProvider();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
// This always returns false
return rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), sig);
}
Using RSA is not your case. You need
Define public key
Convert public ket into PgPPublicKey
Get PgpSignature object
Verify signature
To verify signature you will need original data that was signed.
public class iTransactVerifier
{
private const string PublicKey = #"-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 4.5
mQCNAjZu
-----END PGP PUBLIC KEY BLOCK-----";
public static bool Verify(string signature, string data)
{
var inputStream = ConvertStringToStream(signature);
PgpPublicKey publicKey = ReadPublicKeyFromString();
var stream = PgpUtilities.GetDecoderStream(inputStream);
PgpObjectFactory pgpFact = new PgpObjectFactory(stream);
PgpSignatureList sList = pgpFact.NextPgpObject() as PgpSignatureList;
if (sList == null)
{
throw new InvalidOperationException("PgpObjectFactory could not create signature list");
}
PgpSignature firstSig = sList[0];
firstSig.InitVerify(publicKey);
firstSig.Update(Encoding.UTF8.GetBytes(data));
var verified = firstSig.Verify();
return verified;
}
....
private static PgpPublicKey ReadPublicKeyFromString()
{
var varstream = ConvertStringToStream(PublicKey);
var stream = PgpUtilities.GetDecoderStream(varstream);
PgpObjectFactory pgpFact = new PgpObjectFactory(stream);
var keyRing = (PgpPublicKeyRing)pgpFact.NextPgpObject();
return keyRing.GetPublicKey();
}
}

How to verify a signature in .net generated in Android

The problem is the following:
I generate the key in Android (Xamarin.Droid):
public IPublicKey CreateKey(string keyID)
{
/*KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
"key1",
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
.build());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Signature signature = Signature.getInstance("SHA256withRSA/PSS");
signature.initSign(keyPair.getPrivate());
// The key pair can also be obtained from the Android Keystore any time as follows:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
PrivateKey privateKey = (PrivateKey)keyStore.getKey("key1", null);
PublicKey publicKey = keyStore.getCertificate("key1").getPublicKey();*/
//App.Current.MainPage.DisplayAlert("Info", "Creating a new key pair", "Ok");
// UTILIZANDO RSA
KeyPairGenerator kpg =
KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, KEYSTORE_NAME);
kpg.Initialize(
new KeyGenParameterSpec.Builder(keyID,
KeyStorePurpose.Sign)
.SetSignaturePaddings(KeyProperties.SignaturePaddingRsaPss)
.SetDigests(KeyProperties.DigestSha1)
.Build()
);
KeyPair keyPair = kpg.GenerateKeyPair();
Log.Debug(TAG, "New key created for fingerprint authentication");
return keyPair.Public;
}
Then i generate a signature:
KeyStore.PrivateKeyEntry PKentry =
(KeyStore.PrivateKeyEntry)_keystore.GetEntry(keyID, null);
IPublicKey pk = (IPublicKey)PKentry.Certificate.PublicKey;
//this.pk = pk;
privKey = PKentry.PrivateKey;
//cipher.Init(Cipher.EncryptMode, privKey);
//byte[] output = cipher.DoFinal(Encoding.UTF8.GetBytes(input));
//String s = new string(cipher.DoFinal(input));
// signature
Signature sig = Signature.GetInstance("SHA1withRSA/PSS");
sig.InitSign(privKey);
byte[] inputDataToSign = Encoding.UTF8.GetBytes(input);
sig.Update(inputDataToSign);
byte[] signatureBytes = sig.Sign();
And i send the key and the signature to a ASP.net wep API 2 server.
Client side response generation:
RegistrationResponse registrationResponse = new RegistrationResponse();
string fcparams = Utils.Base64Encode(JsonConvert.SerializeObject(finalChallengeParams));
registrationResponse.fcParams = fcparams;
byte[] signedData = sign(fcparams, registrationRequest.username, facetID);
registrationResponse.signedData = signedData;
registrationResponse.Base64key = convertPublicKeyToString(publicKey);
...
...
private string convertPublicKeyToString(IPublicKey publicKey)
{
string publicKeyString = Base64.EncodeToString(publicKey.GetEncoded(), 0);
return publicKeyString;
}
I send it using Refit Nugget.
And this is the code i use when i receive the HTTPRequest on server side:
[Route("regResponse/")]
[HttpPost]
public IHttpActionResult ProcessClientRegistrationResponse([FromBody] RegistrationResponse registrationResponse)
{
//byte[] publicKeyBytes = Convert.FromBase64String(registrationResponse.Base64key);
byte[] publicKeyBytes = registrationResponse.Base64key;
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
/*****/
string alg = rsa.SignatureAlgorithm;
byte[] signedData = registrationResponse.signedData;
byte[] fcParamsBytes = Encoding.UTF8.GetBytes(registrationResponse.fcParams);
RSACng rsaCng = new RSACng();
rsaCng.ImportParameters(rsaParameters);
SHA1Managed hash = new SHA1Managed();
byte[] hashedData;
hashedData = hash.ComputeHash(signedData);
/*********/
bool rsaCngDataOk1 = rsaCng.VerifyData(fcParamsBytes, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
bool rsaCngDataOk2 = rsaCng.VerifyData(fcParamsBytes, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
bool rsaCngDataOk3 = rsaCng.VerifyData(hashedData, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
bool rsaCngDataOk4 = rsaCng.VerifyData(hashedData, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
bool rsaCngHashOk1 = rsaCng.VerifyHash(hashedData, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
bool dataOK1 = rsa.VerifyData(fcParamsBytes, new SHA1CryptoServiceProvider(), signedData);
bool dataOk2 = rsa.VerifyData(fcParamsBytes, signedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
bool hashOk = rsa.VerifyHash(hashedData, CryptoConfig.MapNameToOID("SHA1"), signedData);
return Ok(true);
}
EVERY bool is wrong. I think the problem is clearly on the public key.
The questions are,
does the method publickey.encode() do what i think? I think it converts my public key to a byte[] representation (source: Android developer Key Info)
do i convert the received byte[] representation of the key to a correct RSA key?
Is there any problem on algorithms? I don't think so but we never know...
I don't find the solution. I searched for ways to import public keys from strings in .net or c# and for ways to export Android Public key to string or byte[] but there's no much help for this concrete questions...
#James K Polk gave me the solution.
Apparently C# doesn't work well with PSS padding. I just had to change it to PKCS1. And i changed to digest algorithm too to SHA512.

Categories