001: package org.bouncycastle.openssl;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.ASN1Object;
005: import org.bouncycastle.asn1.ASN1Sequence;
006: import org.bouncycastle.asn1.DERInteger;
007: import org.bouncycastle.asn1.DERSequence;
008: import org.bouncycastle.asn1.cms.ContentInfo;
009: import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
010: import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
011: import org.bouncycastle.asn1.x509.DSAParameter;
012: import org.bouncycastle.jce.PKCS10CertificationRequest;
013: import org.bouncycastle.util.Strings;
014: import org.bouncycastle.util.encoders.Base64;
015: import org.bouncycastle.util.encoders.Hex;
016: import org.bouncycastle.x509.X509AttributeCertificate;
017: import org.bouncycastle.x509.X509V2AttributeCertificate;
018:
019: import java.io.BufferedWriter;
020: import java.io.IOException;
021: import java.io.Writer;
022: import java.math.BigInteger;
023: import java.security.Key;
024: import java.security.KeyPair;
025: import java.security.PrivateKey;
026: import java.security.PublicKey;
027: import java.security.SecureRandom;
028: import java.security.cert.CRLException;
029: import java.security.cert.CertificateEncodingException;
030: import java.security.cert.X509CRL;
031: import java.security.cert.X509Certificate;
032: import java.security.interfaces.DSAParams;
033: import java.security.interfaces.DSAPrivateKey;
034: import java.security.interfaces.RSAPrivateCrtKey;
035: import java.security.interfaces.RSAPrivateKey;
036:
037: /**
038: * General purpose writer for OpenSSL PEM objects.
039: */
040: public class PEMWriter extends BufferedWriter {
041: private String provider;
042:
043: /**
044: * Base constructor.
045: *
046: * @param out output stream to use.
047: */
048: public PEMWriter(Writer out) {
049: this (out, "BC");
050: }
051:
052: public PEMWriter(Writer out, String provider) {
053: super (out);
054:
055: this .provider = provider;
056: }
057:
058: private void writeHexEncoded(byte[] bytes) throws IOException {
059: bytes = Hex.encode(bytes);
060:
061: for (int i = 0; i != bytes.length; i++) {
062: this .write((char) bytes[i]);
063: }
064: }
065:
066: private void writeEncoded(byte[] bytes) throws IOException {
067: char[] buf = new char[64];
068:
069: bytes = Base64.encode(bytes);
070:
071: for (int i = 0; i < bytes.length; i += buf.length) {
072: int index = 0;
073:
074: while (index != buf.length) {
075: if ((i + index) >= bytes.length) {
076: break;
077: }
078: buf[index] = (char) bytes[i + index];
079: index++;
080: }
081: this .write(buf, 0, index);
082: this .newLine();
083: }
084: }
085:
086: public void writeObject(Object o) throws IOException {
087: String type;
088: byte[] encoding;
089:
090: if (o instanceof X509Certificate) {
091: type = "CERTIFICATE";
092: try {
093: encoding = ((X509Certificate) o).getEncoded();
094: } catch (CertificateEncodingException e) {
095: throw new IOException("Cannot encode object: "
096: + e.toString());
097: }
098: } else if (o instanceof X509CRL) {
099: type = "X509 CRL";
100: try {
101: encoding = ((X509CRL) o).getEncoded();
102: } catch (CRLException e) {
103: throw new IOException("Cannot encode object: "
104: + e.toString());
105: }
106: } else if (o instanceof KeyPair) {
107: writeObject(((KeyPair) o).getPrivate());
108: return;
109: } else if (o instanceof PrivateKey) {
110: PrivateKeyInfo info = new PrivateKeyInfo(
111: (ASN1Sequence) ASN1Object.fromByteArray(((Key) o)
112: .getEncoded()));
113:
114: if (o instanceof RSAPrivateKey) {
115: type = "RSA PRIVATE KEY";
116:
117: encoding = info.getPrivateKey().getEncoded();
118: } else if (o instanceof DSAPrivateKey) {
119: type = "DSA PRIVATE KEY";
120:
121: DSAParameter p = DSAParameter.getInstance(info
122: .getAlgorithmId().getParameters());
123: ASN1EncodableVector v = new ASN1EncodableVector();
124:
125: v.add(new DERInteger(0));
126: v.add(new DERInteger(p.getP()));
127: v.add(new DERInteger(p.getQ()));
128: v.add(new DERInteger(p.getG()));
129:
130: BigInteger x = ((DSAPrivateKey) o).getX();
131: BigInteger y = p.getG().modPow(x, p.getP());
132:
133: v.add(new DERInteger(y));
134: v.add(new DERInteger(x));
135:
136: encoding = new DERSequence(v).getEncoded();
137: } else {
138: throw new IOException("Cannot identify private key");
139: }
140: } else if (o instanceof PublicKey) {
141: type = "PUBLIC KEY";
142:
143: encoding = ((PublicKey) o).getEncoded();
144: } else if (o instanceof X509AttributeCertificate) {
145: type = "ATTRIBUTE CERTIFICATE";
146: encoding = ((X509V2AttributeCertificate) o).getEncoded();
147: } else if (o instanceof PKCS10CertificationRequest) {
148: type = "CERTIFICATE REQUEST";
149: encoding = ((PKCS10CertificationRequest) o).getEncoded();
150: } else if (o instanceof ContentInfo) {
151: type = "PKCS7";
152: encoding = ((ContentInfo) o).getEncoded();
153: } else {
154: throw new IOException(
155: "unknown object passed - can't encode.");
156: }
157:
158: writeHeader(type);
159: writeEncoded(encoding);
160: writeFooter(type);
161: }
162:
163: public void writeObject(Object obj, String algorithm,
164: char[] password, SecureRandom random) throws IOException {
165: if (obj instanceof KeyPair) {
166: writeObject(((KeyPair) obj).getPrivate());
167: return;
168: }
169:
170: String type = null;
171: byte[] keyData = null;
172:
173: if (obj instanceof RSAPrivateCrtKey) {
174: type = "RSA PRIVATE KEY";
175:
176: RSAPrivateCrtKey k = (RSAPrivateCrtKey) obj;
177:
178: RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(
179: k.getModulus(), k.getPublicExponent(), k
180: .getPrivateExponent(), k.getPrimeP(), k
181: .getPrimeQ(), k.getPrimeExponentP(), k
182: .getPrimeExponentQ(), k.getCrtCoefficient());
183:
184: // convert to bytearray
185: keyData = keyStruct.getEncoded();
186: } else if (obj instanceof DSAPrivateKey) {
187: type = "DSA PRIVATE KEY";
188:
189: DSAPrivateKey k = (DSAPrivateKey) obj;
190: DSAParams p = k.getParams();
191: ASN1EncodableVector v = new ASN1EncodableVector();
192:
193: v.add(new DERInteger(0));
194: v.add(new DERInteger(p.getP()));
195: v.add(new DERInteger(p.getQ()));
196: v.add(new DERInteger(p.getG()));
197:
198: BigInteger x = k.getX();
199: BigInteger y = p.getG().modPow(x, p.getP());
200:
201: v.add(new DERInteger(y));
202: v.add(new DERInteger(x));
203:
204: keyData = new DERSequence(v).getEncoded();
205: }
206:
207: if (type == null || keyData == null) {
208: // TODO Support other types?
209: throw new IllegalArgumentException(
210: "Object type not supported: "
211: + obj.getClass().getName());
212: }
213:
214: String dekAlgName = Strings.toUpperCase(algorithm);
215:
216: // Note: For backward compatibility
217: if (dekAlgName.equals("DESEDE")) {
218: dekAlgName = "DES-EDE3-CBC";
219: }
220:
221: int ivLength = dekAlgName.startsWith("AES-") ? 16 : 8;
222:
223: byte[] iv = new byte[ivLength];
224: random.nextBytes(iv);
225:
226: byte[] encData = PEMUtilities.crypt(true, provider, keyData,
227: password, dekAlgName, iv);
228:
229: // write the data
230: writeHeader(type);
231: this .write("Proc-Type: 4,ENCRYPTED");
232: this .newLine();
233: this .write("DEK-Info: " + dekAlgName + ",");
234: this .writeHexEncoded(iv);
235: this .newLine();
236: this .newLine();
237: this .writeEncoded(encData);
238: writeFooter(type);
239: }
240:
241: private void writeHeader(String type) throws IOException {
242: this .write("-----BEGIN " + type + "-----");
243: this .newLine();
244: }
245:
246: private void writeFooter(String type) throws IOException {
247: this .write("-----END " + type + "-----");
248: this.newLine();
249: }
250: }
|