I need a fixed length 64 Byte ECDSA signature with the NIST P-256 Curve.
The implementation hast to use JCE.
The following code sample can generate a signature and verify it.
Provider provSign = new SunEC(); Provider provVerify = new SunEC(); // generate EC key KeyPairGenerator kg = KeyPairGenerator.getInstance("EC", provSign); ECGenParameterSpec ecParam = new ECGenParameterSpec("secp256r1"); kg.initialize(ecParam); KeyPair keyPair = kg.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); try { // export public key KeyFactory kf = KeyFactory.getInstance("EC", provSign); ECPublicKeySpec publicKeySpec = kf.getKeySpec(keyPair.getPublic(), ECPublicKeySpec.class); // import public key into other provider kf = KeyFactory.getInstance("EC", provVerify); publicKey = (PublicKey)kf.generatePublic(publicKeySpec); } catch (InvalidKeySpecException ex) { ex.printStackTrace(); } // do test Signature sig = Signature.getInstance("SHA256withECDSA", provSign); Signature ver = Signature.getInstance("SHA256withECDSA", provVerify); byte[] data = new byte[64]; // sign sig.initSign(privateKey); sig.update(data); byte [] sign = sig.sign(); // Working Signature verification ver.initVerify(publicKey); ver.update(data); if (ver.verify(sign) == false) { throw new Exception("Signature Verification failed"); }
The problem is that the sign is somehow encoded (I think in DER Format) and is between 70 and 72 Bytes long but I need a 64 Byte (unencoded/raw) signature.
What I have tried: Convert to fixed length 64 Byte Signature
DerInputStream derInputStream = new DerInputStream(sign); DerValue[] values = derInputStream.getSequence(2); byte[] random = values[0].getPositiveBigInteger().toByteArray(); byte[] signature = values[1].getPositiveBigInteger().toByteArray(); // r and s each occupy half the array // Remove padding bytes byte[] tokenSignature = new byte[64]; System.arraycopy(random, random.length > 32 ? 1 : 0, tokenSignature, random.length < 32 ? 1 : 0, random.length > 32 ? 32 : random.length); System.arraycopy(signature, signature.length > 32 ? 1 : 0, tokenSignature, signature.length < 32 ? 33 : 32, signature.length > 32 ? 32 : signature.length); System.out.println("Full Signature length: "+tokenSignature.length+" r length: "+random.length+" s length"+signature.length);
How to check the 64 Bytes tokenSignature now??? I don’t know how to convert the 64 Byte tokenSignature back to the right format
ver.initVerify(publicKey); ver.update(data); if (ver.verify(???) == false) { throw new Exception("Signature Verification failed"); }
I have achieved the 64 Byte signature verification with BouncyCastle ECDSASigner. But I can’t use ECDSASigner because it is not extending SignatureSpi and therefore not working with JCE complient crypto server.
Advertisement
Answer
I got it working now thanks to @MaartenBodewes
//How to Check Signature byte[] r = Arrays.copyOfRange(tokenSignature, 0,tokenSignature.length/2); byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length/2,tokenSignature.length); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream); ASN1EncodableVector v=new ASN1EncodableVector(); v.add(new ASN1Integer(new BigInteger(1,r))); v.add(new ASN1Integer(new BigInteger(1,s))); derOutputStream.writeObject(new DERSequence(v)); byte[] derSignature = byteArrayOutputStream.toByteArray(); ver.update(data); if (ver.verify(derSignature) == false) { throw new Exception("Signature Verification failed"); }