-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Keys.java
163 lines (134 loc) · 6.08 KB
/
Keys.java
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
* Copyright 2019 Web3 Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.web3j.crypto;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.web3j.utils.Numeric;
import org.web3j.utils.Strings;
import static org.web3j.crypto.SecureRandomUtils.secureRandom;
/** Crypto key utilities. */
public class Keys {
static final int PRIVATE_KEY_SIZE = 32;
static final int PUBLIC_KEY_SIZE = 64;
public static final int ADDRESS_SIZE = 160;
public static final int ADDRESS_LENGTH_IN_HEX = ADDRESS_SIZE >> 2;
static final int PUBLIC_KEY_LENGTH_IN_HEX = PUBLIC_KEY_SIZE << 1;
public static final int PRIVATE_KEY_LENGTH_IN_HEX = PRIVATE_KEY_SIZE << 1;
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
private Keys() {}
/**
* Create a keypair using SECP-256k1 curve.
*
* <p>Private keypairs are encoded using PKCS8
*
* <p>Private keys are encoded using X.509
*/
static KeyPair createSecp256k1KeyPair()
throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
return createSecp256k1KeyPair(secureRandom());
}
static KeyPair createSecp256k1KeyPair(SecureRandom random)
throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", "BC");
ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("secp256k1");
if (random != null) {
keyPairGenerator.initialize(ecGenParameterSpec, random);
} else {
keyPairGenerator.initialize(ecGenParameterSpec);
}
return keyPairGenerator.generateKeyPair();
}
public static ECKeyPair createEcKeyPair()
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException {
return createEcKeyPair(secureRandom());
}
public static ECKeyPair createEcKeyPair(SecureRandom random)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException {
KeyPair keyPair = createSecp256k1KeyPair(random);
return ECKeyPair.create(keyPair);
}
public static String getAddress(ECKeyPair ecKeyPair) {
return getAddress(ecKeyPair.getPublicKey());
}
public static String getAddress(BigInteger publicKey) {
return getAddress(
Numeric.toHexStringWithPrefixZeroPadded(publicKey, PUBLIC_KEY_LENGTH_IN_HEX));
}
public static String getAddress(String publicKey) {
String publicKeyNoPrefix = Numeric.cleanHexPrefix(publicKey);
if (publicKeyNoPrefix.length() < PUBLIC_KEY_LENGTH_IN_HEX) {
publicKeyNoPrefix =
Strings.zeros(PUBLIC_KEY_LENGTH_IN_HEX - publicKeyNoPrefix.length())
+ publicKeyNoPrefix;
}
String hash = Hash.sha3(publicKeyNoPrefix);
return hash.substring(hash.length() - ADDRESS_LENGTH_IN_HEX); // right most 160 bits
}
public static byte[] getAddress(byte[] publicKey) {
byte[] hash = Hash.sha3(publicKey);
return Arrays.copyOfRange(hash, hash.length - 20, hash.length); // right most 160 bits
}
/**
* Checksum address encoding as per <a
* href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md">EIP-55</a>.
*
* @param address a valid hex encoded address
* @return hex encoded checksum address
*/
public static String toChecksumAddress(String address) {
String lowercaseAddress = Numeric.cleanHexPrefix(address).toLowerCase();
String addressHash = Numeric.cleanHexPrefix(Hash.sha3String(lowercaseAddress));
StringBuilder result = new StringBuilder(lowercaseAddress.length() + 2);
result.append("0x");
for (int i = 0; i < lowercaseAddress.length(); i++) {
if (Integer.parseInt(String.valueOf(addressHash.charAt(i)), 16) >= 8) {
result.append(String.valueOf(lowercaseAddress.charAt(i)).toUpperCase());
} else {
result.append(lowercaseAddress.charAt(i));
}
}
return result.toString();
}
public static byte[] serialize(ECKeyPair ecKeyPair) {
byte[] privateKey = Numeric.toBytesPadded(ecKeyPair.getPrivateKey(), PRIVATE_KEY_SIZE);
byte[] publicKey = Numeric.toBytesPadded(ecKeyPair.getPublicKey(), PUBLIC_KEY_SIZE);
byte[] result = Arrays.copyOf(privateKey, PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE);
System.arraycopy(publicKey, 0, result, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE);
return result;
}
public static ECKeyPair deserialize(byte[] input) {
if (input.length != PRIVATE_KEY_SIZE + PUBLIC_KEY_SIZE) {
throw new RuntimeException("Invalid input key size");
}
BigInteger privateKey = Numeric.toBigInt(input, 0, PRIVATE_KEY_SIZE);
BigInteger publicKey = Numeric.toBigInt(input, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE);
return new ECKeyPair(privateKey, publicKey);
}
}