2015-12-03 6 views
4

Мне нужна фиксированная длина 64 байтовой ECDSA-сигнатуры с кривой NIST P-256.Фиксированная длина 64 байта EC P-256 Подпись с JCE

Реализация должна использовать JCE.

Следующий образец кода может сгенерировать подпись и проверить ее.

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"); 
     } 

Проблема заключается в том, что знак каким-то образом кодируются (я думаю, что в DER формате) и находится между 70 и 72 байтами долго, но мне нужно 64 байт (неперекодированный/сырец) подпись.

То, что я пробовал: Преобразовать фиксированную длину 64 байт Подписи

 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); 

Как проверить 64 Bytes tokenSignature Теперь ??? я не знаю, как преобразовать 64 байт tokenSignature обратно в нужный формат

 ver.initVerify(publicKey); 
     ver.update(data); 
     if (ver.verify(???) == false) 
     { 
     throw new Exception("Signature Verification failed"); 
     } 

я достиг проверки подписи 64 байт с BouncyCastle ECDSASigner. Но я не могу использовать ECDSASigner, потому что он не расширяет SignatureSpi и, следовательно, не работает с криптовым сервером JCE complient.

+1

Положите два целых числа в последовательности, используя 'DEROutputStream'? –

+0

Спасибо, Маартен, теперь я понял. Я уже пробовал это с помощью DEROutputStream и не смог выполнить первый раз. – Hollerweger

+0

@MaartenBodewes Вы можете добавить свой комментарий к ответе :-) – Hollerweger

ответ

3

Я получил это работает теперь благодаря @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"); 
} 
+0

Да, это похоже на мой код. Конечно, я бы не использовал литералы для размера подписи (в 'copyOfRange'), но в противном случае решение должно быть правильным. –

+0

Я обновил ответ, спасибо за отзыв – Hollerweger

+0

Быстрый вопрос: всегда ли конструктор ASN1Integer приводит к положительному целому числу? –