简体   繁体   中英

How to generate PublicKey for PrivateKey in X25519?

I'm working with X25519-keys based encryption at the moment.

My question is, basically, how to derive PublicKey from existing X25519 PrivateKey ?

I've tried the following:

    protected PublicKey generatePublicKeyFromPrivate(PrivateKey privateKey) throws GeneralSecurityException {
        PublicKey basePublicKey = generatePublicKey(BigInteger.valueOf(9));
        KeyAgreement keyAgreement = KeyAgreement.getInstance(X25519);
        keyAgreement.init(privateKey, new ECGenParameterSpec(X25519));
        keyAgreement.doPhase(basePublicKey, true);
        byte[] bytes = keyAgreement.generateSecret();
        return generatePublicKey(new BigInteger(bytes));
    }

PublicKey is successfully generated. And I even compared this approach with the third-party library approach ( Google Tink ): generated PubliKeys match.

However, when I try to use Java's KeyPairGenerator to get both PrivateKey and PubliKey at once and then try to generate a PublicKey for that PrivateKey myself, they differ.

    public KeyPair generateX25519KeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(X25519);
        return keyPairGenerator.generateKeyPair();
    }

I decided to look up the source code of the KeyPairGenerator ( XDHKeyPairGenerator implementation) and found the part when they do multiplication:

    /**
     *
     * Multiply an encoded scalar with a point as a BigInteger and return an
     * encoded point. The array k holding the scalar will be pruned by
     * modifying it in place.
     *
     * @param k an encoded scalar
     * @param u the u-coordinate of a point as a BigInteger
     * @return the encoded product
     */
    public byte[] encodedPointMultiply(byte[] k, BigInteger u) {
        pruneK(k);
        ImmutableIntegerModuloP elemU = field.getElement(u);
        return pointMultiply(k, elemU).asByteArray(params.getBytes());
    }

However, the method that is used to separately generate PubliKey uses another multiplication:

    /**
     * Compute a public key from an encoded private key. This method will
     * modify the supplied array in order to prune it.
     */
    public BigInteger computePublic(byte[] k) {
        pruneK(k);
        return pointMultiply(k, this.basePoint).asBigInteger();
    }

As you can see, the difference is that in the first case they apply the asByteArray() method to the result:

     * Returns the little-endian encoding of this' % 2^(8 * len), where this'
     * is the canonical integer value equivalent to this.
     *
     * @param len the length of the desired array
     * @return a byte array of length len containing the result
     */
    default byte[] asByteArray(int len) {
        byte[] result = new byte[len];
        asByteArray(result);
        return result;
    }

So my question really is: why do they do so? Why this "little-endian encoding" is applied to the PublicKey when the KeyPairGenerator is being used, but it's not applied when the PublicKey is derived from the PrivateKey separately.

The public key is computed by multiplying the base point by the "clamped" secret scalar and returning the encoded X coordinate.

Clamping means masking the 3 least significant bits, masking the top bit, and setting the second top bit.

See RFC7748 for details.

But looks like this is more of a programming question, and the actual issue may not have anything to do with X25519. For a given secret, compare the output you get from that code with the same operation implemented with a different library. If the public keys match, the issue is somewhere else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM