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
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
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
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
135 keyPairGen.initialize(keySize, new SecureRandom());
136
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 }