View Javadoc
1   package cn.home1.oss.lib.common.crypto;
2   
3   import static com.google.common.base.Preconditions.checkArgument;
4   
5   import com.google.common.base.Preconditions;
6   
7   import lombok.Getter;
8   import lombok.SneakyThrows;
9   import lombok.extern.slf4j.Slf4j;
10  
11  import org.bouncycastle.asn1.ASN1Encodable;
12  import org.bouncycastle.asn1.ASN1Primitive;
13  import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
14  import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
15  import org.bouncycastle.util.io.pem.PemObject;
16  import org.bouncycastle.util.io.pem.PemWriter;
17  
18  import java.io.StringWriter;
19  import java.security.KeyPair;
20  import java.security.KeyPairGenerator;
21  import java.security.Provider;
22  import java.security.SecureRandom;
23  
24  import cn.home1.oss.lib.common.CodecUtils;
25  import cn.home1.oss.lib.common.StringUtils;
26  
27  /**
28   * Created by zhanghaolun on 16/11/4.
29   */
30  @Slf4j
31  public class RsaKeyGenerator implements KeyGenerator {
32  
33    private final Provider provider;
34    @Getter
35    private final String spec;
36    @Getter
37    private final String keyFormat;
38    @Getter
39    private final int keySize;
40    @Getter
41    private final String keyType;
42    private RsaKey key;
43  
44    public RsaKeyGenerator(final String spec) {
45      this.provider = Cryptos.provider();
46      this.spec = spec;
47      this.keyFormat = RsaKey.keyFormat(spec);
48      this.keySize = RsaKey.keySize(spec);
49      this.keyType = RsaKey.keyType(spec);
50      Preconditions.checkArgument(RsaKey.SUPPORTED_PAIR_FORMATS.contains(this.keyFormat), "keyFormat " + this.keyFormat + " not supported.");
51      Preconditions.checkArgument(RsaKey.KEY_TYPE_PAIR.equals(this.keyType), "keyType " + this.keyType + " not supported.");
52    }
53  
54    @Override
55    public KeyExpression generateKey() {
56      final KeyExpression keyExpression = RsaKeyGenerator.generateRsaKeyPair( //
57        this.provider, this.keyFormat, this.keySize);
58      this.key = new RsaKey(keyExpression);
59      return this.key.getKeyExpression();
60    }
61  
62    @Override
63    public KeyExpression getKey(final String spec) {
64      if (this.key == null) {
65        this.generateKey();
66      }
67  
68      return this.key.getKey(spec);
69    }
70  
71    public static KeyExpression convertPairFromPkcs8X509ToPkcs1(final KeyExpression pairPkcs8X509) {
72      checkArgument(RsaKey.KEY_FORMAT_PKCS8_X509.equals(RsaKey.keyFormat(pairPkcs8X509.getSpec())), //
73        "unsupported spec" + pairPkcs8X509.getSpec());
74      final byte[] privateKeyPkcs8 = CodecUtils.decodeBase64(RsaKey.extractPrivateKey(pairPkcs8X509));
75      final byte[] publicKeyX509 = CodecUtils.decodeBase64(RsaKey.extractPublicKey(pairPkcs8X509));
76      final String privateKeyPem = convertPrivateKeyFromPkcs8ToPkcs1Pem(privateKeyPkcs8);
77      final String publicKeyPem = convertPublicKeyFromX509ToPkcs1Pem(publicKeyX509);
78  
79      final int keySize = RsaKey.keySize(pairPkcs8X509.getSpec());
80      final String spec = RsaKey.keySpec(RsaKey.KEY_FORMAT_PKCS1 + CryptoConstants.UNDERSCORE + RsaKey.KEY_FORMAT_PKCS1, keySize, RsaKey.KEY_TYPE_PAIR);
81      final String value = StringUtils.dropComment(privateKeyPem, RsaKey.COMMENT_MARK) + CryptoConstants.COLON + StringUtils.dropComment(publicKeyPem, RsaKey.COMMENT_MARK);
82      return new KeyExpression(spec, value);
83    }
84  
85    @SneakyThrows
86    public static String convertPrivateKeyFromPkcs8ToPkcs1Pem(final byte[] privateKeyPkcs8) {
87      // Convert private key from PKCS8 to PKCS1:
88      final ASN1Encodable encodable = PrivateKeyInfo.getInstance(privateKeyPkcs8).parsePrivateKey();
89      return pem(encodable.toASN1Primitive().getEncoded(), RsaKey.KEY_FORMAT_PKCS1, RsaKey.KEY_TYPE_PRIVATE);
90    }
91  
92    @SneakyThrows
93    public static String convertPublicKeyFromX509ToPkcs1Pem(final byte[] publicKeyX509) {
94      // Convert public key from X.509 SubjectPublicKeyInfo to PKCS1:
95      final ASN1Primitive publicKeyPrimitive = SubjectPublicKeyInfo.getInstance(publicKeyX509).parsePublicKey();
96      return pem(publicKeyPrimitive.getEncoded(), RsaKey.KEY_FORMAT_PKCS1, RsaKey.KEY_TYPE_PUBLIC);
97    }
98  
99    public static KeyExpression generateRsaKeyPair( //
100     final Provider provider, final String keyFormat, final int keySize //
101   ) {
102     final String privateKeyFormat = keyFormat.split(CryptoConstants.UNDERSCORE)[0];
103     final String publicKeyFormat = keyFormat.split(CryptoConstants.UNDERSCORE)[1];
104 
105     final KeyExpression pairPkcs8X509 = generateRsaKeyPairPkcs8X509(provider, keySize);
106     final KeyExpression pairPkcs1 = convertPairFromPkcs8X509ToPkcs1(pairPkcs8X509);
107 
108     final String privateKey;
109     if (RsaKey.KEY_FORMAT_PKCS8.equals(privateKeyFormat)) {
110       privateKey = RsaKey.extractPrivateKey(pairPkcs8X509);
111     } else if (RsaKey.KEY_FORMAT_PKCS1.equals(privateKeyFormat)) {
112       privateKey = RsaKey.extractPrivateKey(pairPkcs1);
113     } else {
114       throw new IllegalArgumentException("unsupported privateKeyFormat " + privateKeyFormat);
115     }
116 
117     final String publicKey;
118     if (RsaKey.KEY_FORMAT_X509.equals(publicKeyFormat)) {
119       publicKey = RsaKey.extractPublicKey(pairPkcs8X509);
120     } else if (RsaKey.KEY_FORMAT_PKCS1.equals(publicKeyFormat)) {
121       publicKey = RsaKey.extractPublicKey(pairPkcs1);
122     } else {
123       throw new IllegalArgumentException("unsupported publicKeyFormat " + publicKeyFormat);
124     }
125 
126     final String spec = RsaKey.keySpec(privateKeyFormat + CryptoConstants.UNDERSCORE + publicKeyFormat, keySize, RsaKey.KEY_TYPE_PAIR);
127     final String value = privateKey + CryptoConstants.COLON + publicKey;
128     return new KeyExpression(spec, value);
129   }
130 
131   @SneakyThrows
132   public static KeyPair generateRsaKeyPair(final Provider provider, final int keySize) {
133     final KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(CryptoConstants.ALGO_RSA, provider);
134     // final byte[] seed = DateFormatUtils.format(currentTimeMillis(), "yyyyMMdd").getBytes();
135     keyPairGen.initialize(keySize, new SecureRandom());
136     //keygen.initialize(keySize);
137     return keyPairGen.generateKeyPair();
138   }
139 
140   @SneakyThrows
141   public static KeyExpression generateRsaKeyPairPkcs8X509(
142     final Provider provider, //
143     final int keySize //
144   ) {
145     final KeyPair pairPkcs8X509 = generateRsaKeyPair(provider, keySize);
146     final byte[] privateKeyPkcs8 = pairPkcs8X509.getPrivate().getEncoded();
147     final byte[] publicKeyX509 = pairPkcs8X509.getPublic().getEncoded();
148 
149     final String privateKeyPem = pem(privateKeyPkcs8, RsaKey.KEY_FORMAT_PKCS8, RsaKey.KEY_TYPE_PRIVATE);
150     final String publicKeyPem = pem(publicKeyX509, RsaKey.KEY_FORMAT_X509, RsaKey.KEY_TYPE_PUBLIC);
151 
152     final String spec = RsaKey.keySpec(RsaKey.KEY_FORMAT_PKCS8 + CryptoConstants.UNDERSCORE + RsaKey.KEY_FORMAT_X509, keySize, RsaKey.KEY_TYPE_PAIR);
153     final String value = StringUtils.dropComment(privateKeyPem, RsaKey.COMMENT_MARK) + CryptoConstants.COLON + StringUtils.dropComment(publicKeyPem, RsaKey.COMMENT_MARK);
154     return new KeyExpression(spec, value);
155   }
156 
157   @SneakyThrows
158   public static String pem(final byte[] bytes, final String keyFormat, final String keyType) {
159     final String type;
160     if (RsaKey.KEY_TYPE_PRIVATE.equals(keyType)) {
161       type = "RSA PRIVATE KEY";
162     } else if (RsaKey.KEY_TYPE_PUBLIC.equals(keyType)) {
163       if (RsaKey.KEY_FORMAT_X509.equals(keyFormat)) {
164         type = "PUBLIC KEY";
165       } else {
166         type = "RSA PUBLIC KEY";
167       }
168     } else {
169       throw new IllegalArgumentException("unsupported keyType " + keyType);
170     }
171 
172     final StringWriter stringWriter = new StringWriter();
173     try (final PemWriter pemWriter = new PemWriter(stringWriter)) {
174       pemWriter.writeObject(new PemObject(type, bytes));
175     }
176     return stringWriter.toString();
177   }
178 }