【问题标题】:(1)Convert the ECDSA private & public key, (2)Verification by ECDSA(1)转换ECDSA私钥和公钥,(2)ECDSA验证
【发布时间】:2015-01-13 23:52:33
【问题描述】:

discussion 之后,这是一个简单的教程,如何在不使用任何第三方库的情况下在 java 中使用 ECDSA 算法对字符串进行签名。但问题是:

  1. 如何将公钥和私钥转换为字符串? (因为我想将它们发送到数据库中)。
  2. 谁能帮我创建一个简单的教程,说明如何在 java 中使用 ECDSA 算法验证消息?此时我需要包含签名和公钥作为验证方法。

这是我的 java 代码中的场景,假设有发送方和接收方:

  • 发送方
package sender;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
public class Sign {    
  public static void main(String[] args) throws Exception {
      /*
       * Generate a key pair
       */
      KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
      SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

      keyGen.initialize(256, random);

      KeyPair pair = keyGen.generateKeyPair();
        
      /*
      Generate the private and the public key
      */

      PrivateKey priv = pair.getPrivate();
      /*
      *and then Convert the priv key into a String;
      *HOW can i do that ? this what i'm asking
      */
                   
      PublicKey pub = pair.getPublic();
      /*
      Convert the pub key into a String;
      HOW can i do that ? this what i'm asking
      */      
       
      /*
      -------Encrypt the pub and the priv key, i do with my own code  
      -------Store the enrypted pub & priv key into the database
      -------I'm doing this with my own code
      */
        
        
      /*
      * Create a Signature object and initialize it with the private key
      */
      Signature dsa = Signature.getInstance("SHA1withECDSA");

      dsa.initSign(priv);

      String str = "This is string to sign";
      byte[] strByte = str.getBytes("UTF-8");
      dsa.update(strByte);

      /*
      * Now that all the data to be signed has been read in, generate a
      * signature for it
      */

      byte[] realSig = dsa.sign();
      System.out.println("Signature: " + 
             new BigInteger(1, realSig).toString(16));
      /*
      and Then i'm storing this signature into my database. 
      i have done with this
      */
    }
}
  • 接收方
package recipient;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
 public class Verify {   
   public static void main(String[] args) throws Exception {
   /*
   Step one, taking public key from the database.
   Step two, receive the message + signature.
   Step three, split the message and signature into an "array[0]" for message,
   and "array[1] for the signature"
        
   Verify the signature <--- Here's what im asking to anybody, 
   how can i do, i mean the sample code ? 
   */  
   }
}

对不起我的英语不好:D

【问题讨论】:

  • 对不起,我再也不会那样做了。我是这个“*”的新手,感谢您的警告@ArtjomB。

标签: java cryptography digital-signature jce ecdsa


【解决方案1】:

您就处理 ECDSA 提出了很多不同的问题。我将在这里解决您关于数据库存储的第一个问题。如果您想了解如何正确使用它,我建议您对 ECDSA 的机制进行一些额外的研究。无论如何,这里给出的例子都很难脱离上下文来理解。

要将密钥存储为字符串,您必须首先以 编码 格式检索表示密钥的字节数组(注意:编码未加密)。这可以通过使用类Key 中的getEncoded() 方法来完成,它是PublicKey 和PrivateKey 的超级接口。

例子:

PrivateKey key = // ...

byte[] enc_key = key.getEncoded();

// Byte array to string

StringBuilder key_builder = new StringBuilder();

for(byte b : enc_key){
    key_builder.append(String.format("%02x", b));
}

String serialized_key = key_builder.toString();

要从数据库中再次加载密钥,请将字符串解析为字节数组,将其传递给适当的密钥规范,然后使用密钥工厂检索它。

例子:

String serialzed_key = // ...

byte[] encoded_key = // serialzed_key -> byte array conversion

// If key is private, use PKCS #8

PKCS8EncodedKeySpec formatted_private = new PKCS8EncodedKeySpec(encoded_key);

// or, if key is public, use X.509

X509EncodedKeySpec formatted_public = new X509EncodedKeySpec(encoded_key);

// Retrieve key using KeyFactory

KeyFactory kf = KeyFactory.getInstance("EC");

PublicKey pub = kf.generatePublic(formatted_public);

PrivateKey priv = kf.generatePrivate(formatted_private);

如果您只想使用 ECDSA 作为签名算法,则验证与使用 verify 方法而不是 sign 方法进行签名相同,如下所示:

byte[] message_hash = // ...
byte[] candidate_message = // ...

PublicKey pub = // ...

Signature dsa = Signature.getInstance("SHA1withECDSA");

dsa.initVerify(pub);

dsa.update(candidate_message);

boolean success = dsa.verify(message_hash);

【讨论】:

  • 感谢您的回答,在这一点上有点错误 :) for(byte b : enc_key){ key_builder.append(String.format("%02x")); } 您可能忘记在附加字符串时添加“b”:for(byte b : enc_key){ key_builder.append(String.format("%02x", b)); } 现在我要解决验证问题.
  • @anIndonesian 哎呀,我真傻。在答案中修复它。所以你只使用 ECDSA 进行签名验证?如果是这样,它与签名的过程完全相同,只是您使用方法initVerify()verify() 而不是initSign()sign()
  • 当我尝试解析公钥和私钥时出现错误,想要保持这个线程干净,直到我编辑到一个已解决的问题我将带有错误信息的代码粘贴到Load private & public key 对不起,如果我正在使用外部链接:)
  • 哇,另一种将公钥和私钥解析为“字节数组”的方法,我正在使用 bouncy castle lib :) 将byte[] enc_key = key.getEncoded();String keyString = new String(Base64.encode(enc_key)); 结尾,然后发送“keyString” 进入数据库,用Base64.decode(keyString)加载密钥,然后格式化这两个密钥,它可以工作。现在我真的要去我的验证码了。谢谢终结者。
  • 抱歉,验证消息时又出现了一点错误:) 我们必须使用原始消息运行更新方法,因为在验证过程之前 ori_messg 处于空状态,代码类似于 @ 987654338@ dsa.update(ori_message); 然后我们可以运行验证 dsa.verify(message_hash); 现在布尔返回 true。感谢您提供有用的答案:),请更新您的“验证”答案。