Java >> Java tutorial >  >> Tag >> public

Indlæser rå 64-byte lang offentlig ECDSA-nøgle i Java

Java 7 er påkrævet for EC-funktionaliteten og Java 8 til Base 64-koderen/dekoderen, ingen ekstra biblioteker - bare almindelig Java. Bemærk, at dette faktisk vil vise den offentlige nøgle som en navngiven kurve når de udskrives, vil de fleste andre løsninger ikke gøre det. Hvis du har en opdateret runtime, er dette andet svar mere rent.

Dette svar bliver svært, hvis vi gør dette ved hjælp af ECPublicKeySpec . Så lad os snyde lidt og bruge X509EncodedKeySpec i stedet:

private static byte[] P256_HEAD = Base64.getDecoder().decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");

/**
 * Converts an uncompressed secp256r1 / P-256 public point to the EC public key it is representing.
 * @param w a 64 byte uncompressed EC point consisting of just a 256-bit X and Y
 * @return an <code>ECPublicKey</code> that the point represents 
 */
public static ECPublicKey generateP256PublicKeyFromFlatW(byte[] w) throws InvalidKeySpecException {
    byte[] encodedKey = new byte[P256_HEAD.length + w.length];
    System.arraycopy(P256_HEAD, 0, encodedKey, 0, P256_HEAD.length);
    System.arraycopy(w, 0, encodedKey, P256_HEAD.length, w.length);
    KeyFactory eckf;
    try {
        eckf = KeyFactory.getInstance("EC");
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException("EC key factory not present in runtime");
    }
    X509EncodedKeySpec ecpks = new X509EncodedKeySpec(encodedKey);
    return (ECPublicKey) eckf.generatePublic(ecpks);
}

Brug:

ECPublicKey key = generateP256PublicKeyFromFlatW(w);
System.out.println(key);

Ideen bag dette er at skabe en midlertidig X509-kodet nøgle, som heldigvis ender med det offentlige punkt w i slutningen. Bytene før det indeholder ASN.1 DER-kodningen af ​​OID for den navngivne kurve og strukturelle overhead, der slutter med byte 04 angiver et ukomprimeret punkt. Her er et eksempel på, hvordan strukturen ser ud, ved at bruge værdi 1 og 2 for 32-byte X og Y.

32-byte X- og Y-værdierne af de ukomprimerede punktværdier fjernes for at oprette overskriften. Dette virker kun, fordi punktet har en statisk størrelse - dets placering for enden bestemmes kun af størrelsen af ​​kurven.

Nu er alt det nødvendige i funktionen generateP256PublicKeyFromFlatW er at tilføje det modtagne offentlige punkt w til headeren og kør den gennem dekoderen implementeret til X509EncodedKeySpec .

Ovenstående kode bruger et råt, ukomprimeret offentligt EC-punkt - kun et 32 ​​byte X og Y - uden den ukomprimerede punktindikator med værdien 04 . Det er selvfølgelig nemt også at understøtte 65 byte komprimerede punkter:

/**
 * Converts an uncompressed secp256r1 / P-256 public point to the EC public key it is representing.
 * @param w a 64 byte uncompressed EC point starting with <code>04</code>
 * @return an <code>ECPublicKey</code> that the point represents 
 */
public static ECPublicKey generateP256PublicKeyFromUncompressedW(byte[] w) throws InvalidKeySpecException {
    if (w[0] != 0x04) {
        throw new InvalidKeySpecException("w is not an uncompressed key");
    }
    return generateP256PublicKeyFromFlatW(Arrays.copyOfRange(w, 1, w.length));
}

Til sidst genererede jeg konstanten P256_HEAD hovedværdi i basis 64 ved hjælp af:

private static byte[] createHeadForNamedCurve(String name, int size)
        throws NoSuchAlgorithmException,
        InvalidAlgorithmParameterException, IOException {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
    ECGenParameterSpec m = new ECGenParameterSpec(name);
    kpg.initialize(m);
    KeyPair kp = kpg.generateKeyPair();
    byte[] encoded = kp.getPublic().getEncoded();
    return Arrays.copyOf(encoded, encoded.length - 2 * (size / Byte.SIZE));
}

kaldet af:

String name = "NIST P-256";
int size = 256;
byte[] head = createHeadForNamedCurve(name, size);
System.out.println(Base64.getEncoder().encodeToString(head));

Java gør kryptografi meget langvarig.

Proceduren for at oprette en offentlig nøgle fra et givet EC-punkt:

  1. Konstruer en ECPoint objekt fra dine givne koordinater.
  2. Konstruer en ECParameterSpec objekt fra information om din kurve.
  3. Konstruer en ECPublicKeySpec objekt fra din ECPoint og din ECParameterSpec objekt.
  4. Kald KeyFactory.generatePublic() med din ECPublicKeySpec objekt for at hente en PublicKey objekt.
  5. Cast PublicKey til en ECPublicKey efter behov.

Eksempel nedenfor:

// Setup for P-256 curve params

BigInteger p256_p = new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16);

BigInteger p256_a = new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16);
BigInteger p256_b = new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16);
byte[] p256_seed = {
                        (byte) 0xc4, (byte) 0x9d, (byte) 0x36, (byte) 0x08, 
                        (byte) 0x86, (byte) 0xe7, (byte) 0x04, (byte) 0x93, 
                        (byte) 0x6a, (byte) 0x66, (byte) 0x78, (byte) 0xe1, 
                        (byte) 0x13, (byte) 0x9d, (byte) 0x26, (byte) 0xb7, 
                        (byte) 0x81, (byte) 0x9f, (byte) 0x7e, (byte) 0x90
                    };

BigInteger p256_xg = new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16);
BigInteger p256_yg = new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16);

BigInteger p256_n = new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16);

// Construct prime field
ECFieldFp p256_field = new ECFieldFp(p256_p);

// Construct curve from parameters
EllipticCurve p256 = new EllipticCurve(p256_field, p256_a, p256_b, p256_seed);

// Construct base point for curve
ECPoint p256_base = new ECPoint(p256_xg, p256_yg);

// Construct curve parameter specifications object
ECParameterSpec p256spec = new ECParameterSpec(p256, p256_base, p256_n, 1); // Co-factor 1 for prime curves

// ------------------------------------------------------------- //

// Construct EC point from "raw" public key
ECPoint point = new ECPoint(r, s); // r, s is of type BigInteger

// Create a EC public key specification object from point and curve
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, p256spec);

// Retrieve EC KeyFactory
KeyFactory ECFactory = KeyFactory.getInstance("EC");

// Generate public key via KeyFactory
PublicKey pubKey = ECFactory.generatePublic(pubKeySpec);
ECPublicKey ECPubKey = (ECPublicKey) pubKey;

Det kan være nyttigt at generere ECParameterSpec én gang (måske i en statisk initialiseringsblok) af ydeevnemæssige årsager.

Bemærk:Der er sandsynligvis en meget enklere måde at generere ECParameterSpec-objektet på (via navngivne kurver for eksempel), men indtil videre har jeg kun fundet ECGenParameterSpec har denne funktion. Fortæl mig i kommentarerne, hvis der er en mindre smertefuld tilgang.

For at spare dig selv for smerten ved at gøre ovenstående skal du kode din EC-nøgle under X.509, som fuldt ud vil beskrive nøglen og gør indlæsningen meget nemmere.

I java, med ECPublicKey, er alt hvad du skal gøre at ringe til ECPublicKey.getEncoded() og send/gem byte-arrayet til det sted, hvor du skal bruge nøglen næste gang. Den X.509-kodede nøgle kan derefter rekonstrueres via:

// Retrieve EC KeyFactory
KeyFactory ECFactory = KeyFactory.getInstance("EC");

// Generate public key via KeyFactory
PublicKey pubKey = ECFactory.generatePublic(new X509EncodedKeySpec(data));
ECPublicKey ECPubKey = (ECPublicKey) pubKey;

hvor "data" er den kodede byte-array.


Hvad er den reneste måde at indlæse en 64 byte offentlig nøgle, så den kan bruges til at kontrollere signaturer?

Det reneste jeg kunne mønstre! Bør også fungere med andre kurver..

BEMÆRK:Begrænset til SunJCE-udbyderen eller Android API 26+ (der kan være flere udbydere med denne funktionalitet, jeg er ikke klar over dem i øjeblikket.

public static ECPublicKey rawToEncodedECPublicKey(String curveName, byte[] rawBytes) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException {
    KeyFactory kf = KeyFactory.getInstance("EC");
    byte[] x = Arrays.copyOfRange(rawBytes, 0, rawBytes.length/2);
    byte[] y = Arrays.copyOfRange(rawBytes, rawBytes.length/2, rawBytes.length);
    ECPoint w = new ECPoint(new BigInteger(1,x), new BigInteger(1,y));
    return (ECPublicKey) kf.generatePublic(new ECPublicKeySpec(w, ecParameterSpecForCurve(curveName)));
}

public static ECParameterSpec ecParameterSpecForCurve(String curveName) throws NoSuchAlgorithmException, InvalidParameterSpecException {
    AlgorithmParameters params = AlgorithmParameters.getInstance("EC");
    params.init(new ECGenParameterSpec(curveName));
    return params.getParameterSpec(ECParameterSpec.class);
}

Java tag