如何用私钥签署字符串

Posted

技术标签:

【中文标题】如何用私钥签署字符串【英文标题】:How to sign string with private key 【发布时间】:2011-11-05 16:20:00 【问题描述】:

如果我已经拥有byte[]String 的私​​钥,如何使用SHA1withRSA 获取字符串的签名?

【问题讨论】:

你不能用公钥签署任何东西。 公钥只能用于阅读消息,但不能用公钥对新消息进行签名。可以使用私钥对消息进行签名。 以上两个cmets其实都不对(通常情况下)。大多数情况下,您可以使用任一密钥(私有或公共)加密(并因此签名)内容。这就是非对称加密的工作原理。如果 Bob 想向 Alice 发送加密消息,他实际上使用 Alice 的公钥来加密他的消息,而她将使用她的私钥来解密。如果他还想签署消息,他使用他的私钥加密消息的散列,而 Alice 使用 Bob 的公钥解密该散列并根据收到的消息验证它。 【参考方案1】:

我猜你说的是你事先知道密钥对并想用它签名/验证。

请看下面的代码。

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;

import sun.misc.BASE64Encoder;

public class MainClass 
    public static void main(String[] args) throws Exception 

        KeyPair keyPair = getKeyPair();

        byte[] data = "test".getBytes("UTF8");

        Signature sig = Signature.getInstance("SHA1WithRSA");
        sig.initSign(keyPair.getPrivate());
        sig.update(data);
        byte[] signatureBytes = sig.sign();
        System.out.println("Signature:" + new BASE64Encoder().encode(signatureBytes));

        sig.initVerify(keyPair.getPublic());
        sig.update(data);

        System.out.println(sig.verify(signatureBytes));
    

    private static KeyPair getKeyPair() throws NoSuchAlgorithmException 
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        return kpg.genKeyPair();
    

在这里您需要更改方法 getKeyPair() 以提供您已知的密钥对。您可以从 java 密钥库 [JKS] 加载它。

您不能只使用任意字节数组作为您的公钥或私钥。它们应该是相关的。

【讨论】:

如果您必须在另一个程序中进行验证怎么办?假设客户端想要对要在 URL 中传递的字符串进行签名和编码,而服务器想要使用公钥对字符串进行解码?上面的例子不起作用,因为 Signature 对象不会是服务器端的同一个实例。 哦。我知道了。您必须将数据和数字签名都传递到服务器端。【参考方案2】:

您首先必须从字节数组创建一个公钥

byte publicKeyBytes[] = .... your public key in bytes ... 
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes)); 
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

使用公钥加密后

String data = "... data to be encrypted ....";
String alg = "RSA/ECB/PKCS1Padding";
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte encryptedBytes[] = cipher.doFinal(data.getBytes());

现在只有拥有私钥的人才能读取您的数据

@rczajka:公钥是密钥。您可以使用它来签署只有所有者(拥有 privateKey)才能读取的内容。

【讨论】:

他不是在问如何加密数据,而是在问如何对数据进行签名。加密!=签名【参考方案3】:
public static String sign(String samlResponseString, String keystoreFile, String keyStorePassword, String privateKeyPassword, String alias)
            throws NoSuchAlgorithmException, UnsupportedEncodingException,
            InvalidKeyException, SignatureException 
        PrivateKey pkey=getPrivateKey(  keystoreFile,   keyStorePassword, privateKeyPassword,   alias);
        String signedString = null;
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(pkey);
        signature.update(samlResponseString.getBytes());
        byte[] signatureBytes = signature.sign();
        byte[] encryptedByteValue = Base64.encodeBase64(signatureBytes);
        signedString = new String(encryptedByteValue, "UTF-8");
        System.out.println(signedString);
        return signedString;
    

【讨论】:

getPrivateKey 中定义了什么?【参考方案4】:

我使用bouncy-castle对数据进行签名和验证。

你应该添加maven依赖:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.56</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.56</version>
</dependency>

将 RSA 私钥或公钥从磁盘文件加载到 Java 对象中

首先,我们需要能够将 RSA 私钥或公钥从磁盘文件加载到 Bouncy Castle 的适当类的 Java 对象中

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.lang3.Validate;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;

public class KeyUtil 

    public static AsymmetricKeyParameter loadPublicKey(InputStream is) 
        SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) readPemObject(is);
        try 
            return PublicKeyFactory.createKey(spki);
         catch (IOException ex) 
            throw new RuntimeException("Cannot create public key object based on input data", ex);
        
    

    public static AsymmetricKeyParameter loadPrivateKey(InputStream is) 
        PEMKeyPair keyPair = (PEMKeyPair) readPemObject(is);
        PrivateKeyInfo pki = keyPair.getPrivateKeyInfo();
        try 
            return PrivateKeyFactory.createKey(pki);
         catch (IOException ex) 
            throw new RuntimeException("Cannot create private key object based on input data", ex);
        
    

    private static Object readPemObject(InputStream is) 
        try 
            Validate.notNull(is, "Input data stream cannot be null");
            InputStreamReader isr = new InputStreamReader(is, "UTF-8");
            PEMParser pemParser = new PEMParser(isr);

            Object obj = pemParser.readObject();
            if (obj == null) 
                throw new Exception("No PEM object found");
            
            return obj;
         catch (Throwable ex) 
            throw new RuntimeException("Cannot read PEM object from input data", ex);
        
    


创建 RSA 数字签名

        // GIVEN: InputStream prvKeyInpStream
    AsymmetricKeyParameter privKey = KeyUtil.loadPrivateKey(prvKeyInpStream);

    // GIVEN: byte[] messageBytes = ...
    RSADigestSigner signer = new RSADigestSigner(new SHA512Digest());
    signer.init(true, privKey);
    signer.update(messageBytes, 0, messageBytes.length);

    try 
        byte[] signature = signer.generateSignature();
     catch (Exception ex) 
        throw new RuntimeException("Cannot generate RSA signature. " + ex.getMessage(), ex);
    

验证 RSA 数字签名

// GIVEN: InputStream pubKeyInpStream
AsymmetricKeyParameter publKey = KeyUtil.loadPublicKey(pubKeyInpStream);

// GIVEN: byte[] messageBytes
RSADigestSigner signer = new RSADigestSigner(new SHA512Digest());
signer.init(false, publKey);
signer.update(messageBytes, 0, messageBytes.length);

// GIVEN: byte[] signature - see code sample above
boolean isValidSignature = signer.verifySignature(signature);

【讨论】:

【参考方案5】:
public static String sign(String plainText, PrivateKey privateKey) throws Exception 
    Signature privateSignature = Signature.getInstance("SHA256withRSA");
    privateSignature.initSign(privateKey);
    privateSignature.update(plainText.getBytes(UTF_8));

    byte[] signature = privateSignature.sign();

    return Base64.getEncoder().encodeToString(signature);


public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception 
    Signature publicSignature = Signature.getInstance("SHA256withRSA");
    publicSignature.initVerify(publicKey);
    publicSignature.update(plainText.getBytes(UTF_8));

    byte[] signatureBytes = Base64.getDecoder().decode(signature);

    return publicSignature.verify(signatureBytes);

【讨论】:

嗨,Durga,欢迎来到 SO。在答案中发布 code-sn-ps 真的很好。如果你添加一点解释就更好了。不过,感谢您的第一次贡献!

以上是关于如何用私钥签署字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何用分类帐签署比特币 psbt?

如何用python签署数字签名?

如何用两个字符串生成一个唯一的数字

java编写非对称加密,解密,公钥加密,私钥解密,RSA,rsa

Web3:访问 MetaMask 钱包中的私钥

如何使用现有钱包签署使用节点 JS 脚本运行的 solana 交易?