blob: 8cb90a640d116b30adefe0d3b655125079c3173e (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
// Read a DER-encoded PKCS#8 RSA private key, derive the public key,
// emit the Android adb_keys line on stdout.
//
// java test-rig/Keygen.java <path/to/adbkey.der>
//
// The Android pubkey blob format (from system/core/libcrypto_utils):
// uint32_t modulus_size_words; // = 64 for RSA-2048
// uint32_t n0inv; // -1 / N[0] mod 2^32
// uint8_t modulus[256]; // little-endian
// uint8_t rr[256]; // (2^2048)^2 mod N, little-endian
// uint32_t exponent; // typically 65537
// = 524 bytes, then base64-encoded, then " scrcpy-android@test".
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
public final class Keygen {
private static final int MODULUS_SIZE = 256; // bytes (2048 / 8)
private static final int MODULUS_SIZE_WORDS = MODULUS_SIZE / 4;
private static final int ENCODED_SIZE = 3 * 4 + 2 * MODULUS_SIZE;
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("usage: java Keygen.java <der private key path>");
System.exit(2);
}
byte[] der = Files.readAllBytes(Path.of(args[0]));
PrivateKey priv = KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(der));
RSAPublicKey pub = derivePublic(priv);
byte[] blob = encode(pub);
String b64 = Base64.getEncoder().encodeToString(blob);
System.out.print(b64);
System.out.println(" scrcpy-android@test");
}
private static RSAPublicKey derivePublic(PrivateKey priv) throws Exception {
if (priv instanceof RSAPrivateCrtKey crt) {
RSAPublicKeySpec spec = new RSAPublicKeySpec(crt.getModulus(), crt.getPublicExponent());
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
}
throw new IOException("private key is not an RSAPrivateCrtKey");
}
private static byte[] encode(RSAPublicKey pub) {
ByteBuffer buf = ByteBuffer.allocate(ENCODED_SIZE).order(ByteOrder.LITTLE_ENDIAN);
BigInteger n = pub.getModulus();
buf.putInt(MODULUS_SIZE_WORDS);
// n0inv = (2^32) - (N[0] mod 2^32)^-1 mod 2^32
BigInteger r32 = BigInteger.ZERO.setBit(32);
BigInteger n0inv = n.mod(r32).modInverse(r32);
n0inv = r32.subtract(n0inv);
buf.putInt(n0inv.intValue());
buf.put(beToLe(MODULUS_SIZE, n));
// rr = (2^(MODULUS_SIZE*8))^2 mod N
BigInteger rr = BigInteger.ZERO.setBit(MODULUS_SIZE * 8).modPow(BigInteger.TWO, n);
buf.put(beToLe(MODULUS_SIZE, rr));
buf.putInt(pub.getPublicExponent().intValue());
return buf.array();
}
private static byte[] beToLe(int len, BigInteger v) {
byte[] be = v.toByteArray();
// strip leading sign byte if present
int start = 0;
if (be.length > len) start = be.length - len;
byte[] out = new byte[len];
for (int i = 0; i < len; i++) {
int srcIdx = be.length - 1 - i;
if (srcIdx >= start) out[i] = be[srcIdx];
}
return out;
}
}
|