1 package org.bouncycastle.openssl; 2 3 import java.io.IOException; 4 import java.math.BigInteger; 5 import java.security.Key; 6 import java.security.KeyPair; 7 import java.security.NoSuchProviderException; 8 import java.security.PrivateKey; 9 import java.security.Provider; 10 import java.security.PublicKey; 11 import java.security.SecureRandom; 12 import java.security.Security; 13 import java.security.cert.CRLException; 14 import java.security.cert.CertificateEncodingException; 15 import java.security.cert.X509CRL; 16 import java.security.cert.X509Certificate; 17 import java.security.interfaces.DSAParams; 18 import java.security.interfaces.DSAPrivateKey; 19 import java.security.interfaces.RSAPrivateCrtKey; 20 import java.security.interfaces.RSAPrivateKey; 21 import java.util.ArrayList; 22 import java.util.List; 23 24 import org.bouncycastle.asn1.ASN1EncodableVector; 25 import org.bouncycastle.asn1.ASN1Object; 26 import org.bouncycastle.asn1.ASN1Sequence; 27 import org.bouncycastle.asn1.DERInteger; 28 import org.bouncycastle.asn1.DERSequence; 29 import org.bouncycastle.asn1.cms.ContentInfo; 30 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 31 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure; 32 import org.bouncycastle.asn1.x509.DSAParameter; 33 import org.bouncycastle.jce.PKCS10CertificationRequest; 34 import org.bouncycastle.util.Strings; 35 import org.bouncycastle.util.encoders.Hex; 36 import org.bouncycastle.util.io.pem.PemGenerationException; 37 import org.bouncycastle.util.io.pem.PemHeader; 38 import org.bouncycastle.util.io.pem.PemObject; 39 import org.bouncycastle.util.io.pem.PemObjectGenerator; 40 import org.bouncycastle.x509.X509AttributeCertificate; 41 import org.bouncycastle.x509.X509V2AttributeCertificate; 42 43 /** 44 * PEM generator for the original set of PEM objects used in Open SSL. 45 */ 46 public class MiscPEMGenerator 47 implements PemObjectGenerator 48 { 49 private Object obj; 50 private String algorithm; 51 private char[] password; 52 private SecureRandom random; 53 private Provider provider; 54 55 public MiscPEMGenerator(Object o) 56 { 57 this.obj = o; 58 } 59 60 public MiscPEMGenerator( 61 Object obj, 62 String algorithm, 63 char[] password, 64 SecureRandom random, 65 Provider provider) 66 { 67 this.obj = obj; 68 this.algorithm = algorithm; 69 this.password = password; 70 this.random = random; 71 this.provider = provider; 72 } 73 74 public MiscPEMGenerator( 75 Object obj, 76 String algorithm, 77 char[] password, 78 SecureRandom random, 79 String provider) 80 throws NoSuchProviderException 81 { 82 this.obj = obj; 83 this.algorithm = algorithm; 84 this.password = password; 85 this.random = random; 86 87 if (provider != null) 88 { 89 this.provider = Security.getProvider(provider); 90 if (this.provider == null) 91 { 92 throw new NoSuchProviderException("cannot find provider: " + provider); 93 } 94 } 95 } 96 97 private PemObject createPemObject(Object o) 98 throws IOException 99 { 100 String type; 101 byte[] encoding; 102 103 if (o instanceof PemObject) 104 { 105 return (PemObject)o; 106 } 107 if (o instanceof PemObjectGenerator) 108 { 109 return ((PemObjectGenerator)o).generate(); 110 } 111 if (o instanceof X509Certificate) 112 { 113 type = "CERTIFICATE"; 114 try 115 { 116 encoding = ((X509Certificate)o).getEncoded(); 117 } 118 catch (CertificateEncodingException e) 119 { 120 throw new PemGenerationException("Cannot encode object: " + e.toString()); 121 } 122 } 123 else if (o instanceof X509CRL) 124 { 125 type = "X509 CRL"; 126 try 127 { 128 encoding = ((X509CRL)o).getEncoded(); 129 } 130 catch (CRLException e) 131 { 132 throw new PemGenerationException("Cannot encode object: " + e.toString()); 133 } 134 } 135 else if (o instanceof KeyPair) 136 { 137 return createPemObject(((KeyPair)o).getPrivate()); 138 } 139 else if (o instanceof PrivateKey) 140 { 141 PrivateKeyInfo info = new PrivateKeyInfo( 142 (ASN1Sequence) ASN1Object.fromByteArray(((Key)o).getEncoded())); 143 144 if (o instanceof RSAPrivateKey) 145 { 146 type = "RSA PRIVATE KEY"; 147 148 encoding = info.getPrivateKey().getEncoded(); 149 } 150 else if (o instanceof DSAPrivateKey) 151 { 152 type = "DSA PRIVATE KEY"; 153 154 DSAParameter p = DSAParameter.getInstance(info.getAlgorithmId().getParameters()); 155 ASN1EncodableVector v = new ASN1EncodableVector(); 156 157 v.add(new DERInteger(0)); 158 v.add(new DERInteger(p.getP())); 159 v.add(new DERInteger(p.getQ())); 160 v.add(new DERInteger(p.getG())); 161 162 BigInteger x = ((DSAPrivateKey)o).getX(); 163 BigInteger y = p.getG().modPow(x, p.getP()); 164 165 v.add(new DERInteger(y)); 166 v.add(new DERInteger(x)); 167 168 encoding = new DERSequence(v).getEncoded(); 169 } 170 else if (((PrivateKey)o).getAlgorithm().equals("ECDSA")) 171 { 172 type = "EC PRIVATE KEY"; 173 174 encoding = info.getPrivateKey().getEncoded(); 175 } 176 else 177 { 178 throw new IOException("Cannot identify private key"); 179 } 180 } 181 else if (o instanceof PublicKey) 182 { 183 type = "PUBLIC KEY"; 184 185 encoding = ((PublicKey)o).getEncoded(); 186 } 187 else if (o instanceof X509AttributeCertificate) 188 { 189 type = "ATTRIBUTE CERTIFICATE"; 190 encoding = ((X509V2AttributeCertificate)o).getEncoded(); 191 } 192 else if (o instanceof PKCS10CertificationRequest) 193 { 194 type = "CERTIFICATE REQUEST"; 195 encoding = ((PKCS10CertificationRequest)o).getEncoded(); 196 } 197 else if (o instanceof ContentInfo) 198 { 199 type = "PKCS7"; 200 encoding = ((ContentInfo)o).getEncoded(); 201 } 202 else 203 { 204 throw new PemGenerationException("unknown object passed - can't encode."); 205 } 206 207 return new PemObject(type, encoding); 208 } 209 210 private String getHexEncoded(byte[] bytes) 211 throws IOException 212 { 213 bytes = Hex.encode(bytes); 214 215 char[] chars = new char[bytes.length]; 216 217 for (int i = 0; i != bytes.length; i++) 218 { 219 chars[i] = (char)bytes[i]; 220 } 221 222 return new String(chars); 223 } 224 225 private PemObject createPemObject( 226 Object obj, 227 String algorithm, 228 char[] password, 229 SecureRandom random) 230 throws IOException 231 { 232 if (obj instanceof KeyPair) 233 { 234 return createPemObject(((KeyPair)obj).getPrivate(), algorithm, password, random); 235 } 236 237 String type = null; 238 byte[] keyData = null; 239 240 if (obj instanceof RSAPrivateCrtKey) 241 { 242 type = "RSA PRIVATE KEY"; 243 244 RSAPrivateCrtKey k = (RSAPrivateCrtKey)obj; 245 246 RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure( 247 k.getModulus(), 248 k.getPublicExponent(), 249 k.getPrivateExponent(), 250 k.getPrimeP(), 251 k.getPrimeQ(), 252 k.getPrimeExponentP(), 253 k.getPrimeExponentQ(), 254 k.getCrtCoefficient()); 255 256 // convert to bytearray 257 keyData = keyStruct.getEncoded(); 258 } 259 else if (obj instanceof DSAPrivateKey) 260 { 261 type = "DSA PRIVATE KEY"; 262 263 DSAPrivateKey k = (DSAPrivateKey)obj; 264 DSAParams p = k.getParams(); 265 ASN1EncodableVector v = new ASN1EncodableVector(); 266 267 v.add(new DERInteger(0)); 268 v.add(new DERInteger(p.getP())); 269 v.add(new DERInteger(p.getQ())); 270 v.add(new DERInteger(p.getG())); 271 272 BigInteger x = k.getX(); 273 BigInteger y = p.getG().modPow(x, p.getP()); 274 275 v.add(new DERInteger(y)); 276 v.add(new DERInteger(x)); 277 278 keyData = new DERSequence(v).getEncoded(); 279 } 280 else if (obj instanceof PrivateKey && "ECDSA".equals(((PrivateKey)obj).getAlgorithm())) 281 { 282 type = "EC PRIVATE KEY"; 283 284 PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(ASN1Object.fromByteArray(((PrivateKey)obj).getEncoded())); 285 286 keyData = privInfo.getPrivateKey().getEncoded(); 287 } 288 289 if (type == null || keyData == null) 290 { 291 // TODO Support other types? 292 throw new IllegalArgumentException("Object type not supported: " + obj.getClass().getName()); 293 } 294 295 String dekAlgName = Strings.toUpperCase(algorithm); 296 297 // Note: For backward compatibility 298 if (dekAlgName.equals("DESEDE")) 299 { 300 dekAlgName = "DES-EDE3-CBC"; 301 } 302 303 int ivLength = dekAlgName.startsWith("AES-") ? 16 : 8; 304 305 byte[] iv = new byte[ivLength]; 306 random.nextBytes(iv); 307 308 byte[] encData = PEMUtilities.crypt(true, provider, keyData, password, dekAlgName, iv); 309 310 List headers = new ArrayList(2); 311 312 headers.add(new PemHeader("Proc-Type", "4,ENCRYPTED")); 313 headers.add(new PemHeader("DEK-Info", dekAlgName + "," + getHexEncoded(iv))); 314 315 return new PemObject(type, headers, encData); 316 } 317 318 public PemObject generate() 319 throws PemGenerationException 320 { 321 try 322 { 323 if (algorithm != null) 324 { 325 return createPemObject(obj, algorithm, password, random); 326 } 327 328 return createPemObject(obj); 329 } 330 catch (IOException e) 331 { 332 throw new PemGenerationException("encoding exception: " + e.getMessage(), e); 333 } 334 } 335 } 336