【问题标题】:Google/Tink: How use public key to verify signatureGoogle/Tink:如何使用公钥验证签名
【发布时间】:2019-04-13 04:27:10
【问题描述】:

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

我们拥有以下:

  1. 字符串形式的公钥
  2. 签名本身
  3. 明文

查看 Tink 的文档后,我们无法弄清楚如何加载公钥字符串以便PublicKeyVerifyFactory 使用。

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

【问题讨论】:

  • 您是否尝试过以导出 JSON 格式存储公钥并尝试使用 CleartextKeysetHandle.read 加载它?
  • 没有单一的公钥格式,所以要完全回答这个问题,需要发布公钥。
  • 我也在寻找一种简单的方法来做到这一点,我发现最接近的是 'com.google.crypto.tink.subtle' 包中的 'EllipticCurve' 类。

标签: java cryptography digital-signature public-key tink


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    一些代码示例sn-p来说明:

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

    【讨论】:

      【解决方案3】:

      您可以通过CleartextKeysetHandle.read() 从公钥创建KeysetHandle,然后获取它的原始值,然后验证签名。您无需知道 KeysetHandle 的私有部分即可执行此操作,这是首先使用非对称密钥的关键。

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

      因此,您是 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。上面的例子只是为了展示如何将公钥导出为字符串格式并稍后在另一个应用程序中恢复它。

      【讨论】:

        【解决方案4】:

        我认为我的回答对 Alex 来说为时已晚,但它可能对其他人有所帮助。经过大量代码和密钥分析后,我编写了一个解决方案,该解决方案使用带有 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);
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2013-08-17
          • 2012-07-26
          • 1970-01-01
          • 2013-05-28
          • 1970-01-01
          • 2017-02-18
          • 2015-03-08
          • 2019-08-11
          • 1970-01-01
          相关资源
          最近更新 更多