Java - デジタル署名の例

Java –デジタル署名の例

Asymmetric Cryptography exampleでは、暗号化での公開鍵ペアの使用について説明しました。 Another important use of the Public Key Infrastructure is in Digital Signatures. Digital Signatures are the digital equivalent of handwritten signatures with one important difference; they are not unique but come as a product of the message.

有効なデジタル署名は、メッセージが既知の送信者によって作成された(authentication)、送信者がメッセージの送信を拒否できない(non-repudiation)、メッセージが送信されなかったと信じる理由を受信者に与えます転送中に変更されました(integrity)。 (出典:Wikipedia

1. 公開鍵と秘密鍵のペアを生成する

公開鍵と秘密鍵のペアを生成するコードは、Asymmetric Cryptography exampleで使用されているものと同じです。手順1を参照するか、記事の最後にあるすべてのソースを含むソースコードをダウンロードしてください–GenerateKeys.java

java-digital-signatures-1

2. メッセージに署名する

次に、メッセージを作成して署名する必要があります。 メッセージと署名は別々のファイルにすることができますが、この例では、それらをbyte[]Listに追加し、Objectとしてファイルに書き込みます。

Message.java

package com.techfou.sender;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

public class Message {
    private List list;

    //The constructor of Message class builds the list that will be written to the file.
    //The list consists of the message and the signature.
    public Message(String data, String keyFile) throws InvalidKeyException, Exception {
        list = new ArrayList();
        list.add(data.getBytes());
        list.add(sign(data, keyFile));
    }

    //The method that signs the data using the private key that is stored in keyFile path
    public byte[] sign(String data, String keyFile) throws InvalidKeyException, Exception{
        Signature rsa = Signature.getInstance("SHA1withRSA");
        rsa.initSign(getPrivate(keyFile));
        rsa.update(data.getBytes());
        return rsa.sign();
    }

    //Method to retrieve the Private Key from a file
    public PrivateKey getPrivate(String filename) throws Exception {
        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }

    //Method to write the List of byte[] to a file
    private void writeToFile(String filename) throws FileNotFoundException, IOException {
        File f = new File(filename);
        f.getParentFile().mkdirs();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename));
        out.writeObject(list);
        out.close();
        System.out.println("Your file is ready.");
    }

    public static void main(String[] args) throws InvalidKeyException, IOException, Exception{
        String data = JOptionPane.showInputDialog("Type your message here");

        new Message(data, "MyKeys/privateKey").writeToFile("MyData/SignedData.txt");
    }
}

出力:

java-digital-signatures-2

Your file is ready.

java-digital-signatures-3

3. 署名を検証する

受信者はファイル(he knows it is a List of 2 byte arrays; the message and the signature)を持っており、メッセージが事前共有公開鍵を使用して予期された送信元から送信されていることを確認したいと考えています。

VerifyMessage.java

package com.techfou.receiver;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;

public class VerifyMessage {
    private List list;

    @SuppressWarnings("unchecked")
    //The constructor of VerifyMessage class retrieves the byte arrays from the File
    //and prints the message only if the signature is verified.
    public VerifyMessage(String filename, String keyFile) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
        this.list = (List) in.readObject();
        in.close();

        System.out.println(verifySignature(list.get(0), list.get(1), keyFile) ? "VERIFIED MESSAGE" +
          "\n----------------\n" + new String(list.get(0)) : "Could not verify the signature.");
    }

    //Method for signature verification that initializes with the Public Key,
    //updates the data to be verified and then verifies them using the signature
    private boolean verifySignature(byte[] data, byte[] signature, String keyFile) throws Exception {
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(getPublic(keyFile));
        sig.update(data);

        return sig.verify(signature);
    }

    //Method to retrieve the Public Key from a file
    public PublicKey getPublic(String filename) throws Exception {
        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
    }

    public static void main(String[] args) throws Exception{
        new VerifyMessage("MyData/SignedData.txt", "MyKeys/publicKey");
    }
}

出力:

VERIFIED MESSAGE
----------------
Hello from example.com!!

ソースコードをダウンロード

ダウンロード–digitalsignatures.zip(6 KB)