如何使用私钥对字符串签名

33

如果我已经有byte[]String格式的私钥,那么如何使用SHA1withRSA对一个字符串生成签名?


4
公钥无法签署任何文件。 - rczajka
2
公钥只能用于读取消息,但不能使用公钥签署新消息。私钥可用于签署消息。 - monksy
10
上述两条评论实际上并不正确(通常情况下)。大多数时候,您可以使用任何一个密钥(私钥或公钥)加密(因此签名)内容。这就是非对称加密的工作原理。如果Bob想发送一条加密信息给Alice,他实际上使用Alice的公钥来加密他的消息,而她将使用她的私钥来解密。如果他还想签署该消息,他将使用自己的私钥来加密消息的哈希值,而Alice将使用Bob的公钥来解密该哈希值,并将其与接收到的消息进行验证。 - StFS
5个回答

72

我猜你的意思是你事先知道密钥对并想要使用它来签名/验证。

请参考以下代码。

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对象在服务器端上不会是相同的实例。 - Michael Starkie
哦,我明白了。你需要将数据和数字签名都传递到服务器端。 - Michael Starkie

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

2
嗨Durga,欢迎来到SO。在答案中发布代码片段真的很好。如果您能加上一点解释就更好了。感谢您的第一次贡献! - fragmentedreality
这个人太酷了,不需要解释。 - Ngọc Hy

1
我会翻译中文。下面是需要翻译的内容:

我使用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);

-1
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 函数中定义了什么? - Carlos Silva

-3

首先,你必须从字节数组创建一个公钥。

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:公钥是一把钥匙。您可以使用它来签署只有所有者(拥有私钥的人)才能阅读的内容。


12
他并不是在问如何加密数据,他在问如何对数据进行签名。加密!=签名 - Diego Palomar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接