I have the public modulus and exponent for a key. I am encoding a test piece of text and getting different results than the java code that I am trying to replicate.
The java code is here:
RSAPublicKeySpec rsaPublicSpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(rsaPublicSpec);
X509EncodedKeySpec encodedPublicKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance(“RSA”);
PublicKey publicKey = keyFactory.generatePublic(encodedPublicKeySpec);
Cipher cipher = Cipher.getInstance(“RSA / ECB / PKCS1Padding”);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(clearTextString.getBytes());
BASE64Encoder b64 = new BASE64Encoder();
String base64EncodedStr = b64.encode(encryptedBytes);
What I have in C# right now using Bouncy Castle is:
BigInteger publicModulus = new BigInteger(1, Convert.FromBase64String(publicKeyString));
BigInteger publicExponent = new BigInteger(1,Convert.FromBase64String("AQAB"));
RsaKeyParameters pubParameters = new RsaKeyParameters(false, publicModulus, publicExponent);
IAsymmetricBlockCipher eng = new Pkcs1Encoding(new RsaEngine());
eng.Init(true, pubParameters);
byte[] plaintext = Encoding.UTF8.GetBytes("test data");
byte[] encdata = eng.ProcessBlock(plaintext, 0, plaintext.Length);
Console.WriteLine(Convert.ToBase64String(encdata));
I'm a confused about why the java code gets the public key and then does the X509 version (and whether I need to do that in the C# implementation).
I'm also not sure if I need to compensate for endianness of c# vs. java.
Appreciate some help.
The confusing code didn't do anything. Also, Bouncy Castle wasn't actually needed once I got the modulus as a base64 string. Here's what worked.
var publicKey =
"<RSAKeyValue><Modulus>base64modulusgoeshere</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var sbytes = Encoding.UTF8.GetBytes(s);
var rsa = new RSACryptoServiceProvider(2048);
rsa.FromXmlString(publicKey);
var encdata = rsa.Encrypt(sbytes, false);
enc = Convert.ToBase64String(encdata);
Related
I'm using Java with Bouncy Castle to convert a server-side string_1 to ECDH PublicKey, then convert the PublicKey to string_2 to check the code's correctness, but string_1 and string_2 are different and I don't know where's wrong.
String key ="BJNiB1+k2n2hiYZccfwGE87mbo5+29l+PFfKzO72tvDmXCAT4QtxqqgX2vUi5go1+MpjJCFyBmIfv5jcsOo9fo3cd/v2jwZAyh2IHh7NHWkA6PFip6el2rNfvyNCd5eN6Q==";
System.out.println("server key:" + key);
byte[] keyBytes = Base64.decodeBase64(key.getBytes("utf-8"));
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp384r1");
KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("secp384r1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), keyBytes);
ECPublicKeySpec pubKeySpec1 = new ECPublicKeySpec(point, params);
PublicKey pk = kf.generatePublic(pubKeySpec1);
System.out.println("convert Key*: " + Base64.encodeBase64String(pk.getEncoded()));
The server key and convert key are different, can't find the reason, need help!
server key:BJNiB1+k2n2hiYZccfwGE87mbo5+29l+PFfKzO72tvDmXCAT4QtxqqgX2vUi5go1+MpjJCFyBmIfv5jcsOo9fo3cd/v2jwZAyh2IHh7NHWkA6PFip6el2rNfvyNCd5eN6Q==
convert Key*: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEk2IHX6TafaGJhlxx/AYTzuZujn7b2X48V8rM7va28OZcIBPhC3GqqBfa9SLmCjX4ymMkIXIGYh+/mNyw6j1+jdx3+/aPBkDKHYgeHs0daQDo8WKnp6Xas1+/I0J3l43p
I have a Java working sample app (which uses Bouncy Castle) that I need to port to C# (I'm using Bouncy Castle for C# too).
The code is almost the same. However, even when I provide exactly the same modulus and exponent for both, the result arrays are completely different also the strings.
Reiterating: The Java excerpt is the code that works
Where do I'm getting wrong? Thank you in advance!
Java:
public static String encodeRSA(String keyModulus, String keyExponent,
String data) {
try {
byte btMod[] = Base64.decode(keyModulus);
byte btExp[] = Base64.decode(keyExponent);
BigInteger modulus = new BigInteger(1, btMod);
BigInteger pubExp = new BigInteger(1, btExp);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, pubExp);
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherData = cipher.doFinal(data.getBytes());
String tmp = new String(Base64.encode(cipherData));
System.out.println(tmp);
return tmp;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return "";
}
C#:
private static string EncodeRSA(string modulus, string exponent, string data)
{
//Base64, DotNetUtilities functions and BigInteger type are from Bouncy Castle
byte[] btMod = Base64.Decode(modulus);
byte[] btExp = Base64.Decode(exponent);
BigInteger mod = new BigInteger(1, btMod);
BigInteger exp = new BigInteger(1, btExp);
RsaKeyParameters bcKeySpec = new RsaKeyParameters(false, mod, exp);
RSAParameters keySpec = DotNetUtilities.ToRSAParameters(bcKeySpec);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(keySpec);
byte[] plaintext = Encoding.UTF8.GetBytes(data);
byte[] ciphertext = rsa.Encrypt(plaintext, false);
string cipherresult = Encoding.UTF8.GetString(Base64.Encode(ciphertext));
return cipherresult;
}
Modulus:
gdBAMJVXCuEGhX0b1hPAggpD7Ayi33JhsARksGkEatQsdox3BG3bTR/vz8M4vZe74EZj0aZrk0rGJGmAEJZ9GlXq6JzIRYBW5zULsBoPDq4spgobECJLsXq8CnZzOrOM+meIXFhoK8Jyob4X9q62HkDwhMMyqsBG0epWMHPIgkU=
Exponent:
AQAB
Output:
Java output for the entry "1]teste]111111]MTExMTExMTExMTExMTExMQ=="
using the given modulus/exponent
dUCVsGrZIwSyh0ZAxon3wMSPPoQqflpRNtQ5c+TILuOR/5IihABJpZRL6E1TjYs62WXvQUbeFqRYbdAvbjY3YZk+aSviBosdN54+T8+/5agjveeDBi6LXu6r1+KBriq2K1ULg9YC62SrSbRN8VMJ9gkgatF2ux06PyouJOPJPN8=
EDIT - C# Output with given entry, modulus and exponent
CHyg5J+OMuG9H9S7R24Lg2iXeLN/Rgh7XcyDQJqMNZobH0V1hqe2dxrcE3R+UrVl/aDWJg3aXNtP3+8YFA17fLr9yIbIYv5o2zeRMdHbyrW/z26JGaynsay096KEzJ0uBAACJQ3LZryd5ei7zzo77Bnka2Un7C9TJvldswhldxM=
The output of RSA encryption, or any secure encryption method, outputs data that is indistinguishable from random to an attacker. This is performed by the IV for symmetric ciphers and by the padding method for RSA. If this wasn't the case then an attacker would be able to see similarities for different ciphertext; encrypt "yes" twice and the same ciphertext would appear. So an attacker could easily distinguish E(pk, "yes") | E(pk, "yes") from E(pk, "yes") | E (pk, "no").
So Java and C# both output a ciphertext that is precisely the size of the modulus before encoding. However the plaintext is first padded with secure random data before the modular exponentiation used for RSA. The way to verify that the ciphertext generation was correct is by decrypting the ciphertext using the private key. Actually, if you have multiple runs of either Java or C# you will find that the ciphertext keeps changing even within the same language/runtime.
I'm newer to RSA and used BC get SubjectPublicKeyInfo from a public key.
String key = "-----BEGIN RSA PUBLIC KEY-----\n" +
"........\n" +// Multiple lines here
"-----END RSA PUBLIC KEY-----\n";
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) reader.readObject();
And then I want encrypt datas. I found some one use RSAEngine to do it:
AsymmetricKeyParameter aKey = (RSAKeyParameters) PublicKeyFactory.createKey(subjectPublicKeyInfo);
AsymmetricBlockCipher engine = new RSAEngine();
engine.init(false, aKey);
byte[] dataEncrypted = engine.processBlock(data, 0, data.length);
After I run those code, I found the result isn't equal to the expection. So I want to know is there any mistake in my code?
Finally I found my way out.
If anyone is familiar with BouncyCastle, he can point me the low-level mistakes.
Firstly, to encrypt data should init Engine with true at first arg in init function.
Secondly, my public key is start with '-----BEGIN RSA PUBLIC KEY-----'. It's PKCS#1 format RSA public key and should use BouncyCastle to read in, but encrypt data should have padding. So, I shouldn't use RSAEngine directly, use PKCS1Encoding instead.
At last , post my encrypt code and decrypt code:
Encryption:
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) reader.readObject();
RSAKeyParameters rsaKeyParameters = (RSAKeyParameters)
PublicKeyFactory.createKey(subjectPublicKeyInfo);
PKCS1Encoding engine = new PKCS1Encoding(new RSAEngine());
engine.init(true, rsaKeyParameters);
return engine.processBlock(data, 0, data.length);
Decryption:
public static byte[] decryptByPublicKey(String data, String key) throws Exception {
byte[] rawData = Base64.decode(data);
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
PEMKeyPair pemKeyPair = (PEMKeyPair) reader.readObject();
SubjectPublicKeyInfo publicKeyInfo = pemKeyPair.getPublicKeyInfo();
PrivateKeyInfo privateKeyInfo = pemKeyPair.getPrivateKeyInfo();
RSAKeyParameters rsaKeyParameters = (RSAKeyParameters)
PrivateKeyFactory.createKey(privateKeyInfo);
PKCS1Encoding engine = new PKCS1Encoding(new RSAEngine());
engine.init(false, rsaKeyParameters);
return engine.processBlock(rawData, 0, rawData.length);
}
For encryption , you can use modulus and public exponent to create a key that support by JDK:
Security.addProvider(new BouncyCastleProvider());
PEMParser reader = new PEMParser(new StringReader(key));
PemObject obj = reader.readPemObject();
org.bouncycastle.asn1.pkcs.RSAPublicKey rsaPublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(obj.getContent());
BigInteger modulus = rsaPublicKey.getModulus();
BigInteger publicExponent = rsaPublicKey.getPublicExponent();
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");//This line should use right padding.For PKCS#1 format RSA key , it should be this.
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
See Also:Basic RSA example.
I am trying to encypt some data using RSA public key and signing with SHA-512 algo. But response recevied is differnet in different plat
form.
In C#:
RSACryptoServiceProvider crypto = new RSACryptoServiceProvider();
crypto.ImportCspBlob(Convert.FromBase64String(publickey));
crypto.exportParameters(false); // and got the public key modulus and exp
byte[] response = crypto.SignData(data, "SHA512");
In Java:
// got modulus and exp for public key from c#
byte[] modulo = {.....};
byte[] exp = {1,0,1};
BigInteger modulus = new BigInteger(1, modulo);
BigInteger pubExp = new BigInteger(1, exp);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec priKeySpec = new RSAPublicKeySpec(modulus, pubExp);
RSAPublicKey Key = (RSAPublicKey)keyFactory.generatePublic(priKeySpec);
// Calculate Hash
MessageDigest sha1 = MessageDigest.getInstance("SHA-512");
byte[] digest = sha1.digest(data);
// Encrypt digest
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, Key);
byte[] response = cipher.doFinal(digest);
but both response are not matching.C# generate correct one but java not generating the same byte[]
Any missing part in java code.
private static final String algorithm = "AES/CBC/NOPadding";
if we convert type 中國傳統的 languages then we get null value when we encryt.To overcome this problem we use below thing.
private static final String algorithm = "AES/CBC/PKCS5Padding";
if we do diifferent type languages like 中國傳統的 converion can be encryted.
We are facing an issue while trying to Encrypt data in Android and Decrypt it in a WCF Service. The Android code to encrypt data is as follows:
try{
String strModulus = "tr82UfeGetV7yBKcOPjFTWs7pHqqr/5YKKWMUZ/HG4HnCmWrZsOhuR1FBnMZ/g2YiosoSlu0zd7Ukz9lX7wv2RLfWXfMvZYGpAAvfYWwzbyQ2i1q+tKE/thgKNscoSRellDD+uJcYn1H4hnaudVyYJH9miVhOKhKlExMzw8an6U=";
String strExponent = "AQAB";
byte[] modulusBytes = Base64.decode(strModulus, Base64.DEFAULT);
byte[] exponentBytes = Base64.decode(strExponent, Base64.DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA/ECB/PKCS1Padding");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = new String("Manchester United").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal(plainBytes);
encryptedString = Base64.encodeToString(cipherData, Base64.DEFAULT);
}
catch(Exception e){
Log.e("Error", e.toString());
}
return encryptedString;
The same code in Java:
try{
String strModulus = "tr82UfeGetV7yBKcOPjFTWs7pHqqr/5YKKWMUZHG4HnCmWrZsOhuR1FBnMZ/g2YiosoSlu0zd7Ukz9lX7wv2RLfWXfMvZYGpAAvfYWwzbyQ2i1q+tKE/thgKNscoSRellDD+uJcYn1H4hnaudVyYJH9miVhOKhKlExMzw8an6U=";
String strExponent = "AQAB";
byte[] modulusBytes = DatatypeConverter.parseBase64Binary("tr82UfeGetV7yBKcOPjFTWs7pHqqr/5YKKWMUZ/HG4HnCmWrZsOhuR1FBnMZ/g2YiosoSlu0zd7Ukz9lX7wv2RLfWXfMvZYGpAAvfYWwzbyQ2i1q+tKE/thgKNscoSRellDD+uJcYn1H4hnaudVyYJH9miVhOKhKlExMzw8an6U=");
byte[] exponentBytes = DatatypeConverter.parseBase64Binary("AQAB");
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = new String("Manchester United").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal(plainBytes);
//String encryptedString = Base64.encodeToString(cipherData, Base64.NO_PADDING);
String encryptedString = DatatypeConverter.printBase64Binary(cipherData);
String encryptedData = encryptedString;
}
catch(Exception e){
}
}
WCF Service:
public string Decrypt()
{
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "Tracker";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(cspParams);
rsa1.FromXmlString("<RSAKeyValue><Modulus>tr82UfeGetV7yBKcOPjFTWs7pHqqr/5YKKWMUZ/HG4HnCmWrZsOhuR1FBnMZ/g2YiosoSlu0zd7Ukz9lX7wv2RLfWXfMvZYGpAAvfYWwzbyQ2i1q+tKE/thgKNscoSRellDD+uJcYn1H4hnaudVyYJH9miVhOKhKlExMzw8an6U=</Modulus><Exponent>AQAB</Exponent><P>6e0nv7EBFBugtpoB+ozpg1J4js8E+DVyWCuBsERBPzqu4H7Z/oeLIRSC8Gi5GZgrCpBf3EvyIluM7rzaIfNThQ==</P><Q>x/29X9ns1WcXC42IJjLDjscz5ygdVh79dm8B2tQVbqwyhDsQ6OIOQdu5+eHf4hUMoTrM9KkS2F6FGlLXuaOFoQ==</Q><DP>kTS2LMaJ/dpce5zDx6w6s1q5HSSiWBSNIu/2s9zah448yXvUg6vNkD40PVk0NRAA/7C44H2AExWzOOqfmN17JQ==</DP><DQ>xtAx9drQPWnpl/uQUOEAVa0kpPTVDStrr9Q1FNTnpYkcAyYw7kLkB4anAIoSpk9kqdeprsNxz5VPXtbiTFMKYQ==</DQ><InverseQ>O0594NMjnjSp+/NAa1kQxoQNzn1qqq+p1Zb+kT3/jRc/0d7ZnqSSpxFMXfxx3yZkNAOPDOdbckPQbRZ13RKBHg==</InverseQ><D>bjVEagwvkrZaTt9CTW1hd3362weLFlX6DpE/3R3RcrpVfkSwKGpEhqGrNeeGPlsuqiaf5rAFir4eTqrF1QVliKsU4XE0RyzP5lHGc7dlX4DOHMjs2R9nNWv8QOTPoaRuLrLGorqBXlw/jQPxFI6gQzkIIjzuf//lDVnFam3dw4E=</D></RSAKeyValue>");
string data2Decrypt = "LyVNDhkdJ5jNgwZDiVZ1R0lmd10AQgqNDFHh2vJB1676eg8wj0MOdTyChAGrvEjha0uXg+f/aNBAc4+/LFbCgsA1e+O3wnXr27sXznGJ9G15avZzQHG4JWUS42MXBahAkcJ80pcihTbL9edfQCkEuj9RzQ/zFJyDEMssfd/EPDM=";
byte[] encyrptedBytes = Convert.FromBase64String(data2Decrypt);
byte[] plain = rsa1.Decrypt(encyrptedBytes, false);
string decryptedString = System.Text.Encoding.UTF8.GetString(plain);
}
Original Data : 'Manchester United'
The strange thing is that if I encrypt a string with the Java code it can be decrypted at the WCF service end, however, if it is encrypted using the above Android code, the decryption provides the error: "The data to be decrypted exceeds the maximum for this modulus of 128 bytes".
On debugging, I realized that the byte[] cipherData is not the same for Android and Java. All the previous values happen to be in sync. This generates different Encrypted Strings in Java and Android.
My question is why does this happen? Is there a silly mistake? Is there a way to work around it? Or something else that can be done in Android to get the 'Cipher Data' right?
Any help will be appreciated.
You are using a different padding on Android. Use the exact same padding on Android as well:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");