001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.crypto.jce;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.ByteArrayOutputStream;
020: import java.io.IOException;
021: import java.security.InvalidKeyException;
022: import java.security.KeyFactory;
023: import java.security.NoSuchAlgorithmException;
024: import java.security.NoSuchProviderException;
025: import java.security.PrivateKey;
026: import java.security.PublicKey;
027: import java.security.Signature;
028: import java.security.SignatureException;
029: import java.security.spec.InvalidKeySpecException;
030: import java.security.spec.X509EncodedKeySpec;
031: import java.util.Hashtable;
032:
033: import javax.security.auth.x500.X500Principal;
034:
035: import org.apache.geronimo.crypto.asn1.ASN1InputStream;
036: import org.apache.geronimo.crypto.asn1.ASN1Sequence;
037: import org.apache.geronimo.crypto.asn1.ASN1Set;
038: import org.apache.geronimo.crypto.asn1.DERBitString;
039: import org.apache.geronimo.crypto.asn1.DERObjectIdentifier;
040: import org.apache.geronimo.crypto.asn1.DEROutputStream;
041: import org.apache.geronimo.crypto.asn1.pkcs.PKCSObjectIdentifiers;
042: import org.apache.geronimo.crypto.asn1.pkcs.CertificationRequest;
043: import org.apache.geronimo.crypto.asn1.pkcs.CertificationRequestInfo;
044: import org.apache.geronimo.crypto.asn1.x509.AlgorithmIdentifier;
045: import org.apache.geronimo.crypto.asn1.x509.SubjectPublicKeyInfo;
046: import org.apache.geronimo.crypto.asn1.x509.X509Name;
047: import org.apache.geronimo.crypto.asn1.x9.X9ObjectIdentifiers;
048:
049: /**
050: * A class for verifying and creating PKCS10 Certification requests.
051: * <pre>
052: * CertificationRequest ::= SEQUENCE {
053: * certificationRequestInfo CertificationRequestInfo,
054: * signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
055: * signature BIT STRING
056: * }
057: *
058: * CertificationRequestInfo ::= SEQUENCE {
059: * version INTEGER { v1(0) } (v1,...),
060: * subject Name,
061: * subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
062: * attributes [0] Attributes{{ CRIAttributes }}
063: * }
064: *
065: * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
066: *
067: * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
068: * type ATTRIBUTE.&id({IOSet}),
069: * values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
070: * }
071: * </pre>
072: */
073: public class PKCS10CertificationRequest extends CertificationRequest {
074: private static Hashtable algorithms = new Hashtable();
075: private static Hashtable oids = new Hashtable();
076:
077: static {
078: algorithms.put("MD2WITHRSAENCRYPTION", new DERObjectIdentifier(
079: "1.2.840.113549.1.1.2"));
080: algorithms.put("MD2WITHRSA", new DERObjectIdentifier(
081: "1.2.840.113549.1.1.2"));
082: algorithms.put("MD5WITHRSA", new DERObjectIdentifier(
083: "1.2.840.113549.1.1.1"));
084: algorithms.put("MD5WITHRSAENCRYPTION", new DERObjectIdentifier(
085: "1.2.840.113549.1.1.4"));
086: algorithms.put("MD5WITHRSA", new DERObjectIdentifier(
087: "1.2.840.113549.1.1.4"));
088: algorithms.put("RSAWITHMD5", new DERObjectIdentifier(
089: "1.2.840.113549.1.1.4"));
090: algorithms.put("SHA1WITHRSAENCRYPTION",
091: new DERObjectIdentifier("1.2.840.113549.1.1.5"));
092: algorithms.put("SHA1WITHRSA", new DERObjectIdentifier(
093: "1.2.840.113549.1.1.5"));
094: algorithms.put("SHA224WITHRSAENCRYPTION",
095: PKCSObjectIdentifiers.sha224WithRSAEncryption);
096: algorithms.put("SHA224WITHRSA",
097: PKCSObjectIdentifiers.sha224WithRSAEncryption);
098: algorithms.put("SHA256WITHRSAENCRYPTION",
099: PKCSObjectIdentifiers.sha256WithRSAEncryption);
100: algorithms.put("SHA256WITHRSA",
101: PKCSObjectIdentifiers.sha256WithRSAEncryption);
102: algorithms.put("SHA384WITHRSAENCRYPTION",
103: PKCSObjectIdentifiers.sha384WithRSAEncryption);
104: algorithms.put("SHA384WITHRSA",
105: PKCSObjectIdentifiers.sha384WithRSAEncryption);
106: algorithms.put("SHA512WITHRSAENCRYPTION",
107: PKCSObjectIdentifiers.sha512WithRSAEncryption);
108: algorithms.put("SHA512WITHRSA",
109: PKCSObjectIdentifiers.sha512WithRSAEncryption);
110: algorithms.put("RSAWITHSHA1", new DERObjectIdentifier(
111: "1.2.840.113549.1.1.5"));
112: algorithms.put("RIPEMD160WITHRSAENCRYPTION",
113: new DERObjectIdentifier("1.3.36.3.3.1.2"));
114: algorithms.put("RIPEMD160WITHRSA", new DERObjectIdentifier(
115: "1.3.36.3.3.1.2"));
116: algorithms.put("SHA1WITHDSA", new DERObjectIdentifier(
117: "1.2.840.10040.4.3"));
118: algorithms.put("DSAWITHSHA1", new DERObjectIdentifier(
119: "1.2.840.10040.4.3"));
120: algorithms.put("SHA1WITHECDSA",
121: X9ObjectIdentifiers.ecdsa_with_SHA1);
122: algorithms.put("ECDSAWITHSHA1",
123: X9ObjectIdentifiers.ecdsa_with_SHA1);
124:
125: //
126: // reverse mappings
127: //
128: oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.5"),
129: "SHA1WITHRSA");
130: oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption,
131: "SHA224WITHRSA");
132: oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption,
133: "SHA256WITHRSA");
134: oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption,
135: "SHA384WITHRSA");
136: oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption,
137: "SHA512WITHRSA");
138:
139: oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.4"),
140: "MD5WITHRSA");
141: oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.2"),
142: "MD2WITHRSA");
143: oids.put(new DERObjectIdentifier("1.2.840.113549.1.1.1"),
144: "MD5WIDHRSA");
145: oids.put(new DERObjectIdentifier("1.2.840.10040.4.3"),
146: "DSAWITHSHA1");
147: oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "DSAWITHSHA1");
148: }
149:
150: private static ASN1Sequence toDERSequence(byte[] bytes) {
151: try {
152: ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
153: ASN1InputStream dIn = new ASN1InputStream(bIn);
154:
155: return (ASN1Sequence) dIn.readObject();
156: } catch (Exception e) {
157: throw new IllegalArgumentException("badly encoded request",
158: e);
159: }
160: }
161:
162: /**
163: * construct a PKCS10 certification request from a DER encoded
164: * byte stream.
165: */
166: public PKCS10CertificationRequest(byte[] bytes) {
167: super (toDERSequence(bytes));
168: }
169:
170: public PKCS10CertificationRequest(ASN1Sequence sequence) {
171: super (sequence);
172: }
173:
174: /**
175: * create a PKCS10 certfication request using the BC provider.
176: */
177: public PKCS10CertificationRequest(String signatureAlgorithm,
178: X509Name subject, PublicKey key, ASN1Set attributes,
179: PrivateKey signingKey) throws NoSuchAlgorithmException,
180: NoSuchProviderException, InvalidKeyException,
181: SignatureException {
182: this (signatureAlgorithm, subject, key, attributes, signingKey,
183: null);
184: }
185:
186: private static X509Name convertName(X500Principal name) {
187: try {
188: return new X509Principal(name.getEncoded());
189: } catch (IOException e) {
190: throw new IllegalArgumentException("can't convert name", e);
191: }
192: }
193:
194: /**
195: * create a PKCS10 certfication request using the BC provider.
196: */
197: public PKCS10CertificationRequest(String signatureAlgorithm,
198: X500Principal subject, PublicKey key, ASN1Set attributes,
199: PrivateKey signingKey) throws NoSuchAlgorithmException,
200: NoSuchProviderException, InvalidKeyException,
201: SignatureException {
202: this (signatureAlgorithm, convertName(subject), key, attributes,
203: signingKey, null);
204: }
205:
206: /**
207: * create a PKCS10 certfication request using the named provider.
208: */
209: public PKCS10CertificationRequest(String signatureAlgorithm,
210: X500Principal subject, PublicKey key, ASN1Set attributes,
211: PrivateKey signingKey, String provider)
212: throws NoSuchAlgorithmException, NoSuchProviderException,
213: InvalidKeyException, SignatureException {
214: this (signatureAlgorithm, convertName(subject), key, attributes,
215: signingKey, provider);
216: }
217:
218: /**
219: * create a PKCS10 certfication request using the named provider.
220: */
221: public PKCS10CertificationRequest(String signatureAlgorithm,
222: X509Name subject, PublicKey key, ASN1Set attributes,
223: PrivateKey signingKey, String provider)
224: throws NoSuchAlgorithmException, NoSuchProviderException,
225: InvalidKeyException, SignatureException {
226: DERObjectIdentifier sigOID = (DERObjectIdentifier) algorithms
227: .get(signatureAlgorithm.toUpperCase());
228:
229: if (sigOID == null) {
230: throw new IllegalArgumentException(
231: "Unknown signature type requested");
232: }
233:
234: if (subject == null) {
235: throw new IllegalArgumentException(
236: "subject must not be null");
237: }
238:
239: if (key == null) {
240: throw new IllegalArgumentException(
241: "public key must not be null");
242: }
243:
244: this .sigAlgId = new AlgorithmIdentifier(sigOID, null);
245:
246: byte[] bytes = key.getEncoded();
247: ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
248: ASN1InputStream dIn = new ASN1InputStream(bIn);
249:
250: try {
251: this .reqInfo = new CertificationRequestInfo(subject,
252: new SubjectPublicKeyInfo((ASN1Sequence) dIn
253: .readObject()), attributes);
254: } catch (IOException e) {
255: throw new IllegalArgumentException(
256: "can't encode public key", e);
257: }
258:
259: Signature sig = null;
260:
261: try {
262: if (provider == null) {
263: sig = Signature.getInstance(sigAlgId.getObjectId()
264: .getId());
265: } else {
266: sig = Signature.getInstance(sigAlgId.getObjectId()
267: .getId(), provider);
268: }
269: } catch (NoSuchAlgorithmException e) {
270: if (provider == null) {
271: sig = Signature.getInstance(signatureAlgorithm);
272: } else {
273: sig = Signature.getInstance(signatureAlgorithm,
274: provider);
275: }
276: }
277:
278: sig.initSign(signingKey);
279:
280: try {
281: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
282: DEROutputStream dOut = new DEROutputStream(bOut);
283:
284: dOut.writeObject(reqInfo);
285:
286: sig.update(bOut.toByteArray());
287: } catch (Exception e) {
288: throw new SecurityException(
289: "exception encoding TBS cert request - "
290: + e.getMessage(), e);
291: }
292:
293: this .sigBits = new DERBitString(sig.sign());
294: }
295:
296: /**
297: * return the public key associated with the certification request -
298: * the public key is created using the BC provider.
299: */
300: public PublicKey getPublicKey() throws NoSuchAlgorithmException,
301: NoSuchProviderException, InvalidKeyException {
302: return getPublicKey(null);
303: }
304:
305: public PublicKey getPublicKey(String provider)
306: throws NoSuchAlgorithmException, NoSuchProviderException,
307: InvalidKeyException {
308: SubjectPublicKeyInfo subjectPKInfo = reqInfo
309: .getSubjectPublicKeyInfo();
310:
311: try {
312: X509EncodedKeySpec xspec = new X509EncodedKeySpec(
313: new DERBitString(subjectPKInfo).getBytes());
314: AlgorithmIdentifier keyAlg = subjectPKInfo.getAlgorithmId();
315: try {
316:
317: if (provider == null) {
318: return KeyFactory.getInstance(
319: keyAlg.getObjectId().getId())
320: .generatePublic(xspec);
321: } else {
322: return KeyFactory.getInstance(
323: keyAlg.getObjectId().getId(), provider)
324: .generatePublic(xspec);
325: }
326:
327: } catch (NoSuchAlgorithmException e) {
328: // if we can't resolve this via the OID, just as for the RSA algorithm. This is all
329: // Geronimo requires anyway.
330: if (provider == null) {
331: return KeyFactory.getInstance("RSA")
332: .generatePublic(xspec);
333: } else {
334: return KeyFactory.getInstance("RSA", provider)
335: .generatePublic(xspec);
336: }
337: }
338: } catch (InvalidKeySpecException e) {
339: throw (InvalidKeyException) new InvalidKeyException(
340: "error decoding public key").initCause(e);
341: }
342: }
343:
344: /**
345: * verify the request using the BC provider.
346: */
347: public boolean verify() throws NoSuchAlgorithmException,
348: NoSuchProviderException, InvalidKeyException,
349: SignatureException {
350: return verify(null);
351: }
352:
353: public boolean verify(String provider)
354: throws NoSuchAlgorithmException, NoSuchProviderException,
355: InvalidKeyException, SignatureException {
356: Signature sig = null;
357:
358: try {
359: if (provider == null) {
360: sig = Signature.getInstance(sigAlgId.getObjectId()
361: .getId());
362: } else {
363: sig = Signature.getInstance(sigAlgId.getObjectId()
364: .getId(), provider);
365: }
366: } catch (NoSuchAlgorithmException e) {
367: //
368: // try an alternate
369: //
370: if (oids.get(sigAlgId.getObjectId().getId()) != null) {
371: String signatureAlgorithm = (String) oids.get(sigAlgId
372: .getObjectId().getId());
373:
374: if (provider == null) {
375: sig = Signature.getInstance(signatureAlgorithm);
376: } else {
377: sig = Signature.getInstance(signatureAlgorithm,
378: provider);
379: }
380: }
381: }
382:
383: sig.initVerify(this .getPublicKey(provider));
384:
385: try {
386: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
387: DEROutputStream dOut = new DEROutputStream(bOut);
388:
389: dOut.writeObject(reqInfo);
390:
391: sig.update(bOut.toByteArray());
392: } catch (Exception e) {
393: throw (SecurityException) new SecurityException(
394: "exception encoding TBS cert request - "
395: + e.getMessage()).initCause(e);
396: }
397:
398: return sig.verify(sigBits.getBytes());
399: }
400:
401: /**
402: * return a DER encoded byte array representing this object
403: */
404: public byte[] getEncoded() {
405: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
406: DEROutputStream dOut = new DEROutputStream(bOut);
407:
408: try {
409: dOut.writeObject(this );
410: } catch (IOException e) {
411: throw new RuntimeException(e.getMessage(), e);
412: }
413:
414: return bOut.toByteArray();
415: }
416: }
|