I am integrating with a .Net application which uses Symmetric encryption. My application is on Java. Following is the .Net code which was shared by the team which uses the Symmetric encryption;
public static string SymmetricEncrypt<T>(string value, string PrivateKey, string SALT_STRING) where T : SymmetricAlgorithm, new()
{
PasswordDeriveBytes rgb =
new PasswordDeriveBytes(PrivateKey ,
Encoding.Unicode.GetBytes(SALT_STRING));
SymmetricAlgorithm algorithm = new T();
byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);
ICryptoTransform transform = algorithm.CreateEncryptor(rgbKey, rgbIV);
using (MemoryStream buffer = new MemoryStream())
{
using (CryptoStream stream =
new CryptoStream(buffer, transform, CryptoStreamMode.Write))
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(value);
}
}
return Convert.ToBase64String(buffer.ToArray());
}
}
Looking at the .Net documentation, I could see that SymmetricEncrypt class by default uses Rijndael encryption. And also I have found a class similar to "PasswordDeriveBytes" implementation in bouncy castle which implements the "PKCS5S1" algorithm for key generation using the salt with an iteration count of 100. But still I am unable to generate the exact encryption to what is required by the .Net application. Following code is what i have tried so far;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import mtnsa.sorb.handler.PKCS5Test.PasswordDeriveBytes;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Base64;
public class EncryptionHelper {
public static void main(String[] args)throws Exception {
encrypt("FTTH","HhN01vcEEtMmwdNFliM8QYg0Y89xzBOJJG7BHARC7g", "002400000480000094000000060200000024000052534131000400000100010085525e9438e9fae122f71ec7124" +
"443bf2f9f57f5f3760b3704df168493004b9ef68413f500d54fa9fa3869b42b1e2365204826e54b618d56e7e575f2" +
"7f675f0eae3ea8458a8ee1e92dc3f4bfc34fbe23851afa9d2c28fc8cd5b124f60a03a06bfb598bc3acbd8c4380ae" +
"f02cc58bdf955d140390f740a7e115c59e3b3b5758ca");
}
public static String encrypt(final String valueToEncrpt, final String saltString, final String privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
String encrptedValue = null;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] password = privateKey.getBytes();
byte[] salt = saltString.getBytes();
PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
generator.init(password, salt, 100);
byte[] keyArr = ((KeyParameter)generator.generateDerivedParameters(128)).getKey();
byte[] IvArr = ((KeyParameter)generator.generateDerivedParameters(128)).getKey();
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyArr,"AES"),new IvParameterSpec(IvArr));
byte[]test = cipher.doFinal(valueToEncrpt.getBytes());
System.out.println(new String(Base64.encode(test)));
return encrptedValue;
}
}
In the Java code i have given the sample SALT key and the PRIVATE key used for testing purposes. But still I am unable to get the exact value as generated by the .Net application. Following is an example plain text and the encrypted value which i got from the .Net application which i have failed to replicate in my Java code.
Plain text value : FTTH
Encrypted value : MjgmbdT3Vg6RW/7K1BjQ/Q==
Any help on this is much appreciated as i am currently out of ideas.
Thank you everyone for the valuable comments. I have requested the .Net module authors to change their implementation to use RFC2898DeriveBytes
Related
I want to encrypt my password in angular side before sending it to Springboot API. My idea is to do:-
Generate public key and private key in java
Do a base64 encoding on both public key and private key
Use encoded public key in node (angular) side, this will be used for encryption
Use encoded private key in springboot rest api, this will be used for decryption
Below is my java code which generates, encrypts and decrypts password
RSAKeyPairGenerator.java
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
public class RSAKeyPairGenerator {
private static String privateKeyString = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALyR1WRqVBuvdmXmgrcZ4VMY4hIFTYt63NGEN9atlgnngNliMasvzBofnjRtk5mukezUKujtON6mPnAjKJoEDdiaw3Rk0hKvgW5MszyUBE5bLThexVLbi+nQYSf2GSvd0fjdZn6KjhgVOdbYe+JduU/iPD3/Ti5p+w3MewgZpy2RAgMBAAECgYAqWdqCXfsb6LF/u2C6PN7Faf5EK9q5q9NyXu6nkX70JIFk0U/0cZy2dUlz3vRafMGbXh9xBu5R2yaEyvCwfp6ZF26A8MbS/IaI53LMKmyclhES2f3tZLKUPkg6fntz+e6e/6oSDdQPHP3J2QrpuwzyW+UZJQmmYYj7f9sq/+dawQJBAOnkHWkLuIZNIgLzV0TuHA6fcOXrqrSlS1/Q0qLyaQEZOObocXDvWaCSP+8RXiqZSwAnMJbrGHQA5ULXLWI4GjkCQQDOZP6bfn2VV3m8JSlYL9Uz4TrRYb8Qe3/mohhMsyDB7Ua/6d9BV7JZgbYiXP0utobKVWLxD49OFMkgEs20qY4ZAkB4dJsQ9pBZ2m+hxWE0hsy8WzDxuKV504c2GX3hnaamgi7j/OIvn5UxNSDoJrGwjrIpqgVENF+rnqpz+g3Nf8dBAkBV/op+6yMUGFBmbe1eCwAAD7XcC6f6DBrsU1lgi7n4Uw6JY75bkViEJqFmi+wJjI94uj7xRZRl6g8qx+rhfUvxAkEArD1we6ZLTWz73D8z+4Boj7Su7b5LhfgvXVL5V4Zdog3X58pHK+dYppapDh5KjZZdor+y/6uz1PqIs2dmFy/xtQ==";
private static String publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kdVkalQbr3Zl5oK3GeFTGOISBU2LetzRhDfWrZYJ54DZYjGrL8waH540bZOZrpHs1Cro7Tjepj5wIyiaBA3YmsN0ZNISr4FuTLM8lAROWy04XsVS24vp0GEn9hkr3dH43WZ+io4YFTnW2HviXblP4jw9/04uafsNzHsIGactkQIDAQAB";
private static PrivateKey privateKey;
private static PublicKey publicKey;
public void generateKeys() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair pair = keyGen.generateKeyPair();
RSAKeyPairGenerator.privateKey = pair.getPrivate();
RSAKeyPairGenerator.publicKey = pair.getPublic();
}
public void readKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
byte [] encoded = Base64.getDecoder().decode(privateKeyString);
PKCS8EncodedKeySpec keySpec1 = new PKCS8EncodedKeySpec(encoded);
RSAKeyPairGenerator.privateKey = kf.generatePrivate(keySpec1);
encoded = Base64.getDecoder().decode(publicKeyString);
X509EncodedKeySpec keySpec2 = new X509EncodedKeySpec(encoded);
RSAKeyPairGenerator.publicKey = kf.generatePublic(keySpec2);
}
public static void main(String[] args) throws NoSuchAlgorithmException, IOException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator();
// Next line is for testing with encrypted text generated by nodejs code with genrated keys
// keyPairGenerator.generateKeys();
keyPairGenerator.readKeys();
System.out.println("-----Private----------");
System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
System.out.println("Encoding : "+ privateKey.getFormat());
System.out.println("----------------------");
System.out.println("-----Public----------");
System.out.println(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("Encoding : "+ publicKey.getFormat());
System.out.println("----------------------");
String secretMessage = "Test";
System.out.println(secretMessage);
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
// Next lines are for testing decryption with encrypted text generated by nodejs code with generated keys
// byte[] encryptedMessageBytes = "B��u���<⌂�lǚm0�W������1�%EN�7��‼��l��l�<�����k;�������GĦ9D8��Z��I�Oɺ♫��→P'�§�{k�Ɋ+-}?��rZ".getBytes();
// byte[] encryptedMessageBytes = Base64.getDecoder().decode("woboAUytDXJLlKm7zbqNdxVORG+kio9kZxvMPOHruQfxwNEx7SVFTsw3oeETqbRs4NZskjzO2Nzjyms73vv758Dcy0fEpjlEOKmrWuG62knOT8m6DqGDGlAn1hXpe2u6yYorLX0/6fhyWg1C/JR1sbaKPH/Xt+Fsx5ptMONX0uw=");
System.out.println(new String(encryptedMessageBytes));
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
System.out.println(decryptedMessage);
}
}
This gives me following output
-----Private----------
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMv+t+D6/7ZY6m+CjD97NB989P2YjmGcE2yHUokyjmxVi/SxIu9MboiVNewR+mFjTFYPGgMWHz1/XeVGlinjN/5+0fdiO/JEvEUilFmkkIF3My11I09b9NZ9RJmpCNCbqOg7NSt6VD8IC/w19N25xwcofc3qbgfEjKSs0CgxfahrAgMBAAECgYEAiyLlEBKiryDeZchJGFNULdXw07dmBbWKmg+CgAl3kvSWTQM0rLsY+Rese6OXfy1XN6t9NnW0QSHKTUNj0JYl7btKEblVHERbjwavUe83tcNIc3o0xrGoBAzma14ZicPYm70JWixTO778lm5AXKl504+oOQyVYmfgXcevPXwdIkECQQDvO3DBuu6wp5nbwR4t0736HOH9jVIjJOS8oABUARDhNt1fkb8uEptDr5EomVRMqarJMAjSMpYxrUCpvgtf4nZ5AkEA2ksDr8pbbFvZPaAe0F43LmJtFM3PvCz87S93p20YjwVMmGV17sJ5t7a26HtubgRLEWq4z6rxq2oXPv4y1lstAwJAbj9cVUtKWIrEcutqdwAPmsXYt7p60ctcxjiOLihXmRJprnNCQX89olG0eZs/qBzAofrK9eNuJ/KJzC/SmhuJMQJAAz9gc6oQCCGprrgGHVV5frAqLUgOkh8dOC4fmpcN6XrLs+y2f3HXO7t1JypG704TC9RJoZVKeSFf7Sj8+qFqnwJBALRiQc9SG5Ht5TLo12W4l3bRaxpbo597BgKRjz3hqiySdqjZA9Qe84qmEKveZ9eWehbfqwzKmCbW43I1ZEFZ95o=
Encoding : PKCS#8
----------------------
-----Public----------
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/rfg+v+2WOpvgow/ezQffPT9mI5hnBNsh1KJMo5sVYv0sSLvTG6IlTXsEfphY0xWDxoDFh89f13lRpYp4zf+ftH3YjvyRLxFIpRZpJCBdzMtdSNPW/TWfUSZqQjQm6joOzUrelQ/CAv8NfTduccHKH3N6m4HxIykrNAoMX2oawIDAQAB
Encoding : X.509
----------------------
Test
�N::�`��\A���ƈ�~��5s���
�0�I�C�uƹx2�Z&Kں�"ьC�$
q��K���h�^<�5�E�Ɨ0B�͒Z�{��EDbDH�.��#:�e��h~j���������q�c��R�
Test
In the Nodejs side I am using the above public to encrypt my text.
index.js
const crypto = require("crypto");
var pubkey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/rfg+v+2WOpvgow/ezQffPT9mI5hnBNsh1KJMo5sVYv0sSLvTG6IlTXsEfphY0xWDxoDFh89f13lRpYp4zf+ftH3YjvyRLxFIpRZpJCBdzMtdSNPW/TWfUSZqQjQm6joOzUrelQ/CAv8NfTduccHKH3N6m4HxIykrNAoMX2oawIDAQAB\n-----END PUBLIC KEY-----";
var plaintext = "Test"
var buffer = new Buffer.from(plaintext, "utf8");
var enc = crypto.publicEncrypt(pubkey, buffer);
var bs64 = enc.toString('base64');
console.log(enc.toString());
console.log(enc.toString('base64'));
this gives me following output
B��u���<⌂�lǚm0�W������1�%EN�7��‼��l��l�<�����k;�������GĦ9D8��Z��I�Oɺ♫��→P'�§�{k�Ɋ+-}?��rZ
woboAUytDXJLlKm7zbqNdxVORG+kio9kZxvMPOHruQfxwNEx7SVFTsw3oeETqbRs4NZskjzO2Nzjyms73vv758Dcy0fEpjlEOKmrWuG62knOT8m6DqGDGlAn1hXpe2u6yYorLX0/6fhyWg1C/JR1sbaKPH/Xt+Fsx5ptMONX0uw=
Now I tried using both the strings in java side for decryption.
When I try decoding the string, it gives me following error
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:346)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at com.mindtree.starr.wsr.RSAKeyPairGenerator.main(RSAKeyPairGenerator.java:90)
And, when I try decoding the base64 string, it gie me following error
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:379)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:290)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:365)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at com.mindtree.starr.wsr.RSAKeyPairGenerator.main(RSAKeyPairGenerator.java:90)
What am I doing wrong?
The problem was clearly described in the first comment and the solution is in second comment.
Correct transformation string for Cipher instance that worked for me is RSA/ECB/OAEPWithSHA-1AndMGF1Padding
So below instantiation for decryption Cipher works perfectly.
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
I know that there are many threads over this , but i am not able to find solution with them.
Problem Statement:
Need to encrypt data. Below is my code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
public class CryptoUtil {
private static final String AES = "AES/ECB/PKCS5Padding";
public String encryptMessage(final String message, final byte[] dataKey) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(dataKey, AES);
try {
Cipher cipher = Cipher.getInstance(AES);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedMessage = cipher.doFinal(message.getBytes());
return Base64.getEncoder().encodeToString(encryptedMessage);
}
catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
throw new Exception("Error while encrypting application credentials", e);
}
}
}
I need to do it using BountyCastle service provider. So i followed the steps mentioned here and this. I am using JAVA 11, but i switched to JAVA 8 to follow mentioned link.
In my code i added static block to add BountyCastle as service provider
static {
BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
Security.addProvider(bouncyCastleProvider);
}
But, it is not getting added as verified providers. So i am getting the issue.
Any idea how to handle. How to add BouncyCastle as verified provider.
I am running on local machine , and need to create JAR.
Nobody has answered this, so I'll just post an answer with the comment I had above. I have not tested it so I leave that part to you.
When you do Cipher.getInstance(), it is correct to have "AES/ECB/PKCS5Padding" (for some weird definition of "correct", where Java providers believe PKCS5 padding = PKCS7 padding, but never mind that).
But when you create the key, it is not correct to have the mode and padding in there. Instead, they just want the algorithm. So try:
SecretKeySpec secretKey = new SecretKeySpec(dataKey, "AES");
The change is the quotes around "AES", rather than using AES="AES/ECB/PKCS5Padding".
Not sure if that will fix your problem, but should be a step in the right direction (I hope).
According to section 5.2 (Two GCM Functions) of the Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC, it mentions that for the case of GMAC, the authenticated encryption and decryption functions become the functions for generating and verifying an authentication tag on the non-confidential data.
Looking online and on the JCA reference page, I understand that GMAC is generated like this:
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();
However, I have doubts regarding the GMAC verification. Is this the right way of verifying a GMAC?
Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();
Where verifiedGmac size == 0?
Here is the full code with inputs and outputs:
import org.hamcrest.core.Is;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static javax.xml.bind.DatatypeConverter.parseHexBinary;
import static javax.xml.bind.DatatypeConverter.printHexBinary;
import static org.junit.Assert.assertThat;
public class TestGmac {
#Test
public void generateAndVerifyGmac() throws Exception {
final byte[] message = parseHexBinary("AAAAAAAAAAAA");
final byte[] authenticatedKey = parseHexBinary("63509E5A672C092CAD0B1DC6CE009A61");
final byte[] aadData = buildAadForAuthenticationOnly(message, authenticatedKey);
final byte[] iv = parseHexBinary("BBBBBBBBBBBBBBBBBBBBBBBB");
SecretKeySpec encryptionKey = new SecretKeySpec(parseHexBinary("55804F3AEB4E914DC91255944A1F565A"), "AES");
// Generate GMAC
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();
assertThat(printHexBinary(gmac), Is.is("44C955D63799428524E97993"));
// Verify GMAC
Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();
assertThat(printHexBinary(verifiedGmac), Is.is(""));
}
private byte[] buildAadForAuthenticationOnly(byte[] message, byte[] authenticatedKey) throws IOException {
ByteArrayOutputStream aaDoutputStream = new ByteArrayOutputStream();
aaDoutputStream.write(parseHexBinary("10"));
aaDoutputStream.write(authenticatedKey);
aaDoutputStream.write(message);
return aaDoutputStream.toByteArray();
}
}
Yes, that is correct. The Java API verifies the MAC, throwing an exception if it fails. It returns the encrypted plaintext message, but in your case the message is empty.
There are two design choices for creating a GCM API: handle the authentication tag separately or assume it is part of the ciphertext. Although I prefer the separate handling of ciphertext and authentication tag, Java/Oracle decided to keep to the AEAD RFC and include the authentication tag with the ciphertext (requiring extra buffering, a code size increase of about 30% and removing the posiblity to perform online decryption).
So although your code maybe feels a bit strange the handing of the MAC seems OK - if it weren't you'd get a AEADBadTagException in the call to doFinal during decryption.
Using a static key and IV certainly isn't OK, but I presume those were used for testing purposes - right?
I have code sample in Java and I need the same functionality in C#. Are there any alternatives to the classes, which are used for the sample?
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import javax.xml.bind.DatatypeConverter;
publicKey = publicKey.replaceAll("-----(BEGIN|END).*", "").trim();
X509EncodedKeySpec spec = new X509EncodedKeySpec(DatatypeConverter.parseBase64Binary(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PublicKey pKey = keyFactory.generatePublic(spec);
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initVerify(pKey);
ecdsaSign.update(stringToVerify.getBytes("UTF-8"));
if (ecdsaSign.verify(new BigInteger(ECDSA, 16).toByteArray())) {
// true
}
There's not anything in .NET (by itself) which can read the public key structure only. But if you can get the entirety of a certificate then you can write the following in .NET 4.6.1:
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Demo
{
public static class DemoClass
{
public static bool ECDsaSha256Verify(string pemCert, string data, byte[] signature)
{
using (var cert = new X509Certificate2(Encoding.ASCII.GetBytes(pemCert)))
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
if (ecdsa == null)
{
throw new ArgumentException("Certificate does not have an ECDSA key.");
}
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
return ecdsa.VerifyData(dataBytes, signature, HashAlgorithmName.SHA256);
}
}
}
}
I wasn't sure where your BigInteger signature value was coming from, so that I have just left as a byte array.
I have to make an app in JS which encodes a message with AES and passes it to a server via AJAX. Then the server and decodes the message using Java.
My question is: How to encrypt a message in JS and be able to decrypt it in Java using AES? Knowing that the communication between java and js is already established via webservices
Client-side, I use the Crypto JS library (http://code.google.com/p/crypto-js/). Server-side I use the Cipher class provided with Java (I use the Java Play framework but it doesn't matter here).
I'm totally new to cryptography. I've made researches all day long and still can't make this work.
The problem is that the key used to encrypt and decrypt the message must be the same, and I don't know how to do this.
From my searches, I understand there is different modes to use AES. By default Java uses ECB and CryptoJS uses CBC which is a problem, but which seems not so hard to fix, by telling CryptoJS to use the ECB mode too. But then there is a padding problem, it seems the only padding available in Java and CryptoJS is no padding at all. But when I use NoPadding in Java I get an exception.
But even if I manage to fix this, the huge problem is that the key generated by CryptoJS and the one generated by Java are not the same. If I encrypt a message in Java the result is always the same, in Hex. But in crypto JS it is in Base64 and it is never the same....
I understand this is caused by the key generation which isn't the same in Java and CryptoJS (then enters the notion of IV and Salt which are blur for me).
Don't do encryption in browser JS; it's impossible to do securely.
Use SSL. Its intended purpose is to encrypt communication between a browser and a server.
If cost is your problem, there are free SSL certificates.
I recently invested days in this problem. I tried a lot a lot of libraries on the java and javascript side. This blog saved my day: http://watchitlater.com/blog/tag/aes/ .
So it can be done! Just use Gibberish AES (https://github.com/mdp/gibberish-aes) an the javascript side and bouncycastle an on the java side.
The java side encryption might look like this(leaned on the above mentioned blog):
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.spec.InvalidKeySpecException;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Encoder;
public class OpenSSLEncryption {
private static final String CIPHER_ALG = "PBEWITHMD5AND256BITAES-CBC-OPENSSL";
private static final Provider CIPHER_PROVIDER = new BouncyCastleProvider();
private static final String PREFIX = "Salted__";
private static final String UTF_8 = "UTF-8";
private String password;
private PBEKeySpec pbeSpec;
private SecretKeyFactory keyFact;
private Cipher cipher;
private Random rand = new Random();
private BASE64Encoder encoder = new BASE64Encoder();
public OpenSSLEncryption(String password) throws NoSuchAlgorithmException, NoSuchPaddingException {
this.password = password;
pbeSpec = new PBEKeySpec(password.toCharArray());
keyFact = SecretKeyFactory.getInstance(CIPHER_ALG, CIPHER_PROVIDER);
cipher = Cipher.getInstance(CIPHER_ALG, CIPHER_PROVIDER);
}
public synchronized String encrypt(String toEncrypt) throws InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, IOException {
byte[] salt = new byte[8];
rand.nextBytes(salt);
PBEParameterSpec defParams = new PBEParameterSpec(salt, 0);
cipher.init(Cipher.ENCRYPT_MODE, keyFact.generateSecret(pbeSpec), defParams);
byte[] cipherText = cipher.doFinal(toEncrypt.getBytes(UTF_8));
ByteArrayOutputStream baos = new ByteArrayOutputStream(cipherText.length + 16);
baos.write(PREFIX.getBytes(UTF_8));
baos.write(salt);
baos.write(cipherText);
baos.close();
return encoder.encode(baos.toByteArray());
}
}