1 package cn.home1.oss.lib.common.crypto;
2
3 import static cn.home1.oss.lib.common.StringUtils.dropComment;
4 import static cn.home1.oss.lib.common.crypto.CryptoConstants.ALGO_RSA;
5 import static cn.home1.oss.lib.common.crypto.CryptoConstants.COLON;
6 import static cn.home1.oss.lib.common.crypto.CryptoConstants.UNDERSCORE;
7 import static com.google.common.base.Preconditions.checkArgument;
8 import static java.lang.Integer.parseInt;
9
10 import com.google.common.collect.ImmutableSet;
11
12 import lombok.Getter;
13 import lombok.SneakyThrows;
14 import lombok.extern.slf4j.Slf4j;
15
16 import org.apache.commons.codec.binary.Hex;
17 import org.bouncycastle.asn1.ASN1Primitive;
18 import org.bouncycastle.asn1.ASN1Sequence;
19
20 import java.security.KeyFactory;
21 import java.security.interfaces.RSAPrivateKey;
22 import java.security.interfaces.RSAPublicKey;
23 import java.security.spec.PKCS8EncodedKeySpec;
24 import java.security.spec.RSAPrivateKeySpec;
25 import java.security.spec.X509EncodedKeySpec;
26 import java.util.Collection;
27
28 import cn.home1.oss.lib.common.CodecUtils;
29
30
31
32
33 @Slf4j
34 public class RsaKey {
35
36 public static final char COMMENT_MARK = '-';
37 public static final String KEY_FORMAT_PKCS1 = "PKCS1";
38 public static final String KEY_FORMAT_PKCS8 = "PKCS8";
39 public static final String KEY_FORMAT_X509 = "X509";
40 public static final String KEY_FORMAT_PKCS1_PKCS1 = KEY_FORMAT_PKCS1 + "_" + KEY_FORMAT_PKCS1;
41 public static final String KEY_FORMAT_PKCS1_X509 = KEY_FORMAT_PKCS1 + "_" + KEY_FORMAT_X509;
42 public static final String KEY_FORMAT_PKCS8_X509 = KEY_FORMAT_PKCS8 + "_" + KEY_FORMAT_X509;
43
44 public static final String KEY_TYPE_PAIR = "PAIR";
45 public static final String KEY_TYPE_PRIVATE = "PRIV";
46 public static final String KEY_TYPE_PUBLIC = "PUB";
47
48 static final Collection<String> SUPPORTED_PAIR_FORMATS = ImmutableSet.of(
49 KEY_FORMAT_PKCS1_PKCS1,
50 KEY_FORMAT_PKCS1_X509,
51 KEY_FORMAT_PKCS8_X509
52 );
53
54 @Getter
55 private final KeyExpression keyExpression;
56 @Getter
57 private final RSAPublicKey rsaPublicKey;
58 @Getter
59 private final RSAPrivateKey rsaPrivateKey;
60 @Getter
61 private final String keyFormat;
62 @Getter
63 private final int keySize;
64 @Getter
65 private final String keyType;
66 @Getter
67 private final String privateKeyFormat;
68 @Getter
69 private final String publicKeyFormat;
70
71 public RsaKey(final KeyExpression keyExpression) {
72 this.keyExpression = keyExpression;
73
74 final String spec = keyExpression.getSpec();
75 final String value = keyExpression.getValue();
76
77 this.keyFormat = RsaKey.keyFormat(spec);
78 this.keySize = RsaKey.keySize(spec);
79 this.keyType = RsaKey.keyType(spec);
80
81 if (KEY_TYPE_PAIR.equals(this.keyType)) {
82 checkArgument(SUPPORTED_PAIR_FORMATS.contains(this.keyFormat), "unsupported keyFormat " + this.keyFormat);
83 this.privateKeyFormat = this.keyFormat.split(UNDERSCORE)[0];
84 this.publicKeyFormat = this.keyFormat.split(UNDERSCORE)[1];
85 this.rsaPrivateKey = RsaKey.privateKey(RsaKey.extractPrivateKey(this.keyExpression), this.privateKeyFormat);
86 this.rsaPublicKey = RsaKey.publicKey(RsaKey.extractPublicKey(this.keyExpression), this.publicKeyFormat);
87 } else if (KEY_TYPE_PRIVATE.equals(this.keyType)) {
88 this.privateKeyFormat = this.keyFormat;
89 this.publicKeyFormat = null;
90 this.rsaPrivateKey = RsaKey.privateKey(value, this.keyFormat);
91 this.rsaPublicKey = null;
92 } else if (KEY_TYPE_PUBLIC.equals(this.keyType)) {
93 this.privateKeyFormat = null;
94 this.publicKeyFormat = this.keyFormat;
95 this.rsaPrivateKey = null;
96 this.rsaPublicKey = RsaKey.publicKey(value, this.keyFormat);
97 } else {
98 throw new IllegalArgumentException("unsupported keyType " + this.keyType);
99 }
100 }
101
102 public RsaKey(final String keyExpression) {
103 this(new KeyExpression(keyExpression));
104 }
105
106 public String getEncodedRsaPrivateKey() {
107 return CodecUtils.encodeBase64(this.getRsaPrivateKey().getEncoded());
108 }
109
110 public String getEncodedRsaPublicKey() {
111 return CodecUtils.encodeBase64(this.getRsaPublicKey().getEncoded());
112 }
113
114 public KeyExpression getPrivateKey() {
115 final KeyExpression result;
116 if (KEY_TYPE_PAIR.equals(this.keyType)) {
117 final String spec = RsaKey.keySpec(this.privateKeyFormat, this.keySize, KEY_TYPE_PRIVATE);
118 final String value = RsaKey.extractPrivateKey(this.keyExpression);
119 result = new KeyExpression(spec, value);
120 } else if (KEY_TYPE_PRIVATE.equals(this.keyType)) {
121 result = this.keyExpression;
122 } else {
123 throw new UnsupportedOperationException("no privateKey present");
124 }
125 return result;
126 }
127
128 public KeyExpression getPublicKey() {
129 final KeyExpression result;
130 if (KEY_TYPE_PAIR.equals(this.keyType)) {
131 final String spec = RsaKey.keySpec(this.publicKeyFormat, this.keySize, KEY_TYPE_PUBLIC);
132 final String value = RsaKey.extractPublicKey(this.keyExpression);
133 result = new KeyExpression(spec, value);
134 } else if (KEY_TYPE_PUBLIC.equals(this.keyType)) {
135 result = this.keyExpression;
136 } else {
137 throw new UnsupportedOperationException("no publicKey present");
138 }
139 return result;
140 }
141
142 public KeyExpression getKey(final String spec) {
143 final String keyFormat = RsaKey.keyFormat(spec);
144 final int keySize = RsaKey.keySize(spec);
145 final String keyType = RsaKey.keyType(spec);
146 checkArgument(this.keySize == keySize, "keySize not match " + this.keySize);
147
148 final KeyExpression result;
149 if (KEY_TYPE_PAIR.equals(keyType)) {
150 checkArgument(KEY_TYPE_PAIR.equals(this.keyType), "no keyPair present");
151 checkArgument(keyFormat.equals(this.keyFormat), "keyFormat not match " + this.keyFormat);
152 result = new KeyExpression(spec, this.keyExpression.getValue());
153 } else if (KEY_TYPE_PRIVATE.equals(keyType)) {
154 checkArgument(keyFormat.equals(this.privateKeyFormat), "keyFormat not match " + this.privateKeyFormat);
155 result = this.getPrivateKey();
156 } else if (KEY_TYPE_PUBLIC.equals(keyType)) {
157 checkArgument(keyFormat.equals(this.publicKeyFormat), "keyFormat not match " + this.publicKeyFormat);
158 result = this.getPublicKey();
159 } else {
160 throw new IllegalArgumentException("unsupported keyType " + keyType);
161 }
162 return result;
163 }
164
165 @Override
166 public String toString() {
167 return this.keyExpression.toString();
168 }
169
170 public static String extractPrivateKey(final KeyExpression keyExpression) {
171 return keyExpression.getValue().substring(0, keyExpression.getValue().indexOf(COLON));
172 }
173
174 public static String extractPublicKey(final KeyExpression keyExpression) {
175 return keyExpression.getValue().substring(keyExpression.getValue().indexOf(COLON) + 1);
176 }
177
178 public static String keyFormat(final String spec) {
179 final int index = spec.indexOf(UNDERSCORE, spec.indexOf(UNDERSCORE) + 1) + 1;
180 return spec.substring(index);
181 }
182
183 public static int keySize(final String spec) {
184 final String text = spec.split(UNDERSCORE)[0].substring(3);
185 return parseInt(text);
186 }
187
188 public static String keySpec(final String keyFormat, final int keySize, final String keyType) {
189 return ALGO_RSA + keySize + UNDERSCORE + keyType.toUpperCase() + UNDERSCORE + keyFormat.toUpperCase();
190 }
191
192 public static String keyType(final String spec) {
193 return spec.split(UNDERSCORE)[1];
194 }
195
196 public static RSAPrivateKey privateKey(final String key, final String keyFormat) {
197 final RSAPrivateKey result;
198 if (KEY_FORMAT_PKCS1.equals(keyFormat)) {
199 result = RsaKey.privateKeyPkcs1(key);
200 } else if (KEY_FORMAT_PKCS8.equals(keyFormat)) {
201 result = RsaKey.privateKeyPkcs8(key);
202 } else {
203 throw new IllegalArgumentException("unsupported keyFormat " + keyFormat);
204 }
205 return result;
206 }
207
208 public static RSAPublicKey publicKey(final String key, final String keyFormat) {
209 checkArgument(KEY_FORMAT_X509.equals(keyFormat), "unsupported keyFormat " + keyFormat);
210 return RsaKey.publicKeyX509(key);
211 }
212
213 @SneakyThrows
214 public static RSAPrivateKey privateKeyPkcs1(final String pkcs1) {
215 final String withoutComment = dropComment(pkcs1, COMMENT_MARK);
216 final byte[] raw = CodecUtils.decodeBase64(withoutComment);
217
218 if (log.isDebugEnabled()) {
219 log.debug("withoutComment: {}", withoutComment);
220 log.debug("raw: {}", Hex.encodeHexString(raw));
221 }
222
223 final ASN1Primitive asn1Primitive = ASN1Sequence.fromByteArray(raw);
224
225 final org.bouncycastle.asn1.pkcs.RSAPrivateKey asn1 =
226 org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(asn1Primitive);
227 final RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(asn1.getModulus(), asn1.getPrivateExponent());
228 return (RSAPrivateKey) KeyFactory.getInstance(ALGO_RSA).generatePrivate(rsaPrivateKeySpec);
229 }
230
231 @SneakyThrows
232 public static RSAPrivateKey privateKeyPkcs8(final String pkcs8) {
233 final byte[] raw = CodecUtils.decodeBase64(dropComment(pkcs8, COMMENT_MARK));
234 return (RSAPrivateKey) KeyFactory.getInstance(ALGO_RSA).generatePrivate(new PKCS8EncodedKeySpec(raw));
235 }
236
237 @SneakyThrows
238 public static RSAPublicKey publicKeyX509(final String x509) {
239 final byte[] raw = CodecUtils.decodeBase64(dropComment(x509, COMMENT_MARK));
240 return (RSAPublicKey) KeyFactory.getInstance(ALGO_RSA).generatePublic(new X509EncodedKeySpec(raw));
241 }
242 }