Verifying Java signature in C# - java

I have signature created in Java by following code
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
signature = (Signature) rsaSha256.getCipher();
signature.initSign(privateKey);
signature.update(binaryData);
signatureBytes = signature.sign();
By verifying signature in C#, im always getting false. Following code use BouncyCastle library
ISigner signer = SignerUtilities.GetSigner("SHA256withRSA");
using (TextReader sr = new StringReader(publicKey))
{
PemReader pr = new PemReader(sr);
RsaKeyParameters keys = (RsaKeyParameters)pr.ReadObject();
signer.Init(false, keys);
signer.BlockUpdate(value, 0, value.Length);
bool isValid = signer.VerifySignature(signature);
return isValid;
}
Following code return false too
private static bool VerifyWithPublicKey(byte[] data, byte[] sig, string publicKey)
{
RSACryptoServiceProvider rsa;
using (var keyreader = new StringReader(publicKey))
{
var pemReader = new PemReader(keyreader);
var y = (RsaKeyParameters)pemReader.ReadObject();
RSAParameters p1 = DotNetUtilities.ToRSAParameters(y);
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(p1);
}
byte[] hash;
using (var sha256 = SHA256.Create())
{
hash = sha256.ComputeHash(data);
}
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
RSADeformatter.SetHashAlgorithm("SHA256");
//Verify the hash and display the results to the console.
if (RSADeformatter.VerifySignature(hash, sig))
{
Console.WriteLine("The signature was verified.");
}
else
{
Console.WriteLine("The signature was NOT verified.");
}
// This always returns false
return rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), sig);
}
Im out of ideas. Anyone done something similar? If so, can you share your code please

This works for me, I see a difference where you use .GetSigner("SHA256withRSA") but I use "SHA-256withRSA"
public static bool VerifySignature(byte[] hashBytes, byte[] signatureBytes)
{
PemReader pemReader = new PemReader(new StreamReader("PublicKey.pem"));
RsaKeyParameters parameters = (RsaKeyParameters)pemReader.ReadObject();
RsaDigestSigner signer = (RsaDigestSigner)SignerUtilities.GetSigner("SHA-256withRSA");
signer.Init(false, parameters);
signer.BlockUpdate(hashBytes, 0, hashBytes.Length);
bool isValid = signer.VerifySignature(signatureBytes);
return isValid;
}

Related

RSA signature Java to C#

I have a tool which creates a signature. I am looking for equivalent code in dot net core 2.1.
Java Code: sign(currentdate,'some text','SHA1WithRSA')
public String sign(String date, String subjectId, String algorithm){
Signature rsa = Signature.getInstance(algorithm);
rsa.initSign(this.getPrivate());
rsa.update(subjectId.getBytes());
rsa.update(date.getBytes());
String signature = new String(Base64.getEncoder().encode(rsa.sign()));
return signature;
}
public PrivateKey getPrivate() {
String privateKeyPEM = "<This is the private key string";
byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
return kf.generatePrivate(spec);
}
Suggest a way to generate dot net core 2.1 equivalent code for the above with or without 3rd party tool.
This is work for me using Portable.BouncyCastle library.
private string GetSign(string data)
{
using (var rsa = new RSACryptoServiceProvider())
{
var privateKey = new StringBuilder();
privateKey.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
privateKey.AppendLine("private key as string");
privateKey.AppendLine("-----END RSA PRIVATE KEY-----");
var pem = new PemReader(new StringReader(privateKey.ToString()));
var keyPair = (AsymmetricCipherKeyPair)pem.ReadObject();
var privateKeyParameters = (RsaPrivateCrtKeyParameters)keyPair.Private;
var rsaParameters = DotNetUtilities.ToRSAParameters(privateKeyParameters);
rsa.ImportParameters(rsaParameters);
var sign = rsa.SignData(Encoding.UTF8.GetBytes(data), new HashAlgorithmName("SHA1") /*pass your algorithm*/,
RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(sign);
}
}

How to fix "java.security.InvalidKeyException: Unsupported key algorithm: EC. Only RSA supported" while using Keystore in api 18

I need to store sensitive data in local storage with an API 18 , i choose to use the Keystore. I try several solution but none worked.
I try to make my RSAPrivateKey in PrivateKey without cast but it don't work.
I also try to use other crypting algorithm but i never success to make them work in API 18
public String decryptString(String alias, String encryptedText) {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
String decryptedText = "";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKey);
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(encryptedText, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
decryptedText = new String(bytes, 0, bytes.length, "UTF-8");
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return decryptedText;
}
public String encryptString(String alias, String initialText) {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
String encryptedText = "";
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
// Encrypt the text
if(initialText.isEmpty()) {
Log.e(TAG, "initialText is Empty");
return "";
}
Cipher input = Cipher.getInstance("RSA/ECB/PKCS1Padding");
input.init(Cipher.ENCRYPT_MODE, publicKey);//Need RSA private or public key
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, input);
cipherOutputStream.write(initialText.getBytes("UTF-8"));
cipherOutputStream.close();
byte [] vals = outputStream.toByteArray();
encryptedText = Base64.encodeToString(vals, Base64.DEFAULT);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return encryptedText;
}
Here is the erot i get. I would like to success to keep my data in a secure place
java.security.InvalidKeyException: Unsupported key algorithm: EC. Only RSA supported
at com.cryptor.Cryptor.encryptString(Cryptor.java:108)
I don't see where/when you generate your RSA key. On my side, I have done the following steps :
Create/retrieve the Keystore
Generate RSA keys with a KeyPairGenerator (be careful : different methods since Android M)
val generator = KeyPairGenerator.getInstance(ALGORITHM, CryptoConstants.ANDROID_KEY_STORE)
Here, ALGORITHM="RSA" and not "RSA/ECB/PKCS1Padding" and CryptoConstants.ANDROID_KEY_STORE = "AndroidKeyStore" (for example)
Save keys in the Keystore
Encrypt with the public key
Decrypt with the private key
With these steps, my encryption methods are
fun encrypt(publicKey: PublicKey, rawText: ByteArray): String {
try {
val cipher = CipherUtil.getStandardCipherInstance(TRANSFORMATION) // TRANSFORMATION = "RSA/ECB/PKCS1Padding"
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val bytes = cipher.doFinal(rawText)
return Base64.encodeToString(bytes, BASE64_SETTINGS) // BASE64_SETTINGS = Base64.NO_WRAP
} catch (e: GeneralSecurityException) {
throw SecurityException(e)
}
}
fun decrypt(privateKey: PrivateKey, base64CipherBytes: ByteArray): ByteArray {
try {
val cipher = CipherUtil.getStandardCipherInstance(TRANSFORMATION) // TRANSFORMATION = "RSA/ECB/PKCS1Padding"
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val encryptedData = Base64.decode(base64CipherBytes, BASE64_SETTINGS) // BASE64_SETTINGS
return cipher.doFinal(encryptedData)
} catch (e: GeneralSecurityException) {
throw SecurityException(e)
}
}
Btw, you can bypass the Base64 encoding if you don't need it.

C# How ro get X509 PublicKey from byte stream

Trying to get X509 PublicKey from byte stream using this code.
[Fact]
public void CreatePublicKeyParameters__ShouldReturnPublicKey__WhenPassPublicKeyBytes()
{
ApplePay applePay = new ApplePay(new MOBSHOPApplePayRequest());
byte[] privateKey = Base64.Decode("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjyo3fzxT7j+CFxC7I4B5iVee2FUyn2vfOSjcgp2/g6qhRANCAARdoBFEtnuapXFKw4DYWsW0yV4bavpdWKszkefi19AhlIRE3WSNWSn25W5tZNFjMWtLISBmqANyufx2xP19oRvy");
var publickey = applePay.CreatePublicKeyParameters(privateKey);
}
public AsymmetricKeyParameter CreatePublicKeyParameters(byte[] ephemeralPublicKeyBytes)
{
return (ECPublicKeyParameters)PublicKeyFactory.CreateKey(ephemeralPublicKeyBytes);
}
Getting following error
Bad sequence size: 3
Parameter name: seq
BouncyCastle.Crypto
at Org.BouncyCastle.Asn1.X509.SubjectPublicKeyInfo..ctor(Asn1Sequence seq)
at Org.BouncyCastle.Asn1.X509.SubjectPublicKeyInfo.GetInstance(Object obj)
at Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(Byte[] keyInfoData)
Basically, I'm trying to convert this following JAVA code to C#
public ECPublicKey CreatePublicKeyParameters(byte[] ephemeralPublicKeyBytes)
{
KeyFactory keyFactory = KeyFactory.getInstance( "ECDH", "BC" );
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec( ephemeralPublicKeyBytes );
ECPublicKey ephemeralPublicKey = (ECPublicKey) keyFactory.generatePublic( encodedKeySpec );
return ECPublicKey
}
Please help me.
Sorry, it was wrong test input data. It's working with this below testcase.
[Fact]
public void CreatePublicKeyParameters__ShouldReturnPublicKey__WhenPassPublicKeyBytes()
{
ApplePay applePay = new ApplePay(new MOBSHOPApplePayRequest());
byte[] privateKey = Base64.Decode(
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEl/XAbOgrSCupps/QbIxJ3u4QZ1PlbO5uGDD1zj/JGMoephYSEgw+63gHQHekx3T8duXN3CoYafUpuQlwOeK6/w==");
var publickey = applePay.CreatePublicKeyParameters(privateKey);
}
Thanks

.NET equivalent of Java KeyFactory.getInstance "RSA"/"RSA/ECB/PKCS1Padding"

I have the following code,
public static String encrypt(String plainText, String key){
try{
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.decode(key, Base64.DEFAULT)));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")),Base64.DEFAULT);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
I want to convert this to C#. I have tried CryptUtils but it doesn't work https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Common/CryptUtils.cs
Sample key,
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ9AMIIBCgKCAQEAoqB1N9kugk4UKYnbh0fcg3qYyYKP0m4B
MjWd05ReeAdj+7JRYDEKO6xthDdVSdRO1/2V+YtY8DnXWnxRaICwu8235s3imZCyqgYnomPWdY+F
K540oTz/zug+9wbrlzt/WQFUU4lPlQbzm/Gjw8XfaCozT0e3bnWQcD7rORCOyuJgwSGgREjTv1ss
pgEaKTMknii9vpGZLeAXwoeIYROhuT4IoIkPDhtY0/UZiCi6v7Ja2dmy53VlWIkcm3rcnSJdvpXr
OgiHvaNABHmeymNycNqd6WUaysBRheluQ86nq/2nZPW0gcvmYt5zbMMYX3yY/n2WtAKeNQBAEW1q
b0s6MwIDAQAB
Possible encryped value,
Y3VTjghDnTrCeG8C/RklKsJ3Y0Mt89sSGGin28E4iQPQvKqeZBws7rBQEZaRamDWftxCkEYZs4Qh
V2l4IVlrawdtRmQlcQh8McrpqP/97Gz8pEDEYnqA7kqBTqZw0Z5o0WsshGSwiAQ9wNSym4xHejkq
zrKxWP8XCMkcT0NlKlRMoqKKICFKZbqWeSQkQM5y9OEcmB6inNNkJCoM1Ip48+cK3cOE6dqXNVrl
sSTZ8WQKwoB3dJmcYqexR3kAvBYdX6ZxEF+2+6b9h8+tc5G7Y5R2eqycyUossdkCcI3fNVhyc72P
axCjZFWZUgfDGCxg1WNhStrH9L8c59P35JKKug==
Since i don't have the private key, i can't decrypt, but at least this produces the right lengthed values.
So try this (you need bouncycastle for reading pem):
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
var keyBytes =
Convert.FromBase64String(
"MIIBI...."); // your key here
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
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);
byte[] plaintext = Encoding.UTF8.GetBytes("amount=1&currency=AED");
byte[] ciphertext = rsa.Encrypt(plaintext, false);
string cipherresult = Convert.ToBase64String(ciphertext);

Why always give me false in signature?

I try in this code to verify my code, I have public key , my data and signature.I am read my signature from file and convert my string to public key then get my data and verify to signature.
public static boolean verify () {
String publickey = "MIGfMA0GCSqGSIb3DQE";
byte[] encKey = Base64.decodeBase64(publickey.getBytes());
try {
byte[] MACaddress = GetData();
BufferedReader in = new BufferedReader(new FileReader(
"EndSignatuer.txt"));
FileInputStream keyfis = new FileInputStream("EndSignatuer.txt");
byte[] Signen = new byte[keyfis.available()];
keyfis.read(Signen);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
byte[] deSignen = Base64.decodeBase64(Signen);
byte[] decrypted_digest = cipher.doFinal(deSignen);
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
md5_digest.update(MACaddress);
byte[] digest = md5_digest.digest();
if (decrypted_digest == digest) {
return true;
}else {
return false;//her why give me false
}
Code encryption:
public static void GenarationKEY(byte[] data) {
try {
File fileEndSignatuer = new File("EndSignatuer.txt");
FileOutputStream fopEndSignatuer = new FileOutputStream(
fileEndSignatuer);
// /Read private key from file
FileInputStream keyfis = new FileInputStream("PiveteKey.txt");
byte[] PrivateKeyB = new byte[keyfis.available()];
keyfis.read(PrivateKeyB);
keyfis.close();
byte[] decodePrivetekey = Base64.decodeBase64(PrivateKeyB);
// /get private key
PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(
decodePrivetekey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privKey = keyFactory.generatePrivate(pubKeySpec);
// / make hash
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
// /make encoding
MessageDigest md5_digest = MessageDigest.getInstance("MD5");
byte[] digest = md5_digest.digest(data);
byte[] cipherText = cipher.doFinal(digest);
byte[] degnatureencode = Base64.encodeBase64(cipherText);
fopEndSignatuer.write(degnatureencode);
fopEndSignatuer.flush();
fopEndSignatuer.close();
}
First of all, what you do is not quite RSA digital signature. Use Signature class instead of combination Cipher and MessageDigest. Or if you insist to do it on low level, then consult specification, particularly section 9.2.
Secondly, string MIGfMA0GCSqGSIb3DQE doesn't represent RSA public key neither it is proper Base64-encoded data.
Also you want to use Arrays.equals(byte[], byte[]) instead of equality operator, as the latter just ensures that array object is the same, while the former compares actual content of the arrays.

Categories