001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.geronimo.crypto;
018:
019: import java.io.BufferedReader;
020: import java.io.ByteArrayInputStream;
021: import java.io.ByteArrayOutputStream;
022: import java.io.FileOutputStream;
023: import java.io.IOException;
024: import java.io.InputStreamReader;
025: import java.io.OutputStream;
026: import java.io.PrintWriter;
027: import java.security.InvalidKeyException;
028: import java.security.KeyFactory;
029: import java.security.NoSuchAlgorithmException;
030: import java.security.NoSuchProviderException;
031: import java.security.PublicKey;
032: import java.security.Signature;
033: import java.security.SignatureException;
034: import java.security.cert.Certificate;
035: import java.security.cert.CertificateEncodingException;
036: import java.security.spec.RSAPublicKeySpec;
037: import java.util.HashMap;
038: import java.util.Hashtable;
039: import java.util.Map;
040: import java.util.Vector;
041:
042: import javax.security.auth.x500.X500Principal;
043:
044: import org.apache.commons.logging.Log;
045: import org.apache.commons.logging.LogFactory;
046: import org.apache.geronimo.crypto.asn1.ASN1InputStream;
047: import org.apache.geronimo.crypto.asn1.ASN1Sequence;
048: import org.apache.geronimo.crypto.asn1.DERBitString;
049: import org.apache.geronimo.crypto.asn1.DERObject;
050: import org.apache.geronimo.crypto.asn1.DERSequence;
051: import org.apache.geronimo.crypto.asn1.DERString;
052: import org.apache.geronimo.crypto.asn1.pkcs.CertificationRequestInfo;
053: import org.apache.geronimo.crypto.asn1.pkcs.PKCSObjectIdentifiers;
054: import org.apache.geronimo.crypto.asn1.x509.RSAPublicKeyStructure;
055: import org.apache.geronimo.crypto.asn1.x509.SubjectPublicKeyInfo;
056: import org.apache.geronimo.crypto.asn1.x509.X509CertificateStructure;
057: import org.apache.geronimo.crypto.asn1.x509.X509Name;
058: import org.apache.geronimo.crypto.encoders.Base64;
059: import org.apache.geronimo.crypto.jce.PKCS10CertificationRequest;
060:
061: /**
062: * This class implements some utility methods used by CA
063: *
064: * @version $Rev: 617735 $ $Date: 2008-02-01 17:20:02 -0800 (Fri, 01 Feb 2008) $
065: */
066: public class CaUtils {
067: private static final Log log = LogFactory.getLog(CaUtils.class);
068: public static final String CERT_HEADER = "-----BEGIN CERTIFICATE-----";
069: public static final String CERT_FOOTER = "-----END CERTIFICATE-----";
070: public static final String CERT_REQ_HEADER = "-----BEGIN CERTIFICATE REQUEST-----";
071: public static final String CERT_REQ_FOOTER = "-----END CERTIFICATE REQUEST-----";
072: public static final int B64_LINE_SIZE = 76;
073: public static final String CERT_REQ_SUBJECT = "subject";
074: public static final String CERT_REQ_PUBLICKEY = "publickey";
075: public static final String CERT_REQ_PUBLICKEY_OBJ = "publickeyObj";
076: public static final String CERT_REQ_VERSION = "version";
077: public static final String PKAC_CHALLENGE = "challenge";
078:
079: /**
080: * This method returns base64 encoded text of a given certificate.
081: * @param cert The certificate that needs to be encoded in base64
082: */
083: public static String base64Certificate(Certificate cert)
084: throws CertificateEncodingException, Exception {
085: return base64Text(cert.getEncoded(), CaUtils.CERT_HEADER,
086: CaUtils.CERT_FOOTER, CaUtils.B64_LINE_SIZE);
087: }
088:
089: /**
090: * This method encodes a given byte array into base64 along with specified header and footers.
091: * @param data The byte array to be encoded in base64
092: * @param header Header for base64 encoded text
093: * @param footer Footer for base64 encoded text
094: * @param lineSize Maximum line size to split base64 encoded text if required
095: */
096: public static String base64Text(byte[] data, String header,
097: String footer, int lineSize) throws Exception {
098: ByteArrayOutputStream bout = new ByteArrayOutputStream();
099: storeInBase64(bout, data, header, footer, lineSize);
100: bout.close();
101: return bout.toString();
102: }
103:
104: /**
105: * This method encodes a given byte array into base64 along with specified header and footers and writes
106: * the output to a specified OutputStream.
107: * @param fout Output stream to write the encoded text
108: * @param data The byte array to be encoded in base64
109: * @param header Header for base64 encoded text
110: * @param footer Footer for base64 encoded text
111: * @param lineSize Maximum line size to split base64 encoded text if required
112: */
113: public static void storeInBase64(OutputStream fout, byte[] data,
114: String header, String footer, int lineSize)
115: throws Exception {
116: PrintWriter out = new PrintWriter(fout);
117: if (header != null)
118: out.println(header);
119:
120: byte[] encodedData = Base64.encode(data);
121: int i = 0;
122: do {
123: out.println(new String(encodedData, i, Math.min(lineSize,
124: encodedData.length - i)));
125: i += lineSize;
126: } while (i < encodedData.length);
127:
128: if (footer != null)
129: out.println(footer);
130: out.flush();
131: }
132:
133: /**
134: * This method encodes a given byte array into base64 along with specified header and footers and writes
135: * the output to a specified file.
136: * @param outfile File name to write the output to
137: * @param data The byte array to be encoded in base64
138: * @param header Header for base64 encoded text
139: * @param footer Footer for base64 encoded text
140: * @param lineSize Maximum line size to split base64 encoded text if required
141: */
142: public static void storeInBase64(String outfile, byte[] data,
143: String header, String footer, int lineSize)
144: throws Exception {
145: FileOutputStream fout = new FileOutputStream(outfile);
146: storeInBase64(fout, data, header, footer, lineSize);
147: fout.close();
148: }
149:
150: /**
151: * This method creates a java.security.PublicKey object based on the public key information given in SubjectPublicKeyInfo
152: * @param pubKeyInfo SubjectPublicKeyInfo instance containing the public key information.
153: */
154: public static PublicKey getPublicKeyObject(
155: SubjectPublicKeyInfo pubKeyInfo) throws Exception {
156: RSAPublicKeyStructure pubkeyStruct = new RSAPublicKeyStructure(
157: (ASN1Sequence) pubKeyInfo.getPublicKey());
158: RSAPublicKeySpec pubkeySpec = new RSAPublicKeySpec(pubkeyStruct
159: .getModulus(), pubkeyStruct.getPublicExponent());
160: KeyFactory keyFactory = KeyFactory.getInstance("RSA");
161: PublicKey pubKey = keyFactory.generatePublic(pubkeySpec);
162: return pubKey;
163: }
164:
165: /**
166: * This method returns a X509Name object corresponding to the subject in a given certificate
167: * @param cert Certificate from which subject needs to be retrieved
168: */
169: public static X509Name getSubjectX509Name(Certificate cert)
170: throws CertificateEncodingException, IOException {
171: ASN1InputStream ais = new ASN1InputStream(cert.getEncoded());
172: X509CertificateStructure x509Struct = new X509CertificateStructure(
173: (ASN1Sequence) ais.readObject());
174: ais.close();
175: return x509Struct.getSubject();
176: }
177:
178: /**
179: * This method returns a X509Name object corresponding to a given principal
180: */
181: public static X509Name getX509Name(X500Principal principal)
182: throws CertificateEncodingException, IOException {
183: ASN1InputStream ais = new ASN1InputStream(principal
184: .getEncoded());
185: X509Name name = new X509Name((ASN1Sequence) ais.readObject());
186: ais.close();
187: return name;
188: }
189:
190: /**
191: * This method processes a certificate request and returns a map containing subject
192: * and public key in the request.
193: * @param certreq base64 encoded PKCS10 certificate request
194: */
195: public static Map processPKCS10Request(String certreq)
196: throws InvalidKeyException, NoSuchAlgorithmException,
197: NoSuchProviderException, SignatureException, Exception {
198: if (certreq.indexOf("-----") != -1) {
199: // Strip any header and footer
200: BufferedReader br = new BufferedReader(
201: new InputStreamReader(new ByteArrayInputStream(
202: certreq.getBytes())));
203: String line = null;
204: String b64data = "";
205: while ((line = br.readLine()) != null) {
206: if (!line.startsWith("-----")) {
207: b64data += line;
208: }
209: }
210: br.close();
211: certreq = b64data;
212: }
213: byte[] data = Base64.decode(certreq);
214:
215: PKCS10CertificationRequest pkcs10certreq = new PKCS10CertificationRequest(
216: data);
217: if (!pkcs10certreq.verify()) {
218: throw new Exception("CSR verification failed.");
219: }
220: CertificationRequestInfo certReqInfo = pkcs10certreq
221: .getCertificationRequestInfo();
222: Map map = new HashMap();
223: map.put(CERT_REQ_SUBJECT, certReqInfo.getSubject());
224: map.put(CERT_REQ_PUBLICKEY, certReqInfo
225: .getSubjectPublicKeyInfo());
226: map.put(CERT_REQ_PUBLICKEY_OBJ, getPublicKeyObject(certReqInfo
227: .getSubjectPublicKeyInfo()));
228: map.put(CERT_REQ_VERSION, certReqInfo.getVersion());
229: return map;
230: }
231:
232: /**
233: * This method processes a DER encoded SignedPublicKeyAndChallenge in base64 format.
234: * @param spkac SignedPublicKeyAndChallenge in base64 text format
235: * @return a Map with Subject, public-key and challenge
236: */
237: public static Map processSPKAC(String spkac) throws IOException,
238: NoSuchAlgorithmException, InvalidKeyException,
239: SignatureException, Exception {
240: Map map = new HashMap();
241: byte[] data = Base64.decode(spkac);
242: ASN1InputStream ais = new ASN1InputStream(
243: new ByteArrayInputStream(data));
244: DERSequence spkacSeq = (DERSequence) ais.readObject();
245:
246: // SPKAC = SEQ {PKAC, SIGN-ALG, SIGN}
247: // Get PKAC and obtain PK and C
248: DERSequence pkacSeq = (DERSequence) spkacSeq.getObjectAt(0);
249: DERObject pk = (DERObject) pkacSeq.getObjectAt(0);
250: DERObject ch = (DERObject) pkacSeq.getObjectAt(1);
251: SubjectPublicKeyInfo pkInfo = new SubjectPublicKeyInfo(
252: (DERSequence) pk);
253: PublicKey pubKey = getPublicKeyObject(pkInfo);
254:
255: // Get SIGN-ALG
256: DERSequence signAlg = (DERSequence) spkacSeq.getObjectAt(1);
257: DERObject alg0 = (DERObject) signAlg.getObjectAt(0);
258:
259: // Get SIGN
260: DERBitString sign = (DERBitString) spkacSeq.getObjectAt(2);
261: byte[] signature = sign.getBytes();
262:
263: // Verify the signature on SPKAC
264: String signAlgString = PKCSObjectIdentifiers.md5WithRSAEncryption
265: .equals(alg0) ? "MD5withRSA"
266: : PKCSObjectIdentifiers.md2WithRSAEncryption
267: .equals(alg0) ? "MD2withRSA"
268: : PKCSObjectIdentifiers.sha1WithRSAEncryption
269: .equals(alg0) ? "SHA1withRSA" : null;
270: Signature signObj = Signature.getInstance(signAlgString);
271: signObj.initVerify(pubKey);
272: signObj.update(pkacSeq.getEncoded());
273: boolean verified = signObj.verify(signature);
274: if (!verified)
275: throw new Exception(
276: "SignedPublicKeyAndChallenge verification failed.");
277: map.put(CERT_REQ_PUBLICKEY, pkInfo);
278: map.put(CERT_REQ_PUBLICKEY_OBJ, pubKey);
279: if (((DERString) ch).getString() != null)
280: map.put(PKAC_CHALLENGE, ((DERString) ch).getString());
281: return map;
282: }
283:
284: /**
285: * This method creates a X509Name object using the name attributes specified.
286: * @param cn Common Name
287: * @param ou Organization Unit
288: * @param o Organization
289: * @param l Locality
290: * @param st State
291: * @param c Country
292: */
293: public static X509Name getX509Name(String cn, String ou, String o,
294: String l, String st, String c) {
295: Vector order = new Vector();
296: Hashtable attrmap = new Hashtable();
297: if (c != null) {
298: attrmap.put(X509Name.C, c);
299: order.add(X509Name.C);
300: }
301:
302: if (st != null) {
303: attrmap.put(X509Name.ST, st);
304: order.add(X509Name.ST);
305: }
306:
307: if (l != null) {
308: attrmap.put(X509Name.L, l);
309: order.add(X509Name.L);
310: }
311:
312: if (o != null) {
313: attrmap.put(X509Name.O, o);
314: order.add(X509Name.O);
315: }
316:
317: if (ou != null) {
318: attrmap.put(X509Name.OU, ou);
319: order.add(X509Name.OU);
320: }
321:
322: if (cn != null) {
323: attrmap.put(X509Name.CN, cn);
324: order.add(X509Name.CN);
325: }
326:
327: return new X509Name(order, attrmap);
328: }
329: }
|