Trying to code signature verification using Sun JCE with test vectors from the CAVP program.
i'm given public key exponent, and modulus, as well as the message and signature. I do the following:
private static boolean verifySignature(String algo, PrintStream oPS, byte[] message, byte[] modulus, byte[] exponent, byte[] sigBytes, int saltlen) throws Exception
{
Signature sig = Signature.getInstance(algo);
KeyFactory keyMaker = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
new BigInteger(modulus),
new BigInteger(exponent));
RSAPublicKey pubKey = (RSAPublicKey)keyMaker.generatePublic(pubKeySpec);
System.out.println("algorithm is " + algo);
sig.initVerify(pubKey);
Utils.outputValue("n", modulus, modulus.length, System.out, false);
Utils.outputValue("e", exponent, exponent.length, System.out, false);
Utils.outputValue("Msg", message, message.length, System.out, false);
Utils.outputValue("S", sigBytes, sigBytes.length, System.out, false);
BigInteger Nvalue = pubKey.getPublicExponent();
Utils.outputValue("key value of n", Nvalue.toByteArray(), System.out, false);
BigInteger Evalue=pubKey.getModulus();
Utils.outputValue("key value of e", Evalue.toByteArray(), System.out, false);
sig.update(message);
if(sig.verify(sigBytes))
oPS.println("Result = P");
else
oPS.println("Result = F");
.
.
.
The algo is something like "SHA1withRSA", and i build the public key as shown. When i list the resulting modulus and exponent from the key itself, they have become reversed.
n = 009de541c71a95389d9e8619ea1d6e2c69ed6c703701e518351676022ab98395d6b35a38b024f92bce6dd1c5be9d51dffd1687d19dceee73f2c73e4436b955231255f6e3e360ba84462311e10e65932fe069bed2d42c5bf2f88141828bdad3796184870cb8cf3019da264e56b39eccf7224d43a1b98d788b40a4042aac790e946f
e = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
Msg = 64b13df4b008ccccd5ce146018481c2568bbe2b93ec658d1c7f4ad734592cd65b3be2be5a7c1be9a7d9f49cbb2ece0cf8ee0a8c406aede84e0121bd51829d6e083862ae5b282d92c19d3923f70616f565a55c2134572116f91a85ff5f4e6ad2e1d31c15c97f3266af19574241c4fd2a4143fb80cc2b9fa7b22df0239a1715c35
S = 5b4458f6aeff91c4699ee9bdf8757987fb8db229814a2992945ac53bcf19b9179e53cfed258726d205107ac000d41e570fe8c4fb321fc9b4b5469c60cd20032195f314ba6e6b0b30a51c9834242daa1ce525ec90380106568e782ea164baeda5d884defe6e720e9dd63618b823412445e17f991a6daa21bd62bdc73d7d8a20e5
key value of n = 03
key value of e = 009de541c71a95389d9e8619ea1d6e2c69ed6c703701e518351676022ab98395d6b35a38b024f92bce6dd1c5be9d51dffd1687d19dceee73f2c73e4436b955231255f6e3e360ba84462311e10e65932fe069bed2d42c5bf2f88141828bdad3796184870cb8cf3019da264e56b39eccf7224d43a1b98d788b40a4042aac790e946f
Some of these tests are supposed to fail, but some should succeed. None verify correctly... Any thought? Thanks.
Application is below, followed by one set of sample input.
package cavp.rsa;
import cavp.*;
import java.io.*;
import java.security.KeyFactory;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.security.PublicKey;
import java.util.Date;
import java.math.BigInteger;
public class RSASigVerify
{
private static boolean verboseFlag=false;
public static void main(String[] args) throws Exception
{
String requestFile;
String responseFile;
int saltLen=-1;
int argstart=0;
if(args.length < 2)
throw new Exception("missing parameters");
if(args.length == 3) {
if(args[0].equals("-saltlen")) {
saltLen=Integer.parseInt(args[1]);
if(saltLen < 0)
throw new Exception("bad -saltlen parameter ");
argstart=2;
} else {
if(args[0].equals("-x931")) {
saltLen=-2;
argstart=1;
}
} else {
if(args.length > 3)
throw new Exception("invalid invocation");
}
requestFile=args[argstart];
responseFile=args[argstart+1];
if(processFile(requestFile, responseFile, saltLen))
System.out.println("Failed to process file " + requestFile);
else
System.out.println("Processing complete for " + requestFile);
}
private static boolean processFile(String rqfn, String rspfn, int saltLen) throws Exception
{
File inFile = new File(rqfn), outFile = new File(rspfn);
if(!inFile.exists() || !inFile.isFile() || !inFile.canRead()) {
System.out.println("Bad input File " + rqfn);
return true;
}
FileReader inFR = new FileReader(inFile);
BufferedReader inBR = new BufferedReader(inFR);
PrintStream outPS = new PrintStream(outFile);
String input, keyword, value, digestType=null;
int p, v, q, i, lnum=0;
byte[] Msg=null, nBytes=null, eBytes=null, sigBytes=null;
boolean msgFound=false, nFound=false, eFound=false, sigFound=false;
try {
while((input = inBR.readLine()) != null ) {
lnum++;
if((p = input.indexOf('=')) < 0) {
outPS.println(input);
continue;
}
if(input.charAt(p-1) == ' ')
keyword=input.substring(0,p-1);
else
keyword=input.substring(0, p);
p++;
for(i=p; i<input.length(); i++) {
if(input.charAt(i) == ' ')
p++;
else
break;
}
if((v = input.indexOf('\n')) > 0) {
for(q=v-1; q>p+1; q--) {
if(input.charAt(q) != ' ')
break;
}
} else {
if((v = input.indexOf(' ', p)) > 0) {
q=v-1;
} else {
q=input.length();
}
}
value=input.substring(p,q);
if(keyword.indexOf("n") >= 0) {
nBytes=Utils.hexStringToByteArray(value);
if(nBytes.length <= 0) {
System.out.println("bad value for modulus");
return true;
}
nFound=true;
} else {
if(keyword.equals("e")) {
eBytes=Utils.hexStringToByteArray(value);
if(eBytes.length <= 0) {
System.out.println("bad value for private exponent");
return true;
}
eFound=true;
} else {
if(keyword.equals("SHAAlg")) {
if(!value.equals("SHA1") && !value.equals("SHA224") && !value.equals("SHA256") && !value.equals("SHA384") &&
!value.equals("SHA512")) {
System.out.println("Bad SHAAlg parameter " + input);
return true;
}
digestType=value + "withRSA";
} else {
if(keyword.equals("Msg")) {
if(msgFound) {
System.out.println("more than one Msg found");
return true;
} else
msgFound=true;
String tempMsg;
if((value.length() & 0x01) != 0) {
tempMsg="0" + value;
} else {
tempMsg=value;
}
Msg=Utils.hexStringToByteArray(tempMsg);
if(Msg.length <= 0) {
System.out.println("bad Msg parameter");
return true;
}
} else {
if(keyword.equals("S")) {
sigBytes=Utils.hexStringToByteArray(value);
if(sigBytes.length <= 0) {
System.out.println("bad signature parameter");
return true;
}
sigFound=true;
} else {
if(keyword.equals("Result")) {
continue;
}
}
}
}
}
}
outPS.println(input);
if(digestType != null && msgFound && nFound && eFound && sigFound) {
if(verifySignature(digestType, outPS, Msg, nBytes, eBytes, sigBytes, saltLen)) {
throw new Exception("error in digest generator");
}
msgFound=false;
eFound=false;
sigFound=false;
}
}
} catch( FileNotFoundException e) {
System.out.println("error processing file");
}
inFR.close();
outPS.close();
return false;
}
private static boolean verifySignature(String algo, PrintStream oPS, byte[] message, byte[] modulus, byte[] exponent, byte[] sigBytes, int saltlen) throws Exception
{
// yet to implement saltLen == -2 for X9.31 padding
// saltLen >= 0 for PSS Signatures
Signature sig = Signature.getInstance(algo);
KeyFactory keyMaker = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(
new BigInteger(modulus),
new BigInteger(exponent));
RSAPublicKey pubKey = (RSAPublicKey)keyMaker.generatePublic(pubKeySpec);
System.out.println("algorithm is " + algo);
sig.initVerify(pubKey);
sig.update(message);
if(sig.verify(sigBytes))
oPS.println("Result = P");
else
oPS.println("Result = F");
return false;
}
}
The following is one vector from the input:
# CAVS 11.5
# "SigVer PKCS#1 Ver 1.5" information for
# Mod sizes selected: 1024 2048
# SHA Algorithm selected:SHA1 SHA256
# Generated on Mon Nov 28 14:03:58 2011
[mod = 1024]
n = 9de541c71a95389d9e8619ea1d6e2c69ed6c703701e518351676022ab98395d6b35a38b024f92bce6dd1c5be9d>51dffd1687d19dceee73f2c73e4436b955231255f6e3e360ba84462311e10e65932fe069bed2d42c5bf2f88141>828bdad3796184870cb8cf3019da264e56b39eccf7224d43a1b98d788b40a4042aac790e946f
SHAAlg = SHA1
e = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>0000000000000000000000000000000000000000000000000000000000000000000000000003
Msg = 64b13df4b008ccccd5ce146018481c2568bbe2b93ec658d1c7f4ad734592cd65b3be2be5a7c1be9a7d9f49cbb2>ece0cf8ee0a8c406aede84e0121bd51829d6e083862ae5b282d92c19d3923f70616f565a55c2134572116f91a8>5ff5f4e6ad2e1d31c15c97f3266af19574241c4fd2a4143fb80cc2b9fa7b22df0239a1715c35
S = 5b4458f6aeff91c4699ee9bdf8757987fb8db229814a2992945ac53bcf19b9179e53cfed258726d205107ac000>d41e570fe8c4fb321fc9b4b5469c60cd20032195f314ba6e6b0b30a51c9834242daa1ce525ec90380106568e78>2ea164baeda5d884defe6e720e9dd63618b823412445e17f991a6daa21bd62bdc73d7d8a20e5*
Here is a vector file which should Pass:
# CAVS 11.5
# "SigVer PKCS#1 Ver 1.5" information for
# Mod sizes selected: 1024 2048
# SHA Algorithm selected:SHA1 SHA256
# Generated on Mon Nov 28 14:03:58 2011
[mod = 1024]
n = 9de541c71a95389d9e8619ea1d6e2c69ed6c703701e518351676022ab98395d6b35a38b024f92bce6dd1c5be9d51dffd1687d19dceee73f2c73e4436b955231255f6e3e360ba84462311e10e65932fe069bed2d42c5bf2f88141828bdad3796184870cb8cf3019da264e56b39eccf7224d43a1b98d788b40a4042aac790e946f
SHAAlg = SHA1
e = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011
Msg = fe196459e8232a9f94e8c93d88ddae7d38be7a10e2dd03ffb7bb9be43bd59659eec006b12c78c9e652f6c6d3220073a369459b8adea95eb34ede7979f634c7d931b208275365e201d4f82582b18553b70fab605721e1b6ae4d097cdb8a49183b0d16f22524917bd862176fb1fc1357a2731751df732c13dafc662e72bfbb9067
S = 94af4ce7f323d1bf4904a673d2884e30a55a108c44e4eb2bfc0a0f061f46fefe23fe74c760e947bd1fbf1bb1a30d66b3d7dccfb425d3a6551dd444f90d4f06c3e0da6dc3c7a45e062b08d24553f0091acae47ffaae495e13da66308dd9e0be87e2b960ab823f83e55815b66a1076ab238cc3883cbf7312521d0988214bb07d96
I have updated my answer. I was not correct when I said your sample will not work because the signature is as long as the message. In fact, since RSA encryption happens after SHA-1 computation, the final signature can be up-to n bytes long where n is the size of the modulus N.
So, from what I can tell, you may not have a problem at all. Are you sure the sample you posted is a positive case? If so, are you sure it was created using SHA1withRSA algorithm?
Below I have included sample, Groovy, code based on your code. The code basically creates a signature and verifies it. I got the public and private key pairs from this site but any key pair will work. The code is self explanitory so I'll just summarize how the signature is supposed to work incase you'd like to test and verify using random key pairs and messages.
Compute hashcode h on message m. In this case is h = SHA-1(m).
Compute signature s on hashcode h by signing/encrypting it with private key d.
Validate signature s by verifying/decrypting it with public key e and checking the value equals the hashcode of m.
As you can see, the steps above can be performed with simple RSA computation tools. I have checked the example provided in my code to be correct. On thing I found interesting is that Java throws a javax.crypto.BadPaddingException: Message is larger than modulus exception if the modulus is negative (1st bit is set in 2's complement) so I had to append 1 byte of zeros to the left.
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
byte[] modulus = Utils.getBytes("00d94d889e88853dd89769a18015a0a2e6bf82bf356fe14f251fb4f5e2df0d9f9a94a68a30c428b39e3362fb3779a497eceaea37100f264d7fb9fb1a97fbf621133de55fdcb9b1ad0d7a31b379216d79252f5c527b9bc63d83d4ecf4d1d45cbf843e8474babc655e9bb6799cba77a47eafa838296474afc24beb9c825b73ebf549");
byte[] publicExp = Utils.getBytes("010001");
byte[] privateExp = Utils.getBytes("047b9cfde843176b88741d68cf096952e950813151058ce46f2b048791a26e507a1095793c12bae1e09d82213ad9326928cf7c2350acb19c98f19d32d577d666cd7bb8b2b5ba629d25ccf72a5ceb8a8da038906c84dcdb1fe677dffb2c029fd8926318eede1b58272af22bda5c5232be066839398e42f5352df58848adad11a1");
byte[] message = Utils.getBytes("00");
byte[] signature;
signature = createSignature("SHA1withRSA", System.out, message, modulus, privateExp, signature);
println ((new BigInteger(signature)).toString(16));
verifySignature("SHA1withRSA", System.out, message, modulus, publicExp, signature);
public static boolean verifySignature(String algo, PrintStream oPS, byte[] message, byte[] modulus, byte[] exponent, byte[] sigBytes) throws Exception
{
Signature sig = Signature.getInstance(algo);
KeyFactory keyMaker = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
RSAPublicKey pubKey = (RSAPublicKey)keyMaker.generatePublic(pubKeySpec);
sig.initVerify(pubKey);
sig.update(message);
return sig.verify(sigBytes);
}
public static byte[] createSignature(String algo, PrintStream oPS, byte[] message, byte[] modulus, byte[] exponent, byte[] sigBytes) throws Exception
{
Signature sig = Signature.getInstance(algo);
KeyFactory keyMaker = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(exponent));
RSAPrivateKey privKey = (RSAPrivateKey)keyMaker.generatePrivate(privKeySpec);
sig.initSign(privKey);
sig.update(message);
return sig.sign();
}
class Utils{
private static char[] lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
public static void outputValue(String s, byte[] data, int length, PrintStream out, boolean wtf){
out.print(s+" = ");
for(int i=0;i<length;i++){
out.print(lookup[(data[i] >> 4) & 0xF]);
out.print(lookup[data[i] & 0xF]);
}
out.println();
}
public static byte[] getBytes(String s){
byte[] result = new byte[s.length()>>1];
for(int i=0;i<result.length;i++){
result[i] = Integer.parseInt(s.substring(i*2, i*2+2), 16);
}
return result;
}
}
Your code says:
BigInteger Nvalue = pubKey.getPublicExponent();
N is the key modulus, not the public exponent. Your text labels and variable contents don't match up.
Your signature verification problem is probably something else. Do you get an error message, or the verify() method simply return false?
Related
So this was part of an assignment I was working (though I already had to turn it in unsolved) based on this challenge at http://www.loyalty.org/~schoen/rsa/.
In summary, the challenge provides a list of files contain cipher and an associated public key. Using the fact that RSA public keys often repeat primes, we are expected to find as many of the private keys from the 100 as possible and decrypt the associated message. Although I think forming the private key was straightforward (and believe I am doing it correctly), but I get error "javax.crypto.BadPaddingException: Message is larger than modulus". Can someone tell me what I am doing wrong? I'm including my code along with 2 of the bin and cipher files as an example.
My Code:
package decryptrsa;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import java.io.File;
import java.util.Scanner;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.KeyFactory;
import java.security.interfaces.*;
import java.math.BigInteger;
import java.util.ArrayList;
import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
*
* #author qscot
*/
public class DecryptRSA {
static ArrayList<BigInteger> publicKeys = new ArrayList<BigInteger>();
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws Exception {
// TODO code application logic here
ReadPublicKeys();
crackPrivateKeys();
}
static void ReadPublicKeys() throws Exception {
String publicKey;
for (int i = 1; i <= 100; i++) {
publicKey = "";
Scanner scanner = new Scanner(new File("C:\\Users\\qscot\\Downloads\\challenge\\" + i + ".pem"));
scanner.nextLine();
String lineString = scanner.nextLine();
do {
publicKey += lineString;
lineString = scanner.nextLine();
} while (lineString.contains("END PUBLIC KEY") == false);
scanner.close();
byte[] decoded = Base64.decode(publicKey.getBytes());
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey pk = (RSAPublicKey) kf.generatePublic(spec);
BigInteger modulus = pk.getModulus();
publicKeys.add(modulus);
}
}
static void crackPrivateKeys() throws Exception {
BigInteger gcd;
BigInteger q1;
BigInteger q2;
for (int i = 0; i < 100; i++) {
for (int z = 0; z < 100; z++) {
if (i != z) {
gcd = (gcd(publicKeys.get(i), publicKeys.get(z)));
if (!gcd.equals(BigInteger.ONE)) {
q1 = publicKeys.get(i).divide(gcd);
q2 = publicKeys.get(z).divide(gcd);
RSAPrivateKey key1 = getKey(gcd, q1);
RSAPrivateKey key2 = getKey(gcd, q2);
writeDecryptedFile(i, key1);
writeDecryptedFile(z, key2);
}
}
}
}
}
static void writeDecryptedFile(int fileNo, RSAPrivateKey privKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
File file = new File("C:\\Users\\qscot\\Downloads\\challenge\\" + fileNo + ".bin");
FileInputStream fis = new FileInputStream(file);
byte[] fbytes = new byte[(int) file.length()];
fis.read(fbytes);
fis.close();
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] oBytes = cipher.doFinal(fbytes);
FileOutputStream fos = new FileOutputStream("C:\\Users\\qscot\\Downloads\\challenge\\" + fileNo + "-OUTPUT.txt");
fos.write(oBytes);
fos.flush();
fos.close();
}
static BigInteger gcd(BigInteger num1, BigInteger num2) {
while (!num1.equals(BigInteger.ONE) && !num1.equals(BigInteger.ZERO) && !num2.equals(BigInteger.ONE) && !num2.equals(BigInteger.ZERO)) {
if (num1.compareTo(num2) == 1) {
num1 = num1.mod(num2);
} else {
num2 = num2.mod(num1);
}
}
if (num1.equals(BigInteger.ONE) || num2.equals(BigInteger.ONE)) {
return BigInteger.valueOf(1);
} else {
if (num1.equals(BigInteger.ZERO)) {
return num2;
} else {
return num1;
}
}
}
static RSAPrivateKey getKey(BigInteger p, BigInteger q) throws Exception {
KeyFactory kf = KeyFactory.getInstance("RSA");
BigInteger t, d, e;
e = BigInteger.valueOf(65537);
t = (p.subtract(BigInteger.ONE)).multiply((q.subtract(BigInteger.ONE)));
d = e.modInverse(t);
RSAPrivateKeySpec keyspec = new RSAPrivateKeySpec(d, e);
return (RSAPrivateKey) kf.generatePrivate(keyspec);
}
}
6.pem file:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDn5BqEUDrlhvwXQ68MqZ001B74
fGD1w2Le++wILzEX7Ba8LeJVeuwpOxxdxDQ7280yc0SKPiChWpb3bE1/G/hV5e++
95qfhbe+SP7MRL39TxEotADaqyHY6SfloDk5A9NiIzgebWmtFriamBfhrxzx8G3K
6NWAAjDAIMx+xjLn6QIDAQAB
-----END PUBLIC KEY-----
6.bin File:
xƒâTD§¼çÄ ØˆßPã…Ôä3x4b2Ð#•—æ¨
U«õ`Êzÿúw"Ü°™è0ÄÕ~³•—˜§FºqŠ„hÏŒÞõ&د³Ô<*pàbGÃGìMÿö¶3Ùù¸²Z•a¯®éDNïæÝjn¢¯tå!WÐ
6-OUTPUT.txt:
8.pem File:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1fGe+mMJYZ+BDm76Ag25bXcBB
pABkGZpnQpnSTocEuCQyp5/lNNVFdF0QliNRULnxoK+pD8VEBqxR+zkYsYf9iGzU
PzOELWvbFgIJdUPixlaD3/1Y6+eSDffCOsCoJ1A/8DELMbjQdbFoxfqj9AVRU3cd
R0AauL4O9hPz0N9OVQIDAQAB
-----END PUBLIC KEY-----
8.bin File:
¤”»BÃ."
îÂT.<(bø×]¥”£Ó¯!›==±Ñ·;ª%7¿ðU#xÀÉ5ç£
‡*h\w¸¸#¦aܳj Ù~t´õêæSü®Î ŒQU¼L-â-äK}\-žù‹ý«>DÕ£ñ”Õe6Œ"
G®lI
The line RSAPrivateKeySpec keyspec = new RSAPrivateKeySpec(d, e); contains a bug. The arguments to RSAPrivateKeySpec should be, in order, the modulus and the decrypt exponent. According the documentation in the link you provided the padding used is PKCS1PADDING. Therefore you should obtain a Cipher instance using Cipher.getInstance("RSA/ECB/PKCS1PADDING");
There is also an off-by-1 bug in your writeDecryptedFile method. The files are named 1.bin ... 100.bin, but your keys are indexed from 0 through 99. Thus you need to be accessing (fileNo + 1) + ".bin" rather than fileNo + ".bin"
There is no reason to assume the encrypt exponent is always 65537 since this information is available in the RSAPublicKey. Also, do not use the Base64 codec you are using, instead use the one from java.util.Base64.
Your method of reading in the bytes from the cipher *.bin files is unreliable because it assumes a read always returns the number of bytes requested. It will probably work out that way with current implementations but that isn't guaranteed by the javadocs. You should investigate the very simple Files.readAllBytes() method and related methods for reading in small files. They greatly simplify your code.
I am trying to learn some Go and blockchains.. Starting with ECDSA signatures. Trying to figure out how to test if I had a correctly working Go implementation of ECDSA signatures, I figured I would try to create a similar version in Java and compare the results to see if I can get them to match.
So Java attempt:
public static void main(String[] args) throws Exception {
//the keys below are previously generated with "generateKey();" and base64 encoded
generateKey();
String privStr = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAQ7bMVIcWr9NpSD3hPkns5C0qET87UvyY5WI6UML2p0Q==";
String pubStr = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAES8VdACZT/9u1NmaiQk0KIjEXxiaxms74nu/ps6bP0OvYMIlTdIWWU2s35LEKsNJH9u5QM2ocX53BPjwbsENXJw==";
PrivateKey privateKey = base64ToPrivateKey(privStr);
PublicKey publicKey = base64ToPublicKey(pubStr);
String str = "This is string to sign";
byte[] signature = signMsg(str, privateKey);
boolean ok = verifySignature(publicKey, str, signature);
System.out.println("signature ok:" + ok);
String privHex = getPrivateKeyAsHex(privateKey);
}
public static byte[] signMsg(String msg, PrivateKey priv) throws Exception {
Signature ecdsa = Signature.getInstance("SHA1withECDSA");
ecdsa.initSign(priv);
byte[] strByte = msg.getBytes("UTF-8");
ecdsa.update(strByte);
byte[] realSig = ecdsa.sign();
//the printed signature from here is what is used in the Go version (hex string)
System.out.println("Signature: " + new BigInteger(1, realSig).toString(16));
return realSig;
}
//https://stackoverflow.com/questions/30175149/error-when-verifying-ecdsa-signature-in-java-with-bouncycastle
private static boolean verifySignature(PublicKey pubKey, String msg, byte[] signature) throws Exception {
byte[] message = msg.getBytes("UTF-8");
Signature ecdsa = Signature.getInstance("SHA1withECDSA");
ecdsa.initVerify(pubKey);
ecdsa.update(message);
return ecdsa.verify(signature);
}
public static String generateKey() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(256, random); //256 bit key size
KeyPair pair = keyGen.generateKeyPair();
PrivateKey priv = pair.getPrivate();
ECPrivateKey ePriv = (ECPrivateKey) priv;
PublicKey pub = pair.getPublic();
//https://stackoverflow.com/questions/5355466/converting-secret-key-into-a-string-and-vice-versa
String encodedPrivateKey = Base64.getEncoder().encodeToString(priv.getEncoded());
byte[] pubEncoded = pub.getEncoded();
String encodedPublicKey = Base64.getEncoder().encodeToString(pubEncoded);
System.out.println(encodedPrivateKey);
System.out.println(encodedPublicKey);
return encodedPrivateKey;
}
public static PrivateKey base64ToPrivateKey(String encodedKey) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
return bytesToPrivateKey(decodedKey);
}
public static PrivateKey bytesToPrivateKey(byte[] pkcs8key) throws GeneralSecurityException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
KeyFactory factory = KeyFactory.getInstance("EC");
PrivateKey privateKey = factory.generatePrivate(spec);
return privateKey;
}
public static PublicKey base64ToPublicKey(String encodedKey) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
return bytesToPublicKey(decodedKey);
}
public static PublicKey bytesToPublicKey(byte[] x509key) throws GeneralSecurityException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(x509key);
KeyFactory factory = KeyFactory.getInstance("EC");
PublicKey publicKey = factory.generatePublic(spec);
return publicKey;
}
//https://stackoverflow.com/questions/40552688/generating-a-ecdsa-private-key-in-bouncy-castle-returns-a-public-key
private static String getPrivateKeyAsHex(PrivateKey privateKey) {
ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey;
byte[] privateKeyBytes = ecPrivateKey.getS().toByteArray();
System.out.println("S:"+ecPrivateKey.getS());
String hex = bytesToHex(privateKeyBytes);
System.out.println("Private key bytes: " + Arrays.toString(privateKeyBytes));
System.out.println("Private key hex: " + hex);
return hex;
}
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0 ; j < bytes.length ; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Doing the signing and verification in Java works just fine. Easy to configure of course, since they are all the same libs, parameters, and all.
To verify the same signature in Go, I tried:
func TestSigning(t *testing.T) {
privKey := hexToPrivateKey("10EDB31521C5ABF4DA520F784F927B390B4A844FCED4BF2639588E9430BDA9D1")
pubKey := privKey.Public()
sig := "3045022071f06054f450f808aa53294d34f76afd288a23749628cc58add828e8b8f2b742022100f82dcb51cc63b29f4f8b0b838c6546be228ba11a7c23dc102c6d9dcba11a8ff2"
sigHex, _ := hex.DecodeString(sig)
ePubKey := pubKey.(*ecdsa.PublicKey)
ok := verifyMySig(ePubKey, "This is string to sign", sigHex)
println(ok)
}
func verifyMySig(pub *ecdsa.PublicKey, msg string, sig []byte) bool {
r := new(big.Int).SetBytes(sig[:len(sig)/2])
s := new(big.Int).SetBytes(sig[len(sig)/2:])
return ecdsa.Verify(pub, []byte(msg), r, s)
}
func hexToPrivateKey(hexStr string) *ecdsa.PrivateKey {
bytes, _ := hex.DecodeString(hexStr)
k := new(big.Int)
k.SetBytes(bytes)
println("k:")
fmt.Println(k.String())
priv := new(ecdsa.PrivateKey)
curve := elliptic.P256()
priv.PublicKey.Curve = curve
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(k.Bytes())
return priv
}
Initially, I tried to just export the Private key in Java as a base64 encoded string, and import that into Go. But I could not figure out how to get Go to load the key in the format Java stores if (X509EncodedKeySpec). So instead, I tried this way to copy the big integer of the private key only, and generate the public key from that. If I get that to work, then try to copy just the public key..
Anyway, the Go code fails to verify the signature. It is always false. Also, I cannot figure out where to put the SHA function in Go from "SHA1withECDSA" part.
I am sure I am missing some basic concepts here. How to do this properly?
Managed to get this to work. So just to document it for myself and anyone interested..
As pointed by in comments, the signature from Java is in ASN1 format. Found a nice description of the format here: https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1.
I also found some good examples on how to do SHAxx with ECDSA in Go at https://github.com/gtank/cryptopasta (sign.go and sign_test.go). Just need to run the relevant SHA function before the ECDSA code.
Found example code for building the public keys from parameters in Go at http://codrspace.com/supcik/golang-jwt-ecdsa/.
I paste the relevant code below, if someone finds an issue, please let me know..
Relevant Java code:
public static PublicKey bytesToPublicKey(byte[] x509key) throws GeneralSecurityException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(x509key);
KeyFactory factory = KeyFactory.getInstance("EC");
ECPublicKey publicKey = (ECPublicKey) factory.generatePublic(spec);
//We should be able to use these X and Y in Go to build the public key
BigInteger x = publicKey.getW().getAffineX();
BigInteger y = publicKey.getW().getAffineY();
System.out.println(publicKey.toString());
return publicKey;
}
//we can either use the Java standard signature ANS1 format output, or just take the R and S parameters from it, and pass those to Go
//https://stackoverflow.com/questions/48783809/ecdsa-sign-with-bouncycastle-and-verify-with-crypto
public static BigInteger extractR(byte[] signature) throws Exception {
int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
int lengthR = signature[startR + 1];
return new BigInteger(Arrays.copyOfRange(signature, startR + 2, startR + 2 + lengthR));
}
public static BigInteger extractS(byte[] signature) throws Exception {
int startR = (signature[1] & 0x80) != 0 ? 3 : 2;
int lengthR = signature[startR + 1];
int startS = startR + 2 + lengthR;
int lengthS = signature[startS + 1];
return new BigInteger(Arrays.copyOfRange(signature, startS + 2, startS + 2 + lengthS));
}
public static byte[] signMsg(String msg, PrivateKey priv) throws Exception {
Signature ecdsa = Signature.getInstance("SHA1withECDSA");
ecdsa.initSign(priv);
byte[] strByte = msg.getBytes("UTF-8");
ecdsa.update(strByte);
byte[] realSig = ecdsa.sign();
//this is the R and S we could also pass as the signature
System.out.println("R: "+extractR(realSig));
System.out.println("S: "+extractS(realSig));
return realSig;
}
Relevant Go code:
func verifyMySig(pub *ecdsa.PublicKey, msg string, sig []byte) bool {
//https://github.com/gtank/cryptopasta
digest := sha1.Sum([]byte(msg))
var esig ecdsaSignature
asn1.Unmarshal(sig, &esig)
//above is ASN1 decoding from the Java format. Alternatively, we can just transfer R and S parameters and set those
// esig.R.SetString("89498588918986623250776516710529930937349633484023489594523498325650057801271", 0)
// esig.S.SetString("67852785826834317523806560409094108489491289922250506276160316152060290646810", 0)
fmt.Printf("R: %d , S: %d", esig.R, esig.S)
println()
return ecdsa.Verify(pub, digest[:], esig.R, esig.S)
}
func hexToPrivateKey(hexStr string) *ecdsa.PrivateKey {
bytes, err := hex.DecodeString(hexStr)
print(err)
k := new(big.Int)
k.SetBytes(bytes)
println("k:")
fmt.Println(k.String())
priv := new(ecdsa.PrivateKey)
curve := elliptic.P256()
priv.PublicKey.Curve = curve
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(k.Bytes())
//we can check these against the Java implementation to see if it matches to know key was transferred OK
fmt.Printf("X: %d, Y: %d", priv.PublicKey.X, priv.PublicKey.Y)
println()
return priv
}
I have an API documentation that requires encrypting a key to authenticate,
I managed to build and compile their sample code, but the results on windows are different than linux.
When I run and test from Windows, all seems to be correct and works with the API.
That same test on Linux outputs a different result. I need it working on Linux since that's the main server.
I am using & running the same jar file on both environments.
This is the key I am trying to encrypt (it's a dynamic key):
2136230$486B91E1BEA5D082BA3601CD803585CE$20140409$20140409$$ABCDEFGH$Reserved$CTC
This is the correct output on Windows (it's obviously quite longer):
F7BE2B7E0CEAD9D09135FCF2A8AEB11E2937D26B33CCBC9B8132A29A3534040C9737B2A8E3F271A9DF6454696CF890F7886223AE9C86F81EF58E41AEAA3D34A80F7089154E64F4FD36E75C25A7C2DA7FF03D21F57DA950F5
This is the wrong output from Linux:
F66D4CE1238B30EE54ABC74966D7AC3064FEA3ADFB9D37548E41509CE4FED9CB1D146651B491F2433169999A85F73DAF9ACD07A090DF3D85477BE4201ADC9E1A0181EA7CB763050A
What is causing this and how to correct it ?
This is the source code of the program to use as we received from the API company:
public class DESUtil
{
private static final String Algorithm = "DESede/ECB/PKCS5Padding";// DESede/ECB/PKCS5Padding;DESede
private static final String DESede = "DESede";
public static byte[] encrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.ENCRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static byte[] decrypt(byte[] keybyte, byte[] src)
throws NoSuchAlgorithmException, NoSuchPaddingException, Exception
{
SecretKey deskey = new SecretKeySpec(keybyte, DESede);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE, deskey);
return c1.doFinal(src);
}
public static String byte2hex(byte[] b)
{
StringBuffer hs = new StringBuffer();
String stmp = "";
for (int n = 0; n <b.length; n++)
{
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1)
hs.append("0").append(stmp);
else
hs.append(stmp);
}
return hs.toString().toUpperCase(Locale.getDefault());
}
public static byte[] hex2byte(String hexStr)
{
if (hexStr.length() % 2 != 0)
{
AppLogger.error("hex2bytes's hexStr length is not even.");
return null;
}
byte[] toBytes = new byte[hexStr.length() / 2];
for (int i = 0, j = 0; i <hexStr.length(); j++, i = i + 2)
{
int tmpa = Integer.decode(
"0X" + hexStr.charAt(i) + hexStr.charAt(i + 1)).intValue();
toBytes[j] = (byte) (tmpa & 0XFF);
}
return toBytes;
}
public static void main(String[] args)
{
Security.addProvider(new com.sun.crypto.provider.SunJCE());
final byte[] rawKey = "db90e7eb".getBytes();
final byte[] keyBytes = new byte[24];
for (int i = 0; i <rawKey.length; i++)
{
keyBytes[i] = rawKey[i];
}
for (int i = rawKey.length; i <keyBytes.length; i++)
{
keyBytes[i] = (byte)0;
}
String szSrc = "20926330$AD75B1697FB5EB6345B2D412124030D2$10086$10086$10.164.111$ABCDEFGH$Reserved$CTC";
System.out.println("string before encrypt:" + szSrc);
byte[] encoded = null;
try
{
encoded = encrypt(keyBytes, szSrc.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string after encrypt::" + byte2hex(encoded));
byte[] srcBytes = null;
try
{
srcBytes = decrypt(keyBytes, encoded);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("string before decode: :" + (new String(srcBytes)));
}
}
Almost certainly your use of szSrc.getBytes() which uses the platform's default character encoding.
Try szSrc.getBytes("ISO-8859-1") as a starter if it's working on Windows, but if this string comes from an external service you should determine the encoding scheme dynamically (eg. if it comes through a Servlet use httpRequest.getCharacterEncoding()).
I've been developing an Android App and in certain part of the app I need to calculate the MD5 of a certain string. I've been using the following code, but every now and then the output string if the byte it has to convert to String is lower than 10, then it will miss a 0 in the two byte representation:
MessageDigest di = java.security.MessageDigest.getInstance("MD5");
di.update(cadena.getBytes());
byte mdi[] = di.digest();
StringBuffer md5= new StringBuffer();
for (byte b : mdi) {
md5.append(Integer.toHexString(0xFF & b));
}
For example, if I pass the string 109370 the MD5 it will have to return is 932ff0696b0434d7a83e1ff84fe298c5 but instead it calculates the 932ff0696b434d7a83e1ff84fe298c5.
That's because the byte array has a 4 and Integer.toHexString() is returning only 1 char array instead of two.
Any thought about how can I handle this?
Thanks!
below is the code that i am using:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Encode {
private static String convertedToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfOfByte = (data[i] >>> 4) & 0x0F;
int twoHalfBytes = 0;
do {
if ((0 <= halfOfByte) && (halfOfByte <= 9)) {
buf.append((char) ('0' + halfOfByte));
} else {
buf.append((char) ('a' + (halfOfByte - 10)));
}
halfOfByte = data[i] & 0x0F;
} while (twoHalfBytes++ < 1);
}
return buf.toString();
}
public static String MD5(String text) throws NoSuchAlgorithmException,
UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("MD5");
byte[] md5 = new byte[64];
md.update(text.getBytes("iso-8859-1"), 0, text.length());
md5 = md.digest();
return convertedToHex(md5);
}
}
and use it by this way:
MD5Encode.MD5("your string here")
hope this will help you :)
You can use a java.util.Formatter:
Formatter fmt = new Formatter(md5);
for (byte b : mdi) {
fmt.format("%02x", b&0xff);
}
fmt.close();
Use this:
public String calculateMD5(String string) {
StringBuilder result = new StringBuilder();
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(string.getBytes("UTF8"));
byte s[] = m.digest();
for (int i = 0; i < s.length; i++) {
result.append(Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6));
}
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Password hash is unsupported by device android implementation.", e);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Password hash is unsupported by device android implementation.", e);
}
return result.toString();
}
I'm trying to encrypt data between my android application and a PHP webservice.
I found the next piece of code in this website: http://schneimi.wordpress.com/2008/11/25/aes-128bit-encryption-between-java-and-php/
But when I try to decrypt I get the Exception of the title "data not block size aligned"
This are the method in my MCrypt class
public String encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
Cipher cipher;
byte[] encrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return new String( encrypted );
}
public String decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
Cipher cipher;
byte[] decrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return new String( decrypted );
}
private static byte[] hexToBytes(String hex) {
String HEXINDEX = "0123456789abcdef";
int l = hex.length() / 2;
byte data[] = new byte[l];
int j = 0;
for (int i = 0; i < l; i++) {
char c = hex.charAt(j++);
int n, b;
n = HEXINDEX.indexOf(c);
b = (n & 0xf) << 4;
c = hex.charAt(j++);
n = HEXINDEX.indexOf(c);
b += (n & 0xf);
data[i] = (byte) b;
}
return data;
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
And this is how I'm using it in my activity to test:
String encrypted = mcrypt.encrypt(jsonUser.toString());
String decrypted = mcrypt.decrypt(encrypted);
the encrypt method works fine, but the second throws an exception.
At last! I made it work! Thanks for all your suggestion. I would like to share the code just in case somebody get stuck like me:
JAVA
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MCrypt {
private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public MCrypt()
{
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data)
{
if (data==null)
{
return null;
}
int len = data.length;
String str = "";
for (int i=0; i<len; i++) {
if ((data[i]&0xFF)<16)
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
else
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
HOW TO USE IT (JAVA)
mcrypt = new MCrypt();
/* Encrypt */
String encrypted = MCrypt.bytesToHex( mcrypt.encrypt("Text to Encrypt") );
/* Decrypt */
String decrypted = new String( mcrypt.decrypt( encrypted ) );
====================================================
PHP
<?php
class MCrypt
{
private $iv = 'fedcba9876543210'; #Same as in JAVA
private $key = '0123456789abcdef'; #Same as in JAVA
function __construct()
{
}
function encrypt($str) {
//$key = $this->hex2bin($key);
$iv = $this->iv;
$td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
mcrypt_generic_init($td, $this->key, $iv);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex($encrypted);
}
function decrypt($code) {
//$key = $this->hex2bin($key);
$code = $this->hex2bin($code);
$iv = $this->iv;
$td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
mcrypt_generic_init($td, $this->key, $iv);
$decrypted = mdecrypt_generic($td, $code);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return utf8_encode(trim($decrypted));
}
protected function hex2bin($hexdata) {
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
HOW TO USE IT (PHP)
<?php
$mcrypt = new MCrypt();
#Encrypt
$encrypted = $mcrypt->encrypt("Text to encrypt");
#Decrypt
$decrypted = $mcrypt->decrypt($encrypted);
I'm guessing your keyspec and ivspec are not valid for decryption. I've typically transformed them into PublicKey and PrivateKey instances and then use the private key to decrypt.
I looked at the comments in the other answer. I ran into a similar problem trying to encrypt a large block of text using open SSL in php (on both sides). I imagine the same issue would come up in Java.
If you have a 1024 bit RSA key, you must split the incoming text into 117 byte chunks (a char is a byte) and encrypt each (you can concatenate them together). On the other end, you must split the encrypted data into 128 byte chunks and decrypt each. This should give you your original message.
Also note that http may not play friendly with the non-ASCII encrypted data. I base64 encode/decode it before and after transmission (plus you have to worry about additional urlencoding for the base64 change, but it is easy).
I'm not sure of your AES key length, but if it's 1024 bits the chunk length is probably the same. If it's not, you will have to divide the bits by 8 to find the byte chunk length coming out. I'm actually not sure how to get it coming in, unfortunately (maybe multiply by 117/128 ?)
Here's some php code:
class Crypto {
public function encrypt($key, $data) {
$crypto = '';
foreach (str_split($data, 117) as $chunk) {
openssl_public_encrypt($chunk, $encrypted, $key);
$crypto .= $encrypted;
}
return $crypto;
}
//Decrypt omitted. Basically the same, change 117 to 128.
/**##+
* Update data for HTTP transmission and retrieval
* Must be used on encrypted data, but also useful for any binary data
* (e.g. zip files).
*/
public function base64_encode($value) {
return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
}
//String length must be padded for decoding for some reason
public function base64_decode($value) {
return base64_decode(str_pad(strtr($value, '-_', '+/')
, strlen($value) % 4, '=', STR_PAD_RIGHT));
}
/**##-*/
}