I have tested a solution to verify an ECDSA signature (How can I get a PublicKey object from EC public key bytes?) that works perfect with the given data.
This is the data:
byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e3789c36a7a");
byte[] message = DatatypeConverter.parseHexBinary("54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29");
byte[] signature = DatatypeConverter.parseHexBinary("304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654");
And this is the code (which prints true):
private static boolean isValidSignature(byte[] pubKey, byte[] message,byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException {
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
ecdsaVerify.initVerify(getPublicKeyFromBytes(pubKey));
ecdsaVerify.update(message);
return ecdsaVerify.verify(signature);
}
private static PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), pubKey);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
return pk;
}
public static void main (String[] args) {
System.out.println(isValidSignature(pubKey, message, signature));
}
My problem comes when I change the signature and data to an example input from an already implemented system:
final static byte[] pubKey = DatatypeConverter.parseHexBinary("0447303876C6FED5550DF3EE1136989FCD87293D54A5D8E2F2F6D7FBE9A81089B889A5917443AF33E696178CEF4C9D6A4288B2745B29AF6C8BCAD1348F78EB9F9B");
final static byte[] message = DatatypeConverter.parseHexBinary("02158001f53611a06e2d1a270000013ed9305dc2780524015110500000002d0100140092569202017aa00c5dd30000000000000000000000000000000007d1000001020001b20788b80059f48d95cdefc8c6000200200030d41e0000012016840310a50733a9870fffd0430100");
final static byte[] signature = DatatypeConverter.parseHexBinary("531F8918FF250132959B01F7F56FDFD9E6CA3EC2144E12A6DA37C281489A3D96");
New data outputs this error:
java.security.SignatureException: error decoding signature bytes.
at org.bouncycastle.jcajce.provider.asymmetric.util.DSABase.engineVerify(Unknown Source)
at java.security.Signature$Delegate.engineVerify(Signature.java:1178)
at java.security.Signature.verify(Signature.java:612)
at its.sec.exec.TestProgram.isValidSignature(TestProgram.java:168)
at its.sec.exec.TestProgram.execution(TestProgram.java:101)
at its.sec.exec.TestProgram.main(TestProgram.java:55)
I assume the problem is about the signature that comes with the secured message because:
The key pair is the same length and format that the example. And are correct since it comes from the certificate that signs the message.
The message itself (payload) shouldn't affect the security process.
Last thing worth mention is that my documentation says that the signature must be preceded by a field called "R" which "contains the x coordinate of the elliptic curve point resulting from multiplying the generator element by the ephemeral private key" and its length must be the same as the signature (32 byte).
Can someone point me out what I'm missing here?
EDIT: Solution
As Peter Dettman pointed in his answer, the signature was not correctly formatted (also content was incorrect too) in order to be computed by the verify() method. Here is a good explanation that mainly says that:
When encoded in DER, this (signature) becomes the following sequence of bytes:
0x30 b1 0x02 b2 (vr) 0x02 b3 (vs)
where:
b1 is a single byte value, equal to the length, in bytes, of the remaining list of bytes (from the first 0x02 to the end of the encoding);
b2 is a single byte value, equal to the length, in bytes, of (vr);
b3 is a single byte value, equal to the length, in bytes, of (vs);
(vr) is the signed big-endian encoding of the value "r", of minimal length;
(vs) is the signed big-endian encoding of the value "s", of minimal length.
Applying that change, signature grows to 70 bytes and the execution outputs no error.
The expected ECDSA signature format that the BC (and other provider) implementations work with is a DER-encoded ASN.1 sequence containing two integer values r and s. This signature format has been specified in ANSI X9.62. This is the format in the first set of data you give (note that signature is a total of at least 70 bytes).
In the second set of data, signature is only 32 bytes, and is not an ASN.1 sequence at all. I would guess that this value is only the s value, and it is missing the r value and the ASN.1 INTEGER encoding for them both, instead encoding the values as a unsigned big integer value with the same size as the key.
this is a sample code to write r and s in ASN1 DER encoded format
// construct the ASN1Sequence with r and s
ByteArrayOutputStream outs = new ByteArrayOutputStream();
byte radd = (byte)(((signed[0] & 0x80) > 0) ? 1 : 0);
byte sadd = (byte)(((signed[32] & 0x80) > 0) ? 1 : 0);
byte length = (byte)(0x44 + radd + sadd);
outs.write(0x30);
outs.write(length); // length 68 bytes +
outs.write(0x02); // ASN1Integer
outs.write(0x20 + radd); // length 32 bytes
if(radd > 0)
outs.write(0x00); // positive val
outs.write(signed, 0, 32);
outs.write(0x02); // ASN1Integer
outs.write(0x20 + sadd); // length 32 bytes
if(sadd > 0)
outs.write(0x00); // positive val
outs.write(signed, 32, 32);
signed = outs.toByteArray();
Related
I have generated a key pair on iOS and created a data representation using the following code:
var publicKey, privateKey: SecKey?
let keyattribute = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String : 256
] as CFDictionary
SecKeyGeneratePair(keyattribute, &publicKey, &privateKey)
var error: Unmanaged<CFError>?
let pubkeyRep = SecKeyCopyExternalRepresentation(publicKey!, &error) as Data?
let prikeyRep = SecKeyCopyExternalRepresentation(privateKey!, &error) as Data?
According to the documentation from Apple, the SecKeyCopyExternalRepresentation function encodes these keys using uncompressed ANSI X9.63 format
I want to transform these byte arrays into PublicKey and PrivateKey objects in Java.
A few examples that I've found here (using SunJCE) and here (using BouncyCastle) work for the public key, but they don't describe a way to import the private key.
Notice in the Apple documentation how the first 65-bytes are the uncompressed public key (04 || X || Y) concatenated with the private scalar (|| K). Take these bytes off and you can create the private key. I hope this helps somebody.
/*
* For an elliptic curve private key, the output is formatted as the public key
* concatenated with the big endian encoding of the secret scalar, or 04 || X || Y || K.
*/
private PrivateKey createECPrivateKey(byte[] rawBytes) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException {
KeyFactory kf = KeyFactory.getInstance("EC");
BigInteger s = new BigInteger(Arrays.copyOfRange(rawBytes, 65, rawBytes.length));
return kf.generatePrivate(new ECPrivateKeySpec(s, ecParameterSpecForCurve("secp256r1")));
}
When verifying a signature using Signature.verify I receive an "Invalid encoding for signature" exception.
When verifying same signature using Azure service, the signature is verified.
I have a hash-data (SHA-256), a public key, and a signature that I'm trying to verify.
The signature was received using com.microsoft.azure.keyvault.KeyVaultClient.sign method, with signing algorithm "ES256".
This works (using ES256 algorithm) :
com.microsoft.azure.keyvault.KeyVaultClient keyVaultClient;
String keyPairIdentifier;
boolean verify(byte[] hashData, byte[] signature, JsonWebKeySignatureAlgorithm signingAlgorithm) {
com.microsoft.azure.keyvault.models.KeyVerifyResult result = keyVaultClient.verify(keyPairIdentifier, signingAlgorithm, hashData, signature);
return result.value().booleanValue();
}
This fails (certificate holds same public key that is stored in Azure keyvault):
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initVerify(certificate.getPublicKey());
ecdsaSign.update(hashData);
ecdsaSign.verify(signature)
Expected result - true (signature is verified)
Actual result:
java.security.SignatureException: Could not verify signature
at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:325)
at java.security.Signature$Delegate.engineVerify(Signature.java:1222)
at java.security.Signature.verify(Signature.java:655)
at TestKV.KeyVault.VerifyDPSignature.verifySignatureUsingCertificate(VerifyDPSignature.java:143)
at TestKV.KeyVault.VerifyDPSignature.main(VerifyDPSignature.java:104)
Caused by: java.security.SignatureException: Invalid encoding for signature
at sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:400)
at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:322)
... 4 more
Caused by: java.io.IOException: Sequence tag error
at sun.security.util.DerInputStream.getSequence(DerInputStream.java:330)
at sun.security.ec.ECDSASignature.decodeSignature (ECDSASignature.java:376)
dave_thompson_085 - thanks!
There were a few mistakes in the code you attached, the tags of the signature parts should be 0x02, not 0x30, and you didn't increase o after copying the first part.
This is the code after the changes:
byte[] r = new BigInteger(1,Arrays.copyOfRange(signature,0,32)).toByteArray();
byte[] s = new BigInteger(1,Arrays.copyOfRange(signature,32,64)).toByteArray();
byte[] der = new byte[6+r.length+s.length];
der[0] = 0x30; // Tag of signature object
der[1] = (byte)(der.length-2); // Length of signature object
int o = 2;
der[o++] = 0x02; // Tag of ASN1 Integer
der[o++] = (byte)r.length; // Length of first signature part
System.arraycopy (r,0, der,o, r.length);
o += r.length;
der[o++] = 0x02; // Tag of ASN1 Integer
der[o++] = (byte)s.length; // Length of second signature part
System.arraycopy (s,0, der,o, s.length);
After the format change I didn't get the "Sequence tag error" exception. But the verification still failed.
Thanks!
Azure does JWS which simply concatenates fixed-size I2OSP of r and s but Java JCE, like most but not all standards, uses ASN.1 DER encoding e.g. rfc3279 (caveat: there are now OIDs for ECDSA with other hashes).
To convert JWS/plain to DER, see my (cross) https://security.stackexchange.com/questions/174095/convert-ecdsa-signature-from-plain-to-der-format for a C approach, but Java makes it easier because BigInteger does half the work for you:
// byte[64] plain contains the JWS-style r,s (de-base64-ed if necessary)
byte[] r = new BigInteger(1,Arrays.copyOfRange(plain,0,32)).toByteArray();
byte[] s = new BigInteger(1,Arrays.copyOfRange(plain,32,64)).toByteArray();
byte[] der = new byte[6+r.length+s.length]; der[0] = 0x30; der[1] = der.length-2; int o = 2;
der[o++] = 2; der[o++] = (byte)r.length; System.arraycopy (r,0, der,o, r.length); o+=r.length;
der[o++] = 2; der[o++] = (byte)s.length; System.arraycopy (s,0, der,o, s.length); //o+=s.length;
2020-05 corrected and added: also Java 9 up handles this directly using algoirthm names like SHA256withECDSAinP1363format, and on all versions of Java if you add BouncyCastle it does so using names like SHA256withPLAIN-ECDSA or SHA256withCVC-ECDSA. See how to specify signature length for java.security.Signature sign method and Java ECDSAwithSHA256 signature with inconsistent length .
I just had to first decode the signature's raw bytes to Base64 in my case.
byte[] signatureBytes = Base64.getDecoder().decode(signature.getBytes());
byte[] r = new BigInteger(1,Arrays.copyOfRange(signatureBytes,0,32)).toByteArray();
byte[] s = new BigInteger(1,Arrays.copyOfRange(signatureBytes,32,64)).toByteArray();
I have already posted a similar question concerning how to load RSA keys in java. See the best response of this question to fully understand the first part of my code (the method getPulicKey, I mean).
private static PublicKey getPublicKey(String publicKey)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
try (PEMParser pp = new PEMParser(new StringReader(publicKey))) {
SubjectPublicKeyInfo subjPubKeyInfo = (SubjectPublicKeyInfo) pp.readObject();
RSAKeyParameters rsa = (RSAKeyParameters) PublicKeyFactory.createKey(subjPubKeyInfo);
RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey myKey = kf.generatePublic(rsaSpec);
System.out.println(myKey);
return myKey;
}
}
The method verify (below) raises the following exception
Signature length not correct: got 768 but was expecting 512, in Java verify
In the following code, I decode the signature because I suppose it is Base64, but I'm not sure, sorry. I don't know wether I could show you the signature and the object. The signature is a sequence of 1024 digits and numbers. It does not end with "=". The object I have to verify is a json object in String format.
The following is the method I have written to verify a String object, given a sign and a publicKey. It calls the above method getPublicKey(...).
public static boolean verify(String object, String sign, String publicKey) throws NoSuchAlgorithmException,
InvalidKeySpecException, IOException, InvalidKeyException, SignatureException {
//object to be verified
//sign is the signature stored in the postgres DB
//publicKey is the public key stored in the postgres DB
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(getPublicKey(publicKey));
byte[] objectBytes = Base64.getEncoder().encode(object.getBytes("utf-8"));
signature.update(objectBytes);
byte[] signBytes = Base64.getDecoder().decode(sign.getBytes("utf-8"));
System.out.println(signBytes.length); //this line prints 768, with decode. 1024, otherwhise
return signature.verify(signBytes);
}
EDIT:
My workmates are using the following two nodejs methods (verifySign and createSign).
In the following nodejs code (where there are workmates' methods), I encript with "createSign" the message "fake message". The sign is the following:
5f188225c68dee2ce8de588dfaccb667710da94abb5388deabfe3ad83f7a94a72ee4a3c8c51be26c5b58cdec8c82cf8135c478ad609b7985496e201b23de6c5d03e93dcd9df7b5e2315efbfd2ff6496b0aea3b425bb99c912a16aeb5efb6cefc1e175c32aaf16af3a2baca5b54f974af0f14c853228bc06410e7ad1b2b0ecec19f5aed151389bd9ccebd5e998159d5205d81a7c7e37b502df3eb5229a5fd3492680576ebfa1e76b7c47fb757a9bfb18aa9ea0b71512ab9e1afc8e551ebf6d74a042bd447233953efbf374a3a6a210ead2019b8cc8548bb304979b4bfdc90dce644cb109bbddb75dda9df1322fd8e08ef1144e870324f34d4c826d9a4b64be0442aedc6f3d5f571d7336af212825c4e0216aa5eabab6218d685a3e73d81693149b45af5f1857c4a0e50b396d1a2ea5a3effafcc4e124fd23d0427abfe5509357936ef5e7c7ca4476d6a5ae7a26e9563923a03d0780f0d897039d4d3aa2ce49dc84b31907a50045456acb57edd11a896632969245d0f97fd88dace7eb256099bbc4eedf52b5d53b481b2aeb829101d0089903ea9c3621bcbd763962b84ad57407623b576cc6a9c3328d85e0f7dd78565cd39a6648a68dd6f4334dd3a68e48491ae655601a5c9be7673ae0d3f955431fb21f33c0178ecb9067072a6b1e360ee77a45f8e855e6c545276aefc7ae70b5c7e0f1ec0b66460575e3386f8a4bbf7fd3704
Then, I verify it with public key pk (see below).
const PASSPHRASE_KEY = "...";
const crypto = require('crypto');
const prk = "...";
const pk = "-----BEGIN RSA PUBLIC KEY-----\r\n" +
"MIICCgKCAgEA1ht0OqZpP7d/05373OE7pB7yCVGNGzkUEuCneyfOzps6iA03NbvI\r\n" +
"1ZL0Jpp/N3AW73lGdhaoa3X3JE4GsI/bsToVLQwTKmIOC4yjTvBctmFEoyhhTfxW\r\n" +
"s1UHZKl4XZ/7THbRlKHhRaTKyfDAbikkMAxNT/qutLAPjnN1qOwjb1oRq52NP6FJ\r\n" +
"KWTTikz4UeOHroX+Xthn2fJSJDlQ4YMdBbgrZVx5JcHKNuPTKRf5gI8QQKMSA9Q9\r\n" +
"QJRE5OGp7b6dG14ZmOUnUxb00Mp20LgcaGPcuWU+oFsbQaF6W4G4bdkSZRJJXhSg\r\n" +
"d4Q7mahpar94/gnztJmth0GzqTWUYyZIWNqIFoMwuOgeaiDV43zb3uLsRVpRKYYy\r\n" +
"esmzcOy/jTScVLRCD8QRyu9B2wgCkNAVztQOXPCOOa4O1LlVQWaecIs4WPhOqDhi\r\n" +
"KTBhyVkpC1TrrBkp+QMqMqWll1OyVb6k/7uV0qE/i6rHJtjo5v9bcIgYzswyx9CD\r\n" +
"9PKl2Q0L0Jg7TMG+yLDIrLfGeuSeEc4XYJzN7bJcCeiizzu5iU9dQUkrncOrq9jn\r\n" +
"Ub2pM/+A+JqIsoPK3IY/pJKqH4JYpGKhO1iPQF6iXIZT1r3ZgJUSQtzSeyYqhkla\r\n" +
"2uR2BsbPbDqebCuXm3lAsY5w+dujijcn96PKwYha1LsK5sACHuJ79AMCAwEAAQ==\r\n" +
"-----END RSA PUBLIC KEY-----\r\n" +
"";
function createSign(pvt_key, data_unsigned) {
//Create a SHA256 sign generator
const signer = crypto.createSign('SHA256');
//Update context with data to sign
signer.update(data_unsigned);
//Sign the document based to user's private key
return signer.sign({
key: pvt_key,
passphrase: PASSPHRASE_KEY
},
'hex'
);
}
function verifySign(pub_key, signed_data, signature) {
const verifier = crypto.createVerify('sha256');
//Update context with data to verify
verifier.update(signed_data);
//Verify sign with user's public key
const verified = verifier.verify(
pub_key,
signature,
'hex'
);
//Send result
return verified;
}
const phrase = "fake message";
var signMade = createSign(prk, phrase);
console.log("my signature: " + signMade);
//The signature is 5f188225c68dee2ce8de588dfaccb667710da94abb5388deabfe3ad83f7a94a72ee4a3c8c51be26c5b58cdec8c82cf8135c478ad609b7985496e201b23de6c5d03e93dcd9df7b5e2315efbfd2ff6496b0aea3b425bb99c912a16aeb5efb6cefc1e175c32aaf16af3a2baca5b54f974af0f14c853228bc06410e7ad1b2b0ecec19f5aed151389bd9ccebd5e998159d5205d81a7c7e37b502df3eb5229a5fd3492680576ebfa1e76b7c47fb757a9bfb18aa9ea0b71512ab9e1afc8e551ebf6d74a042bd447233953efbf374a3a6a210ead2019b8cc8548bb304979b4bfdc90dce644cb109bbddb75dda9df1322fd8e08ef1144e870324f34d4c826d9a4b64be0442aedc6f3d5f571d7336af212825c4e0216aa5eabab6218d685a3e73d81693149b45af5f1857c4a0e50b396d1a2ea5a3effafcc4e124fd23d0427abfe5509357936ef5e7c7ca4476d6a5ae7a26e9563923a03d0780f0d897039d4d3aa2ce49dc84b31907a50045456acb57edd11a896632969245d0f97fd88dace7eb256099bbc4eedf52b5d53b481b2aeb829101d0089903ea9c3621bcbd763962b84ad57407623b576cc6a9c3328d85e0f7dd78565cd39a6648a68dd6f4334dd3a68e48491ae655601a5c9be7673ae0d3f955431fb21f33c0178ecb9067072a6b1e360ee77a45f8e855e6c545276aefc7ae70b5c7e0f1ec0b66460575e3386f8a4bbf7fd3704
console.log("was it me to sign that?");
var res = verifySign(pk, phrase, signMade);
console.log(res);
It returns true. However, If I pass to the java method verify the following (same) parameters:
object = "fake message"
sign = 5f188225c68dee2ce8de588dfaccb667710da94abb5388deabfe3ad83f7a94a72ee4a3c8c51be26c5b58cdec8c82cf8135c478ad609b7985496e201b23de6c5d03e93dcd9df7b5e2315efbfd2ff6496b0aea3b425bb99c912a16aeb5efb6cefc1e175c32aaf16af3a2baca5b54f974af0f14c853228bc06410e7ad1b2b0ecec19f5aed151389bd9ccebd5e998159d5205d81a7c7e37b502df3eb5229a5fd3492680576ebfa1e76b7c47fb757a9bfb18aa9ea0b71512ab9e1afc8e551ebf6d74a042bd447233953efbf374a3a6a210ead2019b8cc8548bb304979b4bfdc90dce644cb109bbddb75dda9df1322fd8e08ef1144e870324f34d4c826d9a4b64be0442aedc6f3d5f571d7336af212825c4e0216aa5eabab6218d685a3e73d81693149b45af5f1857c4a0e50b396d1a2ea5a3effafcc4e124fd23d0427abfe5509357936ef5e7c7ca4476d6a5ae7a26e9563923a03d0780f0d897039d4d3aa2ce49dc84b31907a50045456acb57edd11a896632969245d0f97fd88dace7eb256099bbc4eedf52b5d53b481b2aeb829101d0089903ea9c3621bcbd763962b84ad57407623b576cc6a9c3328d85e0f7dd78565cd39a6648a68dd6f4334dd3a68e48491ae655601a5c9be7673ae0d3f955431fb21f33c0178ecb9067072a6b1e360ee77a45f8e855e6c545276aefc7ae70b5c7e0f1ec0b66460575e3386f8a4bbf7fd3704
publicKey copied from pk (see node code above)
java raises the exception:
Signature length not correct: got 768 but was expecting 512
Signature lenght not correct.
You're treating the signature as if it's base64-encoded, but it's not - it's just hex.
1024 characters represents 768 base64-encoded bytes, or 512 hex-encoded bytes.
Just decode using hex instead of base64 and it should be fine.
Hint that this is along the right lines, as well as observing that every character of your signature is a valid hex digit (which would be extremely unlikely if it's actually base64):
return signer.sign({
key: pvt_key,
passphrase: PASSPHRASE_KEY
},
'hex' // Note this use of 'hex'...
);
I need to verify a signature in java. I get an url with multiple params, one of them is the signature (hexadecimal format). The message signed is the SHA-256 hash of the concatenation of all the other params. I also have the certificate with the public key to be used for the check.
All the values I'm using for the test are given to me by an example which is supposed to be correct, I just create the concatenation string.
This is the code i run:
// signed message --> hash of concat
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update( concat.getBytes() );
byte[] message = digest.digest();
System.out.println("message length "+message.length); // --> 32
// signature belonging to the message --> checkValue
System.out.println("check value length " +checkValue.length()); // --> 512
byte[] sigBytes = checkValue.getBytes();
System.out.println("check value bytes "+sigBytes.length); // --> 512
// public certificate of the CA
File file3 = new File(certificatePath);
byte[] encCertRSA = new byte[(int) file3.length()];
FileInputStream fis3 = new FileInputStream(file3);
fis3.read(encCertRSA);
fis3.close();
InputStream is = new ByteArrayInputStream( encCertRSA );
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate certRSA = (X509Certificate)f.generateCertificate(is);
certRSA.checkValidity();
PublicKey pubKeyRSA = certRSA.getPublicKey();
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKeyRSA);
// supply the Signature object with the data for which a signature was generated --> hash of concat
sig.update(message);
boolean isValid = sig.verify( sigBytes );
System.out.println("The signature of the email verifies: " + isValid);
This is the error i get:
java.security.SignatureException: Signature length not correct: got 512 but was expecting 256
at sun.security.rsa.RSASignature.engineVerify(Unknown Source)
at java.security.Signature$Delegate.engineVerify(Unknown Source)
at java.security.Signature.verify(Unknown Source)
Am i doing anything wrong? I was expecting the signature to have a length of 256, not 512. I run a test doing a substring of the signature value to match the length of 256 and I don't get the error above, but the the sig.verify returns false.
if you look the code you wrote there is System.out.println("check value bytes "+sigBytes.length); // --> "512" and then boolean isValid = sig.verify( sigBytes ); it looks like your sigBytes variable already has 512 as length before you check
I'm having problems to encrypt some strings in Java. I need to encrypt them the same way than this VisualBasic code does:
Public Function Encrypt(ByRef EncryptionKeyPair As KeyPair, ByVal PlainText As String) As String
//Use Public Key to encrypt
m_objRSA.FromXmlString(EncryptionKeyPair.PublicKey.Key)
//Get Modulus Size and compare it to length of PlainText
// If Length of PlainText > (Modulus Size - 11), then PlainText will need to be broken into segments of size (Modulus Size - 11)
//Each of these segments will be encrypted separately
// and will return encrypted strings equal to the Modulus Size (with at least 11 bytes of padding)
//When decrypting, if the EncryptedText string > Modulus size, it will be split into segments of size equal to Modulus Size
//Each of these EncryptedText segments will be decrypted individually with the resulting PlainText segments re-assembled.
Dim intBlockSize As Integer = GetModulusSize(EncryptionKeyPair.PublicKey.Key) - 11
Dim strEncryptedText As String = ""
While Len(PlainText) > 0
If Len(PlainText) > intBlockSize Then
strEncryptedText = strEncryptedText & EncryptBlock(Left(PlainText, intBlockSize))
PlainText = Right(PlainText, Len(PlainText) - intBlockSize)
Else
strEncryptedText = strEncryptedText & EncryptBlock(PlainText)
PlainText = ""
End If
End While
Return strEncryptedText
End Function
Private Function EncryptBlock(ByRef TheRSAProvider As RSACryptoServiceProvider, ByVal strIn As String) As String
Return ByteArrayAsString(TheRSAProvider.Encrypt(StringAsByteArray(strIn), False))
End Function
Private Function GetModulusSize(ByVal intKeySize As Integer) As Integer
//KeySize is in Bits - so divide by 8 to get # of bytes
Return intKeySize / 8
End Function
I've already searched in the internet and i haven't found anything like this.
I have the public key from the modulus and exponent and i'm doing this:
byte[] expBytes = Base64.decode(exponent.trim());
byte[] modBytes = Base64.decode(modulus.trim());
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponents = new BigInteger(1, expBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponents);
PublicKey pubKey = factory.generatePublic(pubSpec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted =cipher.doFinal(field.getBytes("UTF-16LE"));
String string = new String(encrypted);
The result is not right because i'm doing nothing about the modulus size - 11. Could you please explain me how can i do that in Java?
Thank you.
The modulus size is not the problem. The problem is more likely that you are expecting the same values to be generated. They are not, not even in the VB code or Java code by itself (run the code snippets twice!). RSA PKCS#1 v1.5 padding contains random numbers, ensuring that the encryption will always result in a different value. This is the same for OAEP padding by the way.
Note that you might want to look at OAEP mode and a hybrid cryptosystem instead of what you are doing now. Then you will be safer and you will be able to handle any size of data, although the amount of ciphertext will be larger of course.