6

我们希望在我们的项目中使用Tink库,以便能够在给定公钥的情况下验证一些传入的签名。

我们拥有以下内容:

  1. 公钥作为字符串
  2. 签名本身
  3. 明文

在浏览了 Tink 的文档后,我们无法弄清楚如何加载公钥字符串以便PublicKeyVerifyFactory.

有没有人做过类似的事情?您是否在网上找到任何可以为我们指明正确方向的示例?

4

4 回答 4

3

您可以通过KeysetHandle从公钥创建一个CleartextKeysetHandle.read(),然后获取它的原始密钥,然后验证签名。你不需要知道 KeysetHandle 的私有部分来做到这一点,这是首先使用非对称密钥的关键。

问题是,我应该导出什么以便read()以后使用它?有几种方法,但一种方法是将 导出PublicKeysetHandle为 JSON 格式。CleartextKeysetHandle.write()您使用with将其导出JsonKeysetWriter.withOutputStream(),稍后您可以将其转换回KeysetHandleusing CleartextKeysetHandle.read()with JsonKeysetReader.withBytes()

所以,你是 Bob,想要向 Alice 公开你的公钥。在您的服务中,您将生成您的私钥,提取公钥,将其转换为 JSON 格式并以某种方式导出,例如 REST 端点:

鲍勃的申请

SignatureConfig.register();

// This is your, and only yours, private key.
KeysetHandle privateKeysetHandle =
    KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);

// This is the public key extracted from the private key.
KeysetHandle publicKeysetHandle = privateKeysetHandle.getPublicKeysetHandle();

ByteArrayOutputStream publicKeyStream = new ByteArrayOutputStream();

CleartextKeysetHandle.write(publicKeysetHandle,
    JsonKeysetWriter.withOutputStream(publicKeyStream));

// And this is the public key in JSON format.
// You can publish this in a REST endpoint.
return publicKeyStream.toString();

爱丽丝的申请

String publicKey = getThatJsonPublicKeyFromBobsEndpoint();

// Here the JSON with only the public key is converted into a KeysetHandle.
KeysetHandle keysetHandle = CleartextKeysetHandle
    .read(JsonKeysetReader.withBytes(publicKey.getBytes()));

// Getting the signature verifier from the public keyset handler.
PublicKeyVerify verifier = keysetHandle.getPrimitive(PublicKeyVerify.class);

// And finally verify Bob's signature for his message.
verifier.verify(bobsMessage.getSignature(), bobsMessage.getData());

在 Bob 的应用程序中,每次都会生成私钥。您可能希望坚持使用相同的私钥,因此您需要像 Alice 的应用程序一样存储该私钥并恢复它,但使用 PublicKeysetHandle 您将使用 PrivateKeysetHandle。上面的例子只是为了展示如何将公钥导出为字符串格式并稍后在另一个应用程序中恢复它。

于 2019-09-11T13:53:48.980 回答
1

我认为我的回答对亚历克斯来说为时已晚,但它可能对其他人有帮助。经过大量代码和密钥分析后,我编写了一个解决方案,该解决方案使用带有 Tink 加密例程的外部生成的 ECDSA 公钥验证外部生成的 ECDSA 签名。为了测试这一点,有一个帮助程序使用常规 JCE 工具生成“外部部分”,并将公钥、消息和签名保存在文本文件中(所有数据都是 Base64 编码的)。

我的解决方案加载数据文件(总共有 3 个数据文件来测试所有 3 个可用的 ECDSA 曲线(P256、P384 和 P521))。然后它以 Tink 自己的 JSON 格式创建一个新的公钥文件(同样:3 个文件用于 3 个密钥长度)——这个文件是一个手工解决方案,重新加载密钥文件并构造一个新的签名以符合 Tink。

最后,程序验证签名是否正确。请记住,我的源代码是“概念证明”,并没有针对任何东西进行优化:-) 任何关于更好编码的建议总是被要求!

在分析 Tink 源代码时,我看到也有“RAW”格式,但在 Tink 文档中我找不到任何如何使用它们的词:-(

你也可以在我的 Github-Archive 中找到完整的源代码:https ://github.com/java-crypto/H-Google-Tink 以及我的网站http://javacrypto.bplaced上对所有程序的更详细描述。 net/h-verify-external-signature-in-tink/。这些程序使用 Java 8-191 和 Java 11-0-1 进行了测试。

package tinkExternalSignatureVerification;
/*
* Herkunft/Origin: http://javacrypto.bplaced.net/
* Programmierer/Programmer: Michael Fehr
* Copyright/Copyright: frei verwendbares Programm (Public Domain)
* Copyright: This is free and unencumbered software released into the public domain.
* Lizenttext/Licence: <http://unlicense.org>
* getestet mit/tested with: Java Runtime Environment 8 Update 191 x64
* getestet mit/tested with: Java Runtime Environment 11.0.1 x64
* Datum/Date (dd.mm.jjjj): 18.11.2019
* Funktion: überprüft eine extern erzeugte ecdsa-signatur mittels google tink
* Function: verifies an external generated ecdsa-signature with google tink
*
* Sicherheitshinweis/Security notice
* Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion, 
* insbesondere mit Blick auf die Sicherheit ! 
* Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird.
* The program routines just show the function but please be aware of the security part - 
* check yourself before using in the real world !
* 
* Das Programm benötigt die nachfolgenden Bibliotheken (siehe Github Archiv):
* The programm uses these external libraries (see Github Archive):
* jar-Datei/-File: tink-1.2.2.jar
* https://mvnrepository.com/artifact/com.google.crypto.tink/tink/1.2.2
* jar-Datei/-File: protobuf-java-3.10.0.jar
* https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java/3.10.0
* jar-Datei/-File: json-20190722.jar
* https://mvnrepository.com/artifact/org.json/json/20190722
*  
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPoint;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetReader;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.config.TinkConfig;
import com.google.crypto.tink.signature.PublicKeyVerifyFactory;

public class VerifyEcdsaTinkSignature {

    static String pubKeyString = "";
    static String messageString = "";
    static String signatureString = "";
    public static byte[] xRec = null; // x-value of recoded public key
    public static byte[] yRec = null; // y-value of recoded public key

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        System.out.println("Verify a Classic ECDSA-signed message in Google Tink");
        TinkConfig.register();

        String publicKeyJsonFilenameTemplate = "ecdsa_tink_publickey_";
        String publicKeyJsonFilename = "";
        String filenameTemplate = "ecdsa_classic_data_";
        String filename;
        byte[] message = null;
        PublicKey pubKey;
        byte[] pubKeyByte = null;
        byte[] signatureClassic = null; // the signature from classic ecdsa
        boolean signatureVerification = false;
        int[] keylength = new int[] { 256, 384, 521 };
        // iterate through keylength
        for (int myKeylength : keylength) {
            filename = filenameTemplate + String.valueOf(myKeylength) + ".txt";
            publicKeyJsonFilename = publicKeyJsonFilenameTemplate + String.valueOf(myKeylength) + ".txt";
            pubKeyString = "";
            messageString = "";
            signatureString = "";
            // load data
            switch (myKeylength) {
            case 256: {
                loadData(filename);
                break;
            }
            case 384: {
                loadData(filename);
                break;
            }
            case 521: {
                loadData(filename);
                break;
            }
            default: {
                System.out.println("Error - signature keylength not supported");
                System.exit(0);
            }

            }
            // convert data from base64 to byte[]
            pubKeyByte = Base64.getDecoder().decode(pubKeyString);
            message = Base64.getDecoder().decode(messageString);
            signatureClassic = Base64.getDecoder().decode(signatureString);
            // rebuild publicKey
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pubKeyByte);
            pubKey = keyFactory.generatePublic(publicKeySpec);
            // get x + y value of public key
            returnPublicKeyXY(pubKey); // writes to variables xRec and yRec
            // construct a tink-style public key value for json-file
            byte[] keyValueClassic = generateKeyValue(myKeylength);
            String keyValueClassicString = Base64.getEncoder().encodeToString(keyValueClassic); // saved in value-field
                                                                                                // of json-file
            // save tink public key in json-format, gets the generated primaryKeyId
            int keyId = SaveJson.writeJson(publicKeyJsonFilename, keyValueClassicString);
            // construct a tink-style signature
            byte[] signatureTink = generateSignature(keyId, signatureClassic);
            // reload the self created public key
            KeysetHandle keysetHandle = CleartextKeysetHandle
                    .read(JsonKeysetReader.withFile(new File(publicKeyJsonFilename)));
            // verify signature
            signatureVerification = verifyMessage(keysetHandle, signatureTink, message);
            System.out.println("Data loaded from:" + filename + " The message is:" + new String(message, "UTF-8"));
            System.out.println("The provided signature is correct ?:" + signatureVerification);
        }
    }

    public static void loadData(String filenameLoad) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(filenameLoad));
        pubKeyString = reader.readLine();
        messageString = reader.readLine();
        signatureString = reader.readLine();
        reader.close();
    }

    public static String printHexBinary(byte[] bytes) {
        final char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    // source:
    // https://github.com/google/tink/blob/master/java/src/main/java/com/google/crypto/tink/subtle/EllipticCurves.java
    /**
     * Transforms a big integer to its minimal signed form, i.e., no extra zero byte
     * at the beginning except single one when the highest bit is set.
     */
    private static byte[] toMinimalSignedNumber(byte[] bs) {
        // Remove zero prefixes.
        int start = 0;
        while (start < bs.length && bs[start] == 0) {
            start++;
        }
        if (start == bs.length) {
            start = bs.length - 1;
        }

        int extraZero = 0;
        // If the 1st bit is not zero, add 1 zero byte.
        if ((bs[start] & 0x80) == 0x80) {
            // Add extra zero.
            extraZero = 1;
        }
        byte[] res = new byte[bs.length - start + extraZero];
        System.arraycopy(bs, start, res, extraZero, bs.length - start);
        return res;
    }

    public static void returnPublicKeyXY(PublicKey pub) {
        ECPublicKey key = (ECPublicKey) pub;
        ECPoint ecp = key.getW();
        BigInteger x = ecp.getAffineX();
        BigInteger y = ecp.getAffineY();
        // convert big integer to byte[]
        byte[] x_array = x.toByteArray();
        if (x_array[0] == 0) {
            byte[] tmp = new byte[x_array.length - 1];
            System.arraycopy(x_array, 1, tmp, 0, tmp.length);
            x_array = tmp;
        }
        byte[] y_array = y.toByteArray();
        if (y_array[0] == 0) {
            byte[] tmp = new byte[y_array.length - 1];
            System.arraycopy(y_array, 1, tmp, 0, tmp.length);
            y_array = tmp;
        }
        // some byte[] need an additional x00 in the beginning
        xRec = toMinimalSignedNumber(x_array);
        yRec = toMinimalSignedNumber(y_array);
    }

    public static byte[] generateKeyValue(int keylength) {
        // header depends on keylength
        byte[] header = null;
        switch (keylength) {
        case 256: {
            header = fromHexString("12060803100218021A"); // only for ECDSA_P256
            break;
        }
        case 384: {
            header = fromHexString("12060804100318021A"); // only for ECDSA_P384
            break;
        }
        case 521: {
            header = fromHexString("12060804100418021A"); // only for ECDSA_P521
            break;
        }
        }
        int x_length = xRec.length;
        int y_length = yRec.length;
        // build the value-field with public key in x-/y-notation
        byte[] x_header = new byte[] { (byte) x_length };
        byte[] y_preheader = fromHexString("22");
        byte[] y_header = new byte[] { (byte) y_length };
        // join arrays
        byte[] kv = new byte[header.length + x_header.length + xRec.length + +y_preheader.length + y_header.length
                + yRec.length];
        System.arraycopy(header, 0, kv, 0, header.length);
        System.arraycopy(x_header, 0, kv, header.length, x_header.length);
        System.arraycopy(xRec, 0, kv, (header.length + x_header.length), xRec.length);
        System.arraycopy(y_preheader, 0, kv, (header.length + x_header.length + xRec.length), y_preheader.length);
        System.arraycopy(y_header, 0, kv, (header.length + x_header.length + xRec.length + y_preheader.length),
                y_header.length);
        System.arraycopy(yRec, 0, kv,
                (header.length + x_header.length + xRec.length + y_preheader.length + y_header.length), yRec.length);
        return kv;
    }

    // this routine converts a Hex Dump String to a byte array
    private static byte[] fromHexString(final String encoded) {
        if ((encoded.length() % 2) != 0)
            throw new IllegalArgumentException("Input string must contain an even number of characters");
        final byte result[] = new byte[encoded.length() / 2];
        final char enc[] = encoded.toCharArray();
        for (int i = 0; i < enc.length; i += 2) {
            StringBuilder curr = new StringBuilder(2);
            curr.append(enc[i]).append(enc[i + 1]);
            result[i / 2] = (byte) Integer.parseInt(curr.toString(), 16);
        }
        return result;
    }

    public static byte[] generateSignature(int keyId, byte[] signatureByte) {
        byte[] header = fromHexString("01");
        // convert keyId from int to 4-byte byte[]
        byte[] keyIdBytes = ByteBuffer.allocate(4).putInt(keyId).array();
        // build the signature in tink-style with keyId included
        byte[] si = new byte[header.length + keyIdBytes.length + signatureByte.length];
        System.arraycopy(header, 0, si, 0, header.length);
        System.arraycopy(keyIdBytes, 0, si, header.length, keyIdBytes.length);
        System.arraycopy(signatureByte, 0, si, (header.length + keyIdBytes.length), signatureByte.length);
        return si;
    }

    public static boolean verifyMessage(KeysetHandle publicKeysetHandle, byte[] signature, byte[] message)
            throws UnsupportedEncodingException, GeneralSecurityException {
        Boolean verifiedBool = false;
        PublicKeyVerify verifier = PublicKeyVerifyFactory.getPrimitive(publicKeysetHandle);
        try {
            verifier.verify(signature, message);
            verifiedBool = true;
        } catch (GeneralSecurityException e) {
            verifiedBool = false;
        }
        return verifiedBool;
    }
}

您需要这个额外的帮助类来保存 JSON 文件:

package tinkExternalSignatureVerification;

/*
 * Diese Klasse gehört zu VerifyEcdsaTinkSignature.java
 * This class belongs to VerifyEcdsaTinkSignature.java
 * Herkunft/Origin: http://javacrypto.bplaced.net/
 * Programmierer/Programmer: Michael Fehr
 * Copyright/Copyright: frei verwendbares Programm (Public Domain)
 * Copyright: This is free and unencumbered software released into the public domain.
 * Lizenttext/Licence: <http://unlicense.org>
 */

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.security.SecureRandom;

public class SaveJson {

    public static int writeJson(String filename, String value) throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
        int keyId = newKeyId();
        String str = "{";
        writer.write(str + "\n");
        str = "    \"primaryKeyId\": " + keyId + ",";
        writer.append(str + "\n");
        str = "    \"key\": [{";
        writer.append(str + "\n");
        str = "        \"keyData\": {";
        writer.append(str + "\n");
        str = "            \"typeUrl\": \"type.googleapis.com/google.crypto.tink.EcdsaPublicKey\",";
        writer.append(str + "\n");
        str = "            \"keyMaterialType\": \"ASYMMETRIC_PUBLIC\",";
        writer.append(str + "\n");
        str = "            \"value\": \"" + value + "\"";
        writer.append(str + "\n");
        str = "        },";
        writer.append(str + "\n");
        str = "        \"outputPrefixType\": \"TINK\",";
        writer.append(str + "\n");
        str = "        \"keyId\": " + keyId + ",";
        writer.append(str + "\n");
        str = "        \"status\": \"ENABLED\"";
        writer.append(str + "\n");
        str = "    }]";
        writer.append(str + "\n");
        str = "}";
        writer.append(str);
        writer.close();

        return keyId;
    }

    // routines for keyId
    private static int newKeyId() {
        int keyId = randPositiveInt();
        keyId = randPositiveInt();
        return keyId;
    }

    // source:
    // https://github.com/google/tink/blob/08405fb55ba695b60b41f7f9ae198e5748152604/java/src/main/java/com/google/crypto/tink/KeysetManager.java
    /** @return positive random int */
    private static int randPositiveInt() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] rand = new byte[4];
        int result = 0;
        while (result == 0) {
            secureRandom.nextBytes(rand);
            result = ((rand[0] & 0x7f) << 24) | ((rand[1] & 0xff) << 16) | ((rand[2] & 0xff) << 8) | (rand[3] & 0xff);
        }
        return result;
    }
}

数据文件由这个简短的程序生成:

package tinkExternalSignatureVerification;

/*
* Herkunft/Origin: http://javacrypto.bplaced.net/
* Programmierer/Programmer: Michael Fehr
* Copyright/Copyright: frei verwendbares Programm (Public Domain)
* Copyright: This is free and unencumbered software released into the public domain.
* Lizenttext/Licence: <http://unlicense.org>
* getestet mit/tested with: Java Runtime Environment 8 Update 191 x64
* getestet mit/tested with: Java Runtime Environment 11.0.1 x64
* Datum/Date (dd.mm.jjjj): 18.11.2019
* Funktion: erzeugt eine ecdsa-signatur mittels jce
* Function: generates an ecdsa-signature with jce
*
* Sicherheitshinweis/Security notice
* Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion, 
* insbesondere mit Blick auf die Sicherheit ! 
* Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird.
* The program routines just show the function but please be aware of the security part - 
* check yourself before using in the real world !
*  
*/

import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;

public class GenerateEcdsaClassicSignature {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException,
            InvalidKeyException, SignatureException, IOException {
        System.out.println("Generate a ECDSA Private-/PublicKey and signs a message");

        byte[] message = "This is the message".getBytes("utf-8");
        String messageString = "";
        String filenameTemplate = "ecdsa_classic_data_";
        String filename;
        byte[] signature = null;
        String signatureString = "";
        PrivateKey privKey;
        PublicKey pubKey;
        String pubKeyString = "";
        int[] keylength = new int[] { 256, 384, 521 };
        // iterate through keylength
        for (int myKeylength : keylength) {
            filename = filenameTemplate + String.valueOf(myKeylength) + ".txt";
            // generate keypair
            KeyPair keyPair = generateEcdsaClassicKeyPair(myKeylength);
            privKey = keyPair.getPrivate();
            pubKey = keyPair.getPublic();
            signature = null;
            // sign the message
            switch (myKeylength) {
            case 256: {
                signature = signEcdsaClassic(privKey, message, "SHA256withECDSA");
                break;
            }
            case 384: {
                signature = signEcdsaClassic(privKey, message, "SHA512withECDSA");
                break;
            }
            case 521: {
                signature = signEcdsaClassic(privKey, message, "SHA512withECDSA");
                break;
            }
            default: {
                System.out.println("Error - signature keylength not supported");
                System.exit(0);
            }

            }
            // convert data to base64
            pubKeyString = Base64.getEncoder().encodeToString(pubKey.getEncoded());
            messageString = Base64.getEncoder().encodeToString(message);
            signatureString = Base64.getEncoder().encodeToString(signature);
            // save data to file
            writeData(filename, pubKeyString, messageString, signatureString);
            System.out.println("Data written to:" + filename);
        }

    }

    public static KeyPair generateEcdsaClassicKeyPair(int keylengthInt)
            throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyPairGenerator keypairGenerator = KeyPairGenerator.getInstance("EC");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        keypairGenerator.initialize(keylengthInt, random);
        return keypairGenerator.generateKeyPair();
    }

    public static byte[] signEcdsaClassic(PrivateKey privateKey, byte[] message, String ecdsaHashtype)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
        Signature signature = Signature.getInstance(ecdsaHashtype);
        signature.initSign(privateKey);
        signature.update(message);
        byte[] sigByte = signature.sign();
        return sigByte;
    }

    public static void writeData(String filenameWrite, String pubKeyWrite, String messageWrite, String signatureWrite)
            throws IOException {
        FileWriter fw = new FileWriter(filenameWrite);
        fw.write(pubKeyWrite + "\n");
        fw.write(messageWrite + "\n");
        fw.write(signatureWrite + "\n");
        fw.write(
                "This file contains data in base64-format: publicKey, message, signature. Number in filename is keylength.");
        fw.flush();
        fw.close();
    }

}

最后 - 如果您想使用 JCE 验证数据文件,请使用此程序:

package tinkExternalSignatureVerification;

/*
* Herkunft/Origin: http://javacrypto.bplaced.net/
* Programmierer/Programmer: Michael Fehr
* Copyright/Copyright: frei verwendbares Programm (Public Domain)
* Copyright: This is free and unencumbered software released into the public domain.
* Lizenttext/Licence: <http://unlicense.org>
* getestet mit/tested with: Java Runtime Environment 8 Update 191 x64
* getestet mit/tested with: Java Runtime Environment 11.0.1 x64
* Datum/Date (dd.mm.jjjj): 18.11.2019
* Funktion: überprüft eine ecdsa-signatur mittels jce
* Function: verifies an ecdsa-signature with jce
*
* Sicherheitshinweis/Security notice
* Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine korrekte Funktion, 
* insbesondere mit Blick auf die Sicherheit ! 
* Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird.
* The program routines just show the function but please be aware of the security part - 
* check yourself before using in the real world !
*  
*/

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class VerifyEcdsaClassicSignature {

    static String pubKeyString = "";
    static String messageString = "";
    static String signatureString = "";

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException,
            InvalidKeyException, SignatureException {
        System.out.println("Verify a ECDSA-signed message");
        String filenameTemplate = "ecdsa_classic_data_";
        String filename;
        byte[] message = null;
        PublicKey pubKey;
        byte[] pubKeyByte = null;
        byte[] signature = null;
        String ecdsaHashtype = "";
        boolean signatureVerification = false;
        int[] keylength = new int[] { 256, 384, 521 };
        // iterate through keylength
        for (int myKeylength : keylength) {
            filename = filenameTemplate + String.valueOf(myKeylength) + ".txt";
            pubKeyString = "";
            messageString = "";
            signatureString = "";
            // load data
            switch (myKeylength) {
            case 256: {
                loadData(filename);
                ecdsaHashtype = "SHA256withECDSA";
                break;
            }
            case 384: {
                loadData(filename);
                ecdsaHashtype = "SHA512withECDSA";
                break;
            }
            case 521: {
                loadData(filename);
                ecdsaHashtype = "SHA512withECDSA";
                break;
            }
            default: {
                System.out.println("Error - signature keylength not supported");
                System.exit(0);
            }

            }
            // convert data from base64 to byte[]
            pubKeyByte = Base64.getDecoder().decode(pubKeyString);
            message = Base64.getDecoder().decode(messageString);
            signature = Base64.getDecoder().decode(signatureString);
            // rebuild publicKey
            KeyFactory keyFactory = KeyFactory.getInstance("EC");
            X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pubKeyByte);
            pubKey = keyFactory.generatePublic(publicKeySpec);
            // verify signature
            signatureVerification = verifySignature(pubKey, ecdsaHashtype, message, signature);
            System.out.println("Data loaded from:" + filename + " The message is:" + new String(message, "UTF-8"));
            System.out.println("The provided signature is correct ?:" + signatureVerification);
        }
    }

    public static void loadData(String filenameLoad) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(filenameLoad));
        pubKeyString = reader.readLine();
        messageString = reader.readLine();
        signatureString = reader.readLine();
        reader.close();
    }

    public static Boolean verifySignature(PublicKey publicKey, String ecdsaHashtype, byte[] messageByte,
            byte[] signatureByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
        Signature publicSignature = Signature.getInstance(ecdsaHashtype);
        publicSignature.initVerify(publicKey);
        publicSignature.update(messageByte);
        return publicSignature.verify(signatureByte);
    }

}
于 2019-11-18T15:39:14.283 回答
1

一些代码示例片段来说明:

public static boolean verify(byte[] data, byte[] signature, KeysetHandle publicKeysetHandle, CIPHER_ASYMMETRIC_ALGOS algo_chosen) throws IOException, GeneralSecurityException {
    TinkConfig.register();
    boolean status_verification = False;


    try {
        PublicKeyVerify verifier = PublicKeyVerifyFactory.getPrimitive( publicKeysetHandle);
    verifier.verify(signature, data);
        status_verification = True;
    } catch (GeneralSecurityException e) {
       status_verification = False;
    }


    return status_verification;
}

// 假设您已经拥有以字节为单位的签名。

用法:



boolean status_verification = verify(data, signature, publicKeysetHandle);

if(status_verification == True){
    System.out.println(“status_verification: PASS”);
} else {
    System.out.println(“status_verification: FAIL”);
}

于 2019-06-21T18:30:26.470 回答
0

Tink 将公钥存储在 protobuf 中。有一天,我将编写一些代码,允许将常见的公钥格式(如 PEM 或 JWK)转换为 protobuf,但在此之前,恐怕您必须自己编写代码(并做出贡献!)。

于 2019-01-17T23:04:21.663 回答