用Bouncy Castle(Java)重建ED25519键 [英] Rebuild of ED25519 keys with Bouncy Castle (Java)

查看:92
本文介绍了用Bouncy Castle(Java)重建ED25519键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Bouncy Castle的最新(测试版)版本(bcprov-jdk15on-161b20.jar)支持ED25519和ED448 EC加密用于签名.我设置了这个完整的工作示例,它可以按预期工作.

The latest (beta) version of Bouncy Castle (bcprov-jdk15on-161b20.jar) supports ED25519 and ED448 EC cryptography for signing purposes. I setup this full working example and it works as expected.

我的问题:我是否正确地重建了私钥和公钥,因为我在bc测试中没有找到任何示例?我期望必须使用某些Spec功能,例如"X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content)"来进行RSA密钥重建,但是我的代码可以正常工作.

My question: did I rebuild the private and public keys correctly as I didn't found any example in the bc-tests ? I expected that I have to use some Spec-functionality like "X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content)" for RSA-key rebuilding but my code is working.

package bc;
// original source: https://github.com/bcgit/bc-java/blob/master/core/src/test/java/org/bouncycastle/crypto/test/Ed25519Test.java
// needs bouncy castle beta: bcprov-jdk15on-161b20.jar (version 1.605)
// tested with Java 8 Build 191 x64
// this is a full working example for generating, signing, verififying with ed25519 keys
// code: https://github.com/java-crypto/Bouncy-Castle
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.io.UnsupportedEncodingException;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class Ed25519Bc {

    public static void main(String[] args) throws DataLengthException, CryptoException, UnsupportedEncodingException {
        System.out.println("ED25519 with BC");
        Security.addProvider(new BouncyCastleProvider());
        Provider provider = Security.getProvider("BC");
        System.out.println("Provider          :" + provider.getName() + " Version: " + provider.getVersion());
        // generate ed25519 keys
        SecureRandom RANDOM = new SecureRandom();
        Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
        keyPairGenerator.init(new Ed25519KeyGenerationParameters(RANDOM));
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
        Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();
        Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters) asymmetricCipherKeyPair.getPublic();
        // the message
        byte[] message = "Message to sign".getBytes("utf-8");
        // create the signature
        Signer signer = new Ed25519Signer();
        signer.init(true, privateKey);
        signer.update(message, 0, message.length);
        byte[] signature = signer.generateSignature();
        // verify the signature
        Signer verifier = new Ed25519Signer();
        verifier.init(false, publicKey);
        verifier.update(message, 0, message.length);
        boolean shouldVerify = verifier.verifySignature(signature);
        // output
        byte[] privateKeyEncoded = privateKey.getEncoded();
        byte[] publicKeyEncoded = publicKey.getEncoded();
        System.out.println("privateKey Length :" + privateKeyEncoded.length + " Data:"
                + DatatypeConverter.printHexBinary(privateKeyEncoded));
        System.out.println("publicKey Length  :" + publicKeyEncoded.length + " Data:"
                + DatatypeConverter.printHexBinary(publicKeyEncoded));
        System.out.println(
                "signature Length  :" + signature.length + " Data:" + DatatypeConverter.printHexBinary(signature));
        System.out.println("signature correct :" + shouldVerify);
        // rebuild the keys
        System.out.println("Rebuild the keys and verify the signature with rebuild public key");
        Ed25519PrivateKeyParameters privateKeyRebuild = new Ed25519PrivateKeyParameters(privateKeyEncoded, 0);
        Ed25519PublicKeyParameters publicKeyRebuild = new Ed25519PublicKeyParameters(publicKeyEncoded, 0);
        byte[] privateKeyRebuildEncoded = privateKeyRebuild.getEncoded();
        System.out.println("privateKey Length :" + privateKeyRebuild.getEncoded().length + " Data:"
                + DatatypeConverter.printHexBinary(privateKeyRebuild.getEncoded()));
        byte[] publicKeyRebuildEncoded = publicKeyRebuild.getEncoded();
        System.out.println("publicKey Length  :" + publicKeyRebuild.getEncoded().length + " Data:"
                + DatatypeConverter.printHexBinary(publicKeyRebuild.getEncoded()));
        // compare the keys
        System.out.println("private Keys Equal:" + Arrays.equals(privateKeyEncoded, privateKeyRebuildEncoded));
        System.out.println("public Keys Equal :" + Arrays.equals(publicKeyEncoded, publicKeyRebuildEncoded));
        // verify the signature with rebuild public key
        Signer verifierRebuild = new Ed25519Signer();
        verifierRebuild.init(false, publicKeyRebuild);
        verifierRebuild.update(message, 0, message.length);
        boolean shouldVerifyRebuild = verifierRebuild.verifySignature(signature);
        System.out.println("signature correct :" + shouldVerifyRebuild + " with rebuild public key");
    }
}

这是控制台输出,显示了正确的重建键:

This is the console output that shows the properly rebuild keys:

ED25519 with BC
Provider          :BC Version: 1.605
privateKey Length :32 Data:F6A1F3A0B8F44EE64ACE636AFCA262F656160A728C042E3F98F9A0FD45717DE7
publicKey Length  :32 Data:858C2D6D5910B8AA7B52F7DF8E5806DAD3A7E43DC19C5A548F241BD8B82510FE
signature Length  :64 Data:4D402B0095F6692742DCACB0C2C39BFB70A5687F162DFAB3721A660D2259C96B972DF41B97502347E534FAD8D59496811CDFFFA831264ECBB1429439CF350E08
signature correct :true
Rebuild the keys and verify the signature with rebuild public key
privateKey Length :32 Data:F6A1F3A0B8F44EE64ACE636AFCA262F656160A728C042E3F98F9A0FD45717DE7
publicKey Length  :32 Data:858C2D6D5910B8AA7B52F7DF8E5806DAD3A7E43DC19C5A548F241BD8B82510FE
private Keys Equal:true
public Keys Equal :true
signature correct :true with rebuild public key

推荐答案

我还对Ed25519进行了一些研究,因此我一直在研究BouncyCastle的实现以及Tink和libsodium.对于您如何重建密钥对,我当然看不出任何问题,这似乎与BouncyCastle在其他地方使用它的方式一致:

I'm also doing some research into Ed25519 so I've been looking at the BouncyCastle implementation along with Tink and libsodium; I certainly can't see anything wrong with how you're rebuilding the key pair, it seems be consistent with how BouncyCastle is using it in other places: OpenSSHPrivateKeyUtil and Ed25519Test.

我使用 RFC8037 中的密钥对和示例签名重新编写了您的测试可以按以下方式正常工作(Java 11):

I re-wrote your test using the key pair and example signature from RFC8037, which works fine as follows (Java 11):

import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

import static org.junit.Assert.assertEquals;

public class BouncyCastleTest {

    private static final Logger LOG = LoggerFactory.getLogger(BouncyCastleTest.class);

    @Test
    public void testBouncyCastle() throws CryptoException  {
        // Test case defined in https://tools.ietf.org/html/rfc8037
        var msg = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc".getBytes(StandardCharsets.UTF_8);
        var expectedSig = "hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";

        var privateKeyBytes = Base64.getUrlDecoder().decode("nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A");
        var publicKeyBytes = Base64.getUrlDecoder().decode("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");

        var privateKey = new Ed25519PrivateKeyParameters(privateKeyBytes, 0);
        var publicKey = new Ed25519PublicKeyParameters(publicKeyBytes, 0);

        // Generate new signature
        Signer signer = new Ed25519Signer();
        signer.init(true, privateKey);
        signer.update(msg, 0, msg.length);
        byte[] signature = signer.generateSignature();
        var actualSignature = Base64.getUrlEncoder().encodeToString(signature).replace("=", "");

        LOG.info("Expected signature: {}", expectedSig);
        LOG.info("Actual signature  : {}", actualSignature);

        assertEquals(expectedSig, actualSignature);
    }
}

JCA

您还可以使用JCA实现相同的操作,在此示例中,我的密钥对采用原始"格式(即X和D坐标):

JCA

You can also achieve the same thing using JCA, in this example my keypair are in the 'raw' format (i.e. the X and D coordinates):

import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.Security;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import static org.junit.Assert.assertEquals;

public class Ed25519JCA {

    private static final Logger LOG = LoggerFactory.getLogger(Ed25519JCA.class);

    @Test
    public void testEd25519WithJCA() throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // Test case defined in https://tools.ietf.org/html/rfc8037
        var msg = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc".getBytes(StandardCharsets.UTF_8);
        var expectedSig = "hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";

        // Both formatted as 32bit raw key values (x and d)
        var privateKeyBytes = Base64.getUrlDecoder().decode("nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A");
        var publicKeyBytes = Base64.getUrlDecoder().decode("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");

        var keyFactory = KeyFactory.getInstance("Ed25519");

        // Wrap public key in ASN.1 format so we can use X509EncodedKeySpec to read it
        var pubKeyInfo = new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), publicKeyBytes);
        var x509KeySpec = new X509EncodedKeySpec(pubKeyInfo.getEncoded());

        var jcaPublicKey = keyFactory.generatePublic(x509KeySpec);

        // Wrap private key in ASN.1 format so we can use
        var privKeyInfo = new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), new DEROctetString(privateKeyBytes));
        var pkcs8KeySpec = new PKCS8EncodedKeySpec(privKeyInfo.getEncoded());

        var jcaPrivateKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // Generate new signature
        var dsa = Signature.getInstance("EdDSA"); // Edwards digital signature algorithm
        dsa.initSign(jcaPrivateKey);
        dsa.update(msg, 0, msg.length);
        byte[] signature = dsa.sign();
        var actualSignature = Base64.getUrlEncoder().encodeToString(signature).replace("=", "");

        LOG.info("Expected signature: {}", expectedSig);
        LOG.info("Actual signature  : {}", actualSignature);

        assertEquals(expectedSig, actualSignature);
    }

}

为了完整起见,您也可以首先使用JCA生成密钥对,这避免了很多格式转换:

Just for completeness, you can also generate the keypair using JCA in the first place, this avoids lots of format conversion:

    Security.addProvider(new BouncyCastleProvider());
    var keyPair = KeyPairGenerator.getInstance("Ed25519").generateKeyPair();

这篇关于用Bouncy Castle(Java)重建ED25519键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆