View Javadoc
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   * Created by zhanghaolun on 16/11/17.
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     // final RSAPrivateKeyStructure asn1 = new RSAPrivateKeyStructure((ASN1Sequence) asn1Primitive);
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 }