1 /* RSAKeyPairPKCS8Codec.java -- PKCS#8 Encoding/Decoding handler
2 Copyright (C) 2006, 2010 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.security.key.rsa;
41 import gnu.java.security.Configuration;
42 import gnu.java.security.OID;
43 import gnu.java.security.Registry;
44 import gnu.java.security.der.DER;
45 import gnu.java.security.der.DERReader;
46 import gnu.java.security.der.DERValue;
47 import gnu.java.security.der.DERWriter;
48 import gnu.java.security.key.IKeyPairCodec;
49 import gnu.java.security.util.DerUtil;
51 import java.io.ByteArrayOutputStream;
52 import java.io.IOException;
53 import java.math.BigInteger;
54 import java.security.InvalidParameterException;
55 import java.security.PrivateKey;
56 import java.security.PublicKey;
57 import java.util.ArrayList;
58 import java.util.logging.Logger;
61 * An implementation of an {@link IKeyPairCodec} that knows how to encode /
62 * decode PKCS#8 ASN.1 external representation of RSA private keys.
64 public class RSAKeyPairPKCS8Codec
65 implements IKeyPairCodec
67 private static final Logger log = Configuration.DEBUG ?
68 Logger.getLogger(RSAKeyPairPKCS8Codec.class.getName()) : null;
70 private static final OID RSA_ALG_OID = new OID(Registry.RSA_OID_STRING);
72 // implicit 0-arguments constructor
74 public int getFormatID()
80 * @throws InvalidParameterException ALWAYS.
82 public byte[] encodePublicKey(PublicKey key)
84 throw new InvalidParameterException("Wrong format for public keys");
88 * Returns the PKCS#8 ASN.1 <i>PrivateKeyInfo</i> representation of an RSA
89 * private key. The ASN.1 specification is as follows:
91 * PrivateKeyInfo ::= SEQUENCE {
92 * version INTEGER, -- MUST be 0
93 * privateKeyAlgorithm AlgorithmIdentifier,
94 * privateKey OCTET STRING
97 * AlgorithmIdentifier ::= SEQUENCE {
98 * algorithm OBJECT IDENTIFIER,
99 * parameters ANY DEFINED BY algorithm OPTIONAL
103 * As indicated in RFC-2459: "The parameters field shall have ASN.1 type NULL
104 * for this algorithm identifier.".
106 * The <i>privateKey</i> field, which is an OCTET STRING, contains the
107 * DER-encoded form of the RSA private key defined as:
109 * RSAPrivateKey ::= SEQUENCE {
110 * version INTEGER, -- MUST be 0
111 * modulus INTEGER, -- n
112 * publicExponent INTEGER, -- e
113 * privateExponent INTEGER, -- d
114 * prime1 INTEGER, -- p
115 * prime2 INTEGER, -- q
116 * exponent1 INTEGER, -- d mod (p-1)
117 * exponent2 INTEGER, -- d mod (q-1)
118 * coefficient INTEGER, -- (inverse of q) mod p
122 * @return the DER encoded form of the ASN.1 representation of the
123 * <i>PrivateKeyInfo</i> field for an RSA {@link PrivateKey}..
124 * @throw InvalidParameterException if an error occurs during the marshalling
127 public byte[] encodePrivateKey(PrivateKey key)
129 if (Configuration.DEBUG)
130 log.entering(this.getClass().getName(), "encodePrivateKey()", key);
131 if (! (key instanceof GnuRSAPrivateKey))
132 throw new InvalidParameterException("Wrong key type");
134 GnuRSAPrivateKey pk = (GnuRSAPrivateKey) key;
135 BigInteger n = pk.getN();
136 BigInteger e = pk.getE();
137 BigInteger d = pk.getPrivateExponent();
138 BigInteger p = pk.getPrimeP();
139 BigInteger q = pk.getPrimeQ();
140 BigInteger dP = pk.getPrimeExponentP();
141 BigInteger dQ = pk.getPrimeExponentQ();
142 BigInteger qInv = pk.getCrtCoefficient();
144 DERValue derVersion = new DERValue(DER.INTEGER, BigInteger.ZERO);
146 DERValue derOID = new DERValue(DER.OBJECT_IDENTIFIER, RSA_ALG_OID);
148 ArrayList algorithmID = new ArrayList(2);
149 algorithmID.add(derOID);
150 algorithmID.add(new DERValue(DER.NULL, null));
151 DERValue derAlgorithmID = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
154 DERValue derRSAVersion = new DERValue(DER.INTEGER, BigInteger.ZERO);
155 DERValue derN = new DERValue(DER.INTEGER, n);
156 DERValue derE = new DERValue(DER.INTEGER, e);
157 DERValue derD = new DERValue(DER.INTEGER, d);
158 DERValue derP = new DERValue(DER.INTEGER, p);
159 DERValue derQ = new DERValue(DER.INTEGER, q);
160 DERValue derDP = new DERValue(DER.INTEGER, dP);
161 DERValue derDQ = new DERValue(DER.INTEGER, dQ);
162 DERValue derQInv = new DERValue(DER.INTEGER, qInv);
164 ArrayList rsaPrivateKey = new ArrayList();
165 rsaPrivateKey.add(derRSAVersion);
166 rsaPrivateKey.add(derN);
167 rsaPrivateKey.add(derE);
168 rsaPrivateKey.add(derD);
169 rsaPrivateKey.add(derP);
170 rsaPrivateKey.add(derQ);
171 rsaPrivateKey.add(derDP);
172 rsaPrivateKey.add(derDQ);
173 rsaPrivateKey.add(derQInv);
174 DERValue derRSAPrivateKey = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
176 byte[] pkBytes = derRSAPrivateKey.getEncoded();
177 DERValue derPrivateKey = new DERValue(DER.OCTET_STRING, pkBytes);
179 ArrayList pki = new ArrayList(3);
181 pki.add(derAlgorithmID);
182 pki.add(derPrivateKey);
183 DERValue derPKI = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, pki);
186 ByteArrayOutputStream baos = new ByteArrayOutputStream();
189 DERWriter.write(baos, derPKI);
190 result = baos.toByteArray();
192 catch (IOException x)
194 InvalidParameterException y = new InvalidParameterException();
198 if (Configuration.DEBUG)
199 log.exiting(this.getClass().getName(), "encodePrivateKey()", result);
204 * @throws InvalidParameterException ALWAYS.
206 public PublicKey decodePublicKey(byte[] input)
208 throw new InvalidParameterException("Wrong format for public keys");
212 * @param input the byte array to unmarshall into a valid RSA
213 * {@link PrivateKey} instance. MUST NOT be null.
214 * @return a new instance of a {@link GnuRSAPrivateKey} decoded from the
215 * <i>PrivateKeyInfo</i> material fed as <code>input</code>.
216 * @throw InvalidParameterException if an exception occurs during the
217 * unmarshalling process.
219 public PrivateKey decodePrivateKey(byte[] input)
221 if (Configuration.DEBUG)
222 log.entering(this.getClass().getName(), "decodePrivateKey()", input);
224 throw new InvalidParameterException("Input bytes MUST NOT be null");
226 BigInteger version, n, e, d, p, q, dP, dQ, qInv;
227 DERReader der = new DERReader(input);
230 DERValue derPKI = der.read();
231 DerUtil.checkIsConstructed(derPKI, "Wrong PrivateKeyInfo field");
233 DERValue derVersion = der.read();
234 DerUtil.checkIsBigInteger(derVersion, "Wrong Version field");
235 version = (BigInteger) derVersion.getValue();
236 if (version.compareTo(BigInteger.ZERO) != 0)
237 throw new InvalidParameterException("Unexpected Version: " + version);
239 DERValue derAlgoritmID = der.read();
240 DerUtil.checkIsConstructed(derAlgoritmID, "Wrong AlgorithmIdentifier field");
242 DERValue derOID = der.read();
243 OID algOID = (OID) derOID.getValue();
244 if (! algOID.equals(RSA_ALG_OID))
245 throw new InvalidParameterException("Unexpected OID: " + algOID);
247 // rfc-2459 states that this field is OPTIONAL but NULL if/when present
248 DERValue val = der.read();
249 if (val.getTag() == DER.NULL)
252 byte[] pkBytes = (byte[]) val.getValue();
253 der = new DERReader(pkBytes);
254 DERValue derRSAPrivateKey = der.read();
255 DerUtil.checkIsConstructed(derRSAPrivateKey, "Wrong RSAPrivateKey field");
258 DerUtil.checkIsBigInteger(val, "Wrong RSAPrivateKey Version field");
259 version = (BigInteger) val.getValue();
260 if (version.compareTo(BigInteger.ZERO) != 0)
261 throw new InvalidParameterException("Unexpected RSAPrivateKey Version: "
265 DerUtil.checkIsBigInteger(val, "Wrong modulus field");
266 n = (BigInteger) val.getValue();
268 DerUtil.checkIsBigInteger(val, "Wrong publicExponent field");
269 e = (BigInteger) val.getValue();
271 DerUtil.checkIsBigInteger(val, "Wrong privateExponent field");
272 d = (BigInteger) val.getValue();
274 DerUtil.checkIsBigInteger(val, "Wrong prime1 field");
275 p = (BigInteger) val.getValue();
277 DerUtil.checkIsBigInteger(val, "Wrong prime2 field");
278 q = (BigInteger) val.getValue();
280 DerUtil.checkIsBigInteger(val, "Wrong exponent1 field");
281 dP = (BigInteger) val.getValue();
283 DerUtil.checkIsBigInteger(val, "Wrong exponent2 field");
284 dQ = (BigInteger) val.getValue();
286 DerUtil.checkIsBigInteger(val, "Wrong coefficient field");
287 qInv = (BigInteger) val.getValue();
289 catch (IOException x)
291 InvalidParameterException y = new InvalidParameterException();
295 PrivateKey result = new GnuRSAPrivateKey(Registry.PKCS8_ENCODING_ID,
296 n, e, d, p, q, dP, dQ, qInv);
297 if (Configuration.DEBUG)
298 log.exiting(this.getClass().getName(), "decodePrivateKey()", result);