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.
Related
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 am attempting to implement this encryption scheme with AES encryption.
Basically it goes as follows:
user data is encrypted with a surrogate key (surrogateKey)
the surrogate key is XORed with a key derived from the password
(passwordKey) and stored (storedKey)
when the key is needed (to encrypt or decrypt user data) the storedKey is retrieved from the DB and XORed again with the freshly generated passwordKey to recover the surrogateKey
Except, I must be doing something wrong in implementation, because I can never seem to recover a valid surrogateKey and any attempt at decryption is giving me a BadPaddingException.
The code below demonstrates the issue. Sorry if it's a bit long, but you should be able to just copy and paste it into your IDE.`
import java.io.ByteArrayOutputStream;
import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class SurrogateTest {
private static final String alphanumeric =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "1234567890"
+ "!#$%&()+*<>?_-=^~|";
private static String plainText = "I am the very model of a modern major general";
private static String cipherText = "";
private static SecureRandom rnd = new SecureRandom();
private static byte[] salt;
public static void main(String[] args) {
// arguments are password and recovery string
if (args.length > 1) {
System.out.println("password: " + args[0] + "; recovery: " + args[1]);
}
String password = args[0];
// passwordKey
SecretKey passwordKey = getKey(password);
System.out.println("passwordKey: " + DatatypeConverter.printBase64Binary(passwordKey.getEncoded()));
// Generate surrogate encryption key from random string
String rand = randomString(24);
SecretKey surrogateKey = getKey(rand);
byte[] surrogateByteArray = surrogateKey.getEncoded();
System.out.println("surrogate: " + DatatypeConverter.printBase64Binary(surrogateByteArray));
// encrypt plainText
System.out.println("text to encrypt: " + plainText);
cipherText = encryptWithKey(plainText, surrogateKey);
// XOR surrogateKey with passwordKey to get storedKey
SecretKey storedKey = xorWithKey(surrogateKey, passwordKey);
String storedKeyString = DatatypeConverter.printBase64Binary(storedKey.getEncoded());
System.out.println("storedKey: " + storedKeyString);
byte[] storedKey2Array = DatatypeConverter.parseBase64Binary(storedKeyString);
SecretKey storedKey2 = new SecretKeySpec(storedKey2Array, 0, storedKey2Array.length, "AES");
String storedKey2String = DatatypeConverter.printBase64Binary(storedKey2.getEncoded());
System.out.println("storedKey->String->key->string: " + storedKey2String);
// recover surrogateKey from storedKey2
SecretKey password2Key = getKey(password);
System.out.println("password2Key: " + DatatypeConverter.printBase64Binary(password2Key.getEncoded()));
SecretKey surrogate2Key = xorWithKey(storedKey2, password2Key);
System.out.println("surrogate2 (recovered): " + DatatypeConverter.printBase64Binary(surrogate2Key.getEncoded()));
// decrypt text
String decryptedText = decryptWithKey(cipherText, surrogate2Key);
System.out.println("decryptedText: " + decryptedText);
}
private static SecretKey xorWithKey(SecretKey a, SecretKey b) {
byte[] out = new byte[b.getEncoded().length];
for (int i = 0; i < b.getEncoded().length; i++) {
out[i] = (byte) (b.getEncoded()[i] ^ a.getEncoded()[i % a.getEncoded().length]);
}
SecretKey outKey = new SecretKeySpec(out, 0, out.length, "AES");
return outKey;
}
private static String randomString(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
sb.append(alphanumeric.charAt(rnd.nextInt(alphanumeric.length())));
return sb.toString();
}
// return encryption key
private static SecretKey getKey(String password) {
try {
SecureRandom random = new SecureRandom();
salt = new byte[16];
random.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
// obtain secret key
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
return secret;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String encryptWithKey(String str, SecretKey secret) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedText = cipher.doFinal(str.getBytes("UTF-8")); // encrypt the message str here
// concatenate salt + iv + ciphertext
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(salt);
outputStream.write(iv);
outputStream.write(encryptedText);
// properly encode the complete ciphertext
String encrypted = DatatypeConverter.printBase64Binary(outputStream.toByteArray());
return encrypted;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String decryptWithKey(String str, SecretKey secret) {
try {
byte[] ciphertext = DatatypeConverter.parseBase64Binary(str);
if (ciphertext.length < 48) {
return null;
}
salt = Arrays.copyOfRange(ciphertext, 0, 16);
byte[] iv = Arrays.copyOfRange(ciphertext, 16, 32);
byte[] ct = Arrays.copyOfRange(ciphertext, 32, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] plaintext = cipher.doFinal(ct);
return new String(plaintext, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Any ideas what I'm doing wrong?
Running the posted code shows that surrogate2 (recovered) is different from surrogate. It should be obvious this is badly wrong. The reason is that at encryption you derive the 'password' (wrapping) key using a random salt and write that salt at the beginning of your data blob; at decryption you derive the unwrapping key using a new salt and then read the correct salt from the blob and totally ignore it. This means your unwrapping key is wrong, so your unwrapped data key is wrong, so your decryption is wrong.
PS: the random key used to directly encrypt and decrypt the data is usually called a 'data' key (as I just did) or DEK (abbreviation for Data Encryption or Encrypting Key), or a more specific term like 'session key' or 'message key' (in this case), or 'working key' or 'transient key' to emphasize its limited scope. It is not usually called 'surrogate'. And using PBKDF2 to derive this data key from a strongly random string is a waste of time; just use SecureRandom_instance.nextBytes(byte[]) directly for the data key.
I see this "Input length must be multiple of 16 when decrypting with padded cipher" error when I run the program
RealEchoServer.java
import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class RealEchoServer {
public static void main(String[] args) {
int i = 1;
try {
ServerSocket s = new ServerSocket(9003);
for (;;) {
Socket incoming = s.accept();
System.out.println("Spawning " + i);
new RealEchoHandler(incoming, i).start();
i++;
}
} catch (Exception e) {
System.out.println(e);
}
}
}
class RealEchoHandler extends Thread {
DataInputStream in;
DataOutputStream out;
private Socket incoming;
private int counter;
public RealEchoHandler(Socket i, int c) {
incoming = i;
counter = c;
}
public void run() {
try {
String key1 = "1234567812345678";
byte[] key2 = key1.getBytes();
SecretKeySpec secret = new SecretKeySpec(key2, "AES");
String msg = "Singapore Malaysia Japan India Indonesia HongKong Taiwan China England";
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] encrypted = cipher.doFinal(msg.getBytes());
in = new DataInputStream(incoming.getInputStream());
out = new DataOutputStream(incoming.getOutputStream());
boolean done = false;
String str = "";
out.writeUTF("Connected!\n");
out.flush();
while (!done) {
out.writeUTF(">");
out.flush();
str = in.readUTF();
System.out.println(in + ":" + str);
if (str == null) {
done = true;
} else {
System.out.println("Sending Ciphertext : " + new String(encrypted));
out.writeUTF(new String(encrypted));
out.flush();
}
}
incoming.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
RealSocketTest.java
import java.io.*;
import java.net.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.*;
class RealSocketTest {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String str = "";
String str2 = "";
DataOutputStream out;
DataInputStream in;
try {
Socket t = new Socket("127.0.0.1", 9003);
in = new DataInputStream(t.getInputStream());
out = new DataOutputStream(t.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
boolean more = true;
System.out.println(in.readUTF());
while (more) {
str = in.readUTF();
System.out.print(str);
str2 = br.readLine();
out.writeUTF(str2);
out.flush();
str = in.readUTF();
System.out.println("Encrypted Info: " + str);
try {
String key1 = "1234567812345678";
byte[] key2 = key1.getBytes();
SecretKeySpec secret = new SecretKeySpec(key2, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] decrypted = cipher.doFinal(str.getBytes());
System.out.println("Decrypted Info: " + new String(decrypted));
} catch (BadPaddingException e) {
System.out.println("Wrong Key!");
} catch (InvalidKeyException f) {
System.out.println("Invalid Key!");
}
}
} catch (IOException e) {
System.out.println("Error");
}
}
}
I've read a similar problem here Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher , but I don't understand how I could change mine, because it looks very different from mine.
So what should be added/changed to get it decrypted?
The problem is that ciphertexts may contain every possible byte value. Many byte values on the other hand are not printable and therefore not a valid UTF-8 encoding. When you make a String out of it new String(encrypted), it will silently drop some bytes and you won't be able to successfully decrypt the ciphertext.
Two possible fixes:
Encode the ciphertext as Base64 or Hex to be used in a text-based protocol.
Use the DataOutputStream::write() method to make it a binary protocol without encoding it.
Other security stuff:
Always specify the complete String for the expected Cipher instance. Different providers might have different defaults and it may happen that client and server don't use the same method. Example: AES/ECB/PKCS5Padding.
Never use ECB mode. It is not semantically secure. At least use CBC with a random IV (prepend the IV in front of the ciphertex or write it to the stream in order).
Check your ciphertext for manipulation. This is easily done, by employing an authenticated mode like GCM (AES/GCM/NoPadding with GCMParameters). If you don't want that, then at least try to implement an encrypt-then-MAC scheme where you HMAC the ciphertext (with a different key) and check it before decryption.
I wanted to know how to encrypt a file or a photo which will eventually uploaded into the dropbox.
As i have research online and only manage to found this code (pasted at the bottom) which only encrypt the password, but I wanted to know how to encrypt a file or a photo which will eventually uploaded into the dropbox instead.
So is there any reference or help or guide on how to write a java programming ( will be using on Eclipse software ) on encrypting a file using triple DES? Thank you so much.
package com.kushal.utils;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESEncryption {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DES_ENCRYPTION_SCHEME = "DES";
private KeySpec myKeySpec;
private SecretKeyFactory mySecretKeyFactory;
private Cipher cipher;
byte[] keyAsBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public DESEncryption() throws Exception
{
myEncryptionKey = "ThisIsSecretEncryptionKey";
myEncryptionScheme = DES_ENCRYPTION_SCHEME;
keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
myKeySpec = new DESKeySpec(keyAsBytes);
mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = mySecretKeyFactory.generateSecret(myKeySpec);
}
/**
* Method To Encrypt The String
*/
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
BASE64Encoder base64encoder = new BASE64Encoder();
encryptedString = base64encoder.encode(encryptedText);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
/**
* Returns String From An Array Of Bytes
*/
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
/**
* Testing the DES Encryption And Decryption Technique
*/
public static void main(String args []) throws Exception
{
DESEncryption myEncryptor= new DESEncryption();
String stringToEncrypt="Sanjaal.com";
String encrypted=myEncryptor.encrypt(stringToEncrypt);
String decrypted=myEncryptor.decrypt(encrypted);
System.out.println("String To Encrypt: "+stringToEncrypt);
System.out.println("Encrypted Value :" + encrypted);
System.out.println("Decrypted Value :"+decrypted);
}
}
File reading is taken from here
public static byte[] encryptFile(String pFilePath, byte[] pKey) throws GeneralSecurityException, IOException
{
File file = new File(pFilePath);
long length = file.length();
InputStream is = new FileInputStream(file);
// You cannot create an array using a long type.
// It needs to be an int type.
// Before converting to an int type, check
// to ensure that file is not larger than Integer.MAX_VALUE.
if (length > Integer.MAX_VALUE) {
// File is too large
}
// Create the byte array to hold the data
byte[] bytes = new byte[(int)length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
offset += numRead;
}
// Close the input stream and return bytes
is.close();
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file "+file.getName());
}
SecretKeyFactory lDESedeKeyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey kA = lDESedeKeyFactory.generateSecret(new DESedeKeySpec(pKey));
IvParameterSpec lIVSpec = new IvParameterSpec(new byte[8]);
Cipher desedeCBCCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
desedeCBCCipher.init(Cipher.ENCRYPT_MODE, kA, lIVSpec);
byte[] encrypted = desedeCBCCipher.doFinal(bytes);
return encrypted;
}
A file can probably be read as a string of binary digits (lots of 1's and 0's). You could read the file, then encrypt the string using DES the same way you would a password, outputting the result to a new file.
Decryption would work the same way, reading the encrypted file, decrypting, and outputting to an unencrypted file.
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?