diff options
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/openssl')
24 files changed, 2171 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java b/pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java new file mode 100644 index 00000000..64ef73eb --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java @@ -0,0 +1,23 @@ +package org.spongycastle.openssl; + +public class EncryptionException + extends PEMException +{ + private Throwable cause; + + public EncryptionException(String msg) + { + super(msg); + } + + public EncryptionException(String msg, Throwable ex) + { + super(msg); + this.cause = ex; + } + + public Throwable getCause() + { + return cause; + } +}
\ No newline at end of file diff --git a/pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java b/pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java new file mode 100644 index 00000000..995bf7b5 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java @@ -0,0 +1,209 @@ +package org.spongycastle.openssl; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.cms.ContentInfo; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.cert.X509AttributeCertificateHolder; +import org.spongycastle.cert.X509CRLHolder; +import org.spongycastle.cert.X509CertificateHolder; +import org.spongycastle.pkcs.PKCS10CertificationRequest; +import org.spongycastle.util.Strings; +import org.spongycastle.util.io.pem.PemGenerationException; +import org.spongycastle.util.io.pem.PemHeader; +import org.spongycastle.util.io.pem.PemObject; +import org.spongycastle.util.io.pem.PemObjectGenerator; + +/** + * PEM generator for the original set of PEM objects used in Open SSL. + */ +public class MiscPEMGenerator + implements PemObjectGenerator +{ + private static final ASN1ObjectIdentifier[] dsaOids = + { + X9ObjectIdentifiers.id_dsa, + OIWObjectIdentifiers.dsaWithSHA1 + }; + + private static final byte[] hexEncodingTable = + { + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', + (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F' + }; + + private final Object obj; + private final PEMEncryptor encryptor; + + public MiscPEMGenerator(Object o) + { + this.obj = o; // use of this confuses some earlier JDKs. + this.encryptor = null; + } + + public MiscPEMGenerator(Object o, PEMEncryptor encryptor) + { + this.obj = o; + this.encryptor = encryptor; + } + + private PemObject createPemObject(Object o) + throws IOException + { + String type; + byte[] encoding; + + if (o instanceof PemObject) + { + return (PemObject)o; + } + if (o instanceof PemObjectGenerator) + { + return ((PemObjectGenerator)o).generate(); + } + if (o instanceof X509CertificateHolder) + { + type = "CERTIFICATE"; + + encoding = ((X509CertificateHolder)o).getEncoded(); + } + else if (o instanceof X509CRLHolder) + { + type = "X509 CRL"; + + encoding = ((X509CRLHolder)o).getEncoded(); + } + else if (o instanceof PrivateKeyInfo) + { + PrivateKeyInfo info = (PrivateKeyInfo)o; + ASN1ObjectIdentifier algOID = info.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOID.equals(PKCSObjectIdentifiers.rsaEncryption)) + { + type = "RSA PRIVATE KEY"; + + encoding = info.parsePrivateKey().toASN1Primitive().getEncoded(); + } + else if (algOID.equals(dsaOids[0]) || algOID.equals(dsaOids[1])) + { + type = "DSA PRIVATE KEY"; + + DSAParameter p = DSAParameter.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(0)); + v.add(new ASN1Integer(p.getP())); + v.add(new ASN1Integer(p.getQ())); + v.add(new ASN1Integer(p.getG())); + + BigInteger x = ASN1Integer.getInstance(info.parsePrivateKey()).getValue(); + BigInteger y = p.getG().modPow(x, p.getP()); + + v.add(new ASN1Integer(y)); + v.add(new ASN1Integer(x)); + + encoding = new DERSequence(v).getEncoded(); + } + else if (algOID.equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + type = "EC PRIVATE KEY"; + + encoding = info.parsePrivateKey().toASN1Primitive().getEncoded(); + } + else + { + throw new IOException("Cannot identify private key"); + } + } + else if (o instanceof SubjectPublicKeyInfo) + { + type = "PUBLIC KEY"; + + encoding = ((SubjectPublicKeyInfo)o).getEncoded(); + } + else if (o instanceof X509AttributeCertificateHolder) + { + type = "ATTRIBUTE CERTIFICATE"; + encoding = ((X509AttributeCertificateHolder)o).getEncoded(); + } + else if (o instanceof org.spongycastle.pkcs.PKCS10CertificationRequest) + { + type = "CERTIFICATE REQUEST"; + encoding = ((PKCS10CertificationRequest)o).getEncoded(); + } + else if (o instanceof ContentInfo) + { + type = "PKCS7"; + encoding = ((ContentInfo)o).getEncoded(); + } + else + { + throw new PemGenerationException("unknown object passed - can't encode."); + } + + if (encryptor != null) + { + String dekAlgName = Strings.toUpperCase(encryptor.getAlgorithm()); + + // Note: For backward compatibility + if (dekAlgName.equals("DESEDE")) + { + dekAlgName = "DES-EDE3-CBC"; + } + + + byte[] iv = encryptor.getIV(); + + byte[] encData = encryptor.encrypt(encoding); + + List headers = new ArrayList(2); + + headers.add(new PemHeader("Proc-Type", "4,ENCRYPTED")); + headers.add(new PemHeader("DEK-Info", dekAlgName + "," + getHexEncoded(iv))); + + return new PemObject(type, headers, encData); + } + return new PemObject(type, encoding); + } + + private String getHexEncoded(byte[] bytes) + throws IOException + { + char[] chars = new char[bytes.length * 2]; + + for (int i = 0; i != bytes.length; i++) + { + int v = bytes[i] & 0xff; + + chars[2 * i] = (char)(hexEncodingTable[(v >>> 4)]); + chars[2 * i + 1] = (char)(hexEncodingTable[v & 0xf]); + } + + return new String(chars); + } + + public PemObject generate() + throws PemGenerationException + { + try + { + return createPemObject(obj); + } + catch (IOException e) + { + throw new PemGenerationException("encoding exception: " + e.getMessage(), e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java new file mode 100644 index 00000000..3bd54df6 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java @@ -0,0 +1,7 @@ +package org.spongycastle.openssl; + +public interface PEMDecryptor +{ + byte[] decrypt(byte[] keyBytes, byte[] iv) + throws PEMException; +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java new file mode 100644 index 00000000..0d0b1e5c --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java @@ -0,0 +1,9 @@ +package org.spongycastle.openssl; + +import org.spongycastle.operator.OperatorCreationException; + +public interface PEMDecryptorProvider +{ + PEMDecryptor get(String dekAlgName) + throws OperatorCreationException; +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java new file mode 100644 index 00000000..4a04de14 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java @@ -0,0 +1,44 @@ +package org.spongycastle.openssl; + +import java.io.IOException; + +import org.spongycastle.operator.OperatorCreationException; + +public class PEMEncryptedKeyPair +{ + private final String dekAlgName; + private final byte[] iv; + private final byte[] keyBytes; + private final PEMKeyPairParser parser; + + PEMEncryptedKeyPair(String dekAlgName, byte[] iv, byte[] keyBytes, PEMKeyPairParser parser) + { + this.dekAlgName = dekAlgName; + this.iv = iv; + this.keyBytes = keyBytes; + this.parser = parser; + } + + public PEMKeyPair decryptKeyPair(PEMDecryptorProvider keyDecryptorProvider) + throws IOException + { + try + { + PEMDecryptor keyDecryptor = keyDecryptorProvider.get(dekAlgName); + + return parser.parse(keyDecryptor.decrypt(keyBytes, iv)); + } + catch (IOException e) + { + throw e; + } + catch (OperatorCreationException e) + { + throw new PEMException("cannot create extraction operator: " + e.getMessage(), e); + } + catch (Exception e) + { + throw new PEMException("exception processing key pair: " + e.getMessage(), e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java new file mode 100644 index 00000000..63b42b2d --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java @@ -0,0 +1,11 @@ +package org.spongycastle.openssl; + +public interface PEMEncryptor +{ + String getAlgorithm(); + + byte[] getIV(); + + byte[] encrypt(byte[] encoding) + throws PEMException; +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMException.java b/pkix/src/main/java/org/spongycastle/openssl/PEMException.java new file mode 100644 index 00000000..5df7b5f5 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMException.java @@ -0,0 +1,34 @@ +package org.spongycastle.openssl; + +import java.io.IOException; + +public class PEMException + extends IOException +{ + Exception underlying; + + public PEMException( + String message) + { + super(message); + } + + public PEMException( + String message, + Exception underlying) + { + super(message); + this.underlying = underlying; + } + + public Exception getUnderlyingException() + { + return underlying; + } + + + public Throwable getCause() + { + return underlying; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java new file mode 100644 index 00000000..ad010634 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java @@ -0,0 +1,26 @@ +package org.spongycastle.openssl; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; + +public class PEMKeyPair +{ + private final SubjectPublicKeyInfo publicKeyInfo; + private final PrivateKeyInfo privateKeyInfo; + + public PEMKeyPair(SubjectPublicKeyInfo publicKeyInfo, PrivateKeyInfo privateKeyInfo) + { + this.publicKeyInfo = publicKeyInfo; + this.privateKeyInfo = privateKeyInfo; + } + + public PrivateKeyInfo getPrivateKeyInfo() + { + return privateKeyInfo; + } + + public SubjectPublicKeyInfo getPublicKeyInfo() + { + return publicKeyInfo; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java new file mode 100644 index 00000000..32583fd9 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java @@ -0,0 +1,9 @@ +package org.spongycastle.openssl; + +import java.io.IOException; + +interface PEMKeyPairParser +{ + PEMKeyPair parse(byte[] encoding) + throws IOException; +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java b/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java new file mode 100644 index 00000000..bafe2126 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java @@ -0,0 +1,510 @@ +package org.spongycastle.openssl; + +import java.io.IOException; +import java.io.Reader; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.cms.ContentInfo; +import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.pkcs.RSAPublicKey; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.DSAParameter; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.cert.X509AttributeCertificateHolder; +import org.spongycastle.cert.X509CRLHolder; +import org.spongycastle.cert.X509CertificateHolder; +import org.spongycastle.pkcs.PKCS10CertificationRequest; +import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.io.pem.PemHeader; +import org.spongycastle.util.io.pem.PemObject; +import org.spongycastle.util.io.pem.PemObjectParser; +import org.spongycastle.util.io.pem.PemReader; + +/** + * Class for parsing OpenSSL PEM encoded streams containing + * X509 certificates, PKCS8 encoded keys and PKCS7 objects. + * <p> + * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Public keys will be returned as + * well formed SubjectPublicKeyInfo objects, private keys will be returned as well formed PrivateKeyInfo objects. In the + * case of a private key a PEMKeyPair will normally be returned if the encoding contains both the private and public + * key definition. CRLs, Certificates, PKCS#10 requests, and Attribute Certificates will generate the appropriate BC holder class. + * </p> + */ +public class PEMParser + extends PemReader +{ + private final Map parsers = new HashMap(); + + /** + * Create a new PEMReader + * + * @param reader the Reader + */ + public PEMParser( + Reader reader) + { + super(reader); + + parsers.put("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser()); + parsers.put("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser()); + parsers.put("CERTIFICATE", new X509CertificateParser()); + parsers.put("TRUSTED CERTIFICATE", new X509CertificateParser()); + parsers.put("X509 CERTIFICATE", new X509CertificateParser()); + parsers.put("X509 CRL", new X509CRLParser()); + parsers.put("PKCS7", new PKCS7Parser()); + parsers.put("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser()); + parsers.put("EC PARAMETERS", new ECCurveParamsParser()); + parsers.put("PUBLIC KEY", new PublicKeyParser()); + parsers.put("RSA PUBLIC KEY", new RSAPublicKeyParser()); + parsers.put("RSA PRIVATE KEY", new KeyPairParser(new RSAKeyPairParser())); + parsers.put("DSA PRIVATE KEY", new KeyPairParser(new DSAKeyPairParser())); + parsers.put("EC PRIVATE KEY", new KeyPairParser(new ECDSAKeyPairParser())); + parsers.put("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser()); + parsers.put("PRIVATE KEY", new PrivateKeyParser()); + } + + public Object readObject() + throws IOException + { + PemObject obj = readPemObject(); + + if (obj != null) + { + String type = obj.getType(); + if (parsers.containsKey(type)) + { + return ((PemObjectParser)parsers.get(type)).parseObject(obj); + } + else + { + throw new IOException("unrecognised object: " + type); + } + } + + return null; + } + + private class KeyPairParser + implements PemObjectParser + { + private final PEMKeyPairParser pemKeyPairParser; + + public KeyPairParser(PEMKeyPairParser pemKeyPairParser) + { + this.pemKeyPairParser = pemKeyPairParser; + } + + /** + * Read a Key Pair + */ + public Object parseObject( + PemObject obj) + throws IOException + { + boolean isEncrypted = false; + String dekInfo = null; + List headers = obj.getHeaders(); + + for (Iterator it = headers.iterator(); it.hasNext();) + { + PemHeader hdr = (PemHeader)it.next(); + + if (hdr.getName().equals("Proc-Type") && hdr.getValue().equals("4,ENCRYPTED")) + { + isEncrypted = true; + } + else if (hdr.getName().equals("DEK-Info")) + { + dekInfo = hdr.getValue(); + } + } + + // + // extract the key + // + byte[] keyBytes = obj.getContent(); + + try + { + if (isEncrypted) + { + StringTokenizer tknz = new StringTokenizer(dekInfo, ","); + String dekAlgName = tknz.nextToken(); + byte[] iv = Hex.decode(tknz.nextToken()); + + return new PEMEncryptedKeyPair(dekAlgName, iv, keyBytes, pemKeyPairParser); + } + + return pemKeyPairParser.parse(keyBytes); + } + catch (IOException e) + { + if (isEncrypted) + { + throw new PEMException("exception decoding - please check password and data.", e); + } + else + { + throw new PEMException(e.getMessage(), e); + } + } + catch (IllegalArgumentException e) + { + if (isEncrypted) + { + throw new PEMException("exception decoding - please check password and data.", e); + } + else + { + throw new PEMException(e.getMessage(), e); + } + } + } + } + + private class DSAKeyPairParser + implements PEMKeyPairParser + { + public PEMKeyPair parse(byte[] encoding) + throws IOException + { + try + { + ASN1Sequence seq = ASN1Sequence.getInstance(encoding); + + if (seq.size() != 6) + { + throw new PEMException("malformed sequence in DSA private key"); + } + + // ASN1Integer v = (ASN1Integer)seq.getObjectAt(0); + ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(1)); + ASN1Integer q = ASN1Integer.getInstance(seq.getObjectAt(2)); + ASN1Integer g = ASN1Integer.getInstance(seq.getObjectAt(3)); + ASN1Integer y = ASN1Integer.getInstance(seq.getObjectAt(4)); + ASN1Integer x = ASN1Integer.getInstance(seq.getObjectAt(5)); + + return new PEMKeyPair( + new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(p.getValue(), q.getValue(), g.getValue())), y), + new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(p.getValue(), q.getValue(), g.getValue())), x)); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new PEMException( + "problem creating DSA private key: " + e.toString(), e); + } + } + } + + private class ECDSAKeyPairParser + implements PEMKeyPairParser + { + public PEMKeyPair parse(byte[] encoding) + throws IOException + { + try + { + ASN1Sequence seq = ASN1Sequence.getInstance(encoding); + + org.spongycastle.asn1.sec.ECPrivateKey pKey = org.spongycastle.asn1.sec.ECPrivateKey.getInstance(seq); + AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters()); + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey); + SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes()); + + return new PEMKeyPair(pubInfo, privInfo); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new PEMException( + "problem creating EC private key: " + e.toString(), e); + } + } + } + + private class RSAKeyPairParser + implements PEMKeyPairParser + { + public PEMKeyPair parse(byte[] encoding) + throws IOException + { + try + { + ASN1Sequence seq = ASN1Sequence.getInstance(encoding); + + if (seq.size() != 9) + { + throw new PEMException("malformed sequence in RSA private key"); + } + + org.spongycastle.asn1.pkcs.RSAPrivateKey keyStruct = org.spongycastle.asn1.pkcs.RSAPrivateKey.getInstance(seq); + + RSAPublicKey pubSpec = new RSAPublicKey( + keyStruct.getModulus(), keyStruct.getPublicExponent()); + + AlgorithmIdentifier algId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + + return new PEMKeyPair(new SubjectPublicKeyInfo(algId, pubSpec), new PrivateKeyInfo(algId, keyStruct)); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new PEMException( + "problem creating RSA private key: " + e.toString(), e); + } + } + } + + private class PublicKeyParser + implements PemObjectParser + { + public PublicKeyParser() + { + } + + public Object parseObject(PemObject obj) + throws IOException + { + return SubjectPublicKeyInfo.getInstance(obj.getContent()); + } + } + + private class RSAPublicKeyParser + implements PemObjectParser + { + public RSAPublicKeyParser() + { + } + + public Object parseObject(PemObject obj) + throws IOException + { + try + { + RSAPublicKey rsaPubStructure = RSAPublicKey.getInstance(obj.getContent()); + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), rsaPubStructure); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new PEMException("problem extracting key: " + e.toString(), e); + } + } + } + + private class X509CertificateParser + implements PemObjectParser + { + /** + * Reads in a X509Certificate. + * + * @return the X509Certificate + * @throws java.io.IOException if an I/O error occured + */ + public Object parseObject(PemObject obj) + throws IOException + { + try + { + return new X509CertificateHolder(obj.getContent()); + } + catch (Exception e) + { + throw new PEMException("problem parsing cert: " + e.toString(), e); + } + } + } + + private class X509CRLParser + implements PemObjectParser + { + /** + * Reads in a X509CRL. + * + * @return the X509Certificate + * @throws java.io.IOException if an I/O error occured + */ + public Object parseObject(PemObject obj) + throws IOException + { + try + { + return new X509CRLHolder(obj.getContent()); + } + catch (Exception e) + { + throw new PEMException("problem parsing cert: " + e.toString(), e); + } + } + } + + private class PKCS10CertificationRequestParser + implements PemObjectParser + { + /** + * Reads in a PKCS10 certification request. + * + * @return the certificate request. + * @throws java.io.IOException if an I/O error occured + */ + public Object parseObject(PemObject obj) + throws IOException + { + try + { + return new PKCS10CertificationRequest(obj.getContent()); + } + catch (Exception e) + { + throw new PEMException("problem parsing certrequest: " + e.toString(), e); + } + } + } + + private class PKCS7Parser + implements PemObjectParser + { + /** + * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS + * API. + * + * @return the X509Certificate + * @throws java.io.IOException if an I/O error occured + */ + public Object parseObject(PemObject obj) + throws IOException + { + try + { + ASN1InputStream aIn = new ASN1InputStream(obj.getContent()); + + return ContentInfo.getInstance(aIn.readObject()); + } + catch (Exception e) + { + throw new PEMException("problem parsing PKCS7 object: " + e.toString(), e); + } + } + } + + private class X509AttributeCertificateParser + implements PemObjectParser + { + public Object parseObject(PemObject obj) + throws IOException + { + return new X509AttributeCertificateHolder(obj.getContent()); + } + } + + private class ECCurveParamsParser + implements PemObjectParser + { + public Object parseObject(PemObject obj) + throws IOException + { + try + { + Object param = ASN1Primitive.fromByteArray(obj.getContent()); + + if (param instanceof ASN1ObjectIdentifier) + { + return ASN1Primitive.fromByteArray(obj.getContent()); + } + else if (param instanceof ASN1Sequence) + { + return X9ECParameters.getInstance(param); + } + else + { + return null; // implicitly CA + } + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new PEMException("exception extracting EC named curve: " + e.toString()); + } + } + } + + private class EncryptedPrivateKeyParser + implements PemObjectParser + { + public EncryptedPrivateKeyParser() + { + } + + /** + * Reads in an EncryptedPrivateKeyInfo + * + * @return the X509Certificate + * @throws java.io.IOException if an I/O error occured + */ + public Object parseObject(PemObject obj) + throws IOException + { + try + { + return new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(obj.getContent())); + } + catch (Exception e) + { + throw new PEMException("problem parsing ENCRYPTED PRIVATE KEY: " + e.toString(), e); + } + } + } + + private class PrivateKeyParser + implements PemObjectParser + { + public PrivateKeyParser() + { + } + + public Object parseObject(PemObject obj) + throws IOException + { + try + { + return PrivateKeyInfo.getInstance(obj.getContent()); + } + catch (Exception e) + { + throw new PEMException("problem parsing PRIVATE KEY: " + e.toString(), e); + } + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java b/pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java new file mode 100644 index 00000000..e8fec527 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java @@ -0,0 +1,64 @@ +package org.spongycastle.openssl; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.util.Integers; + +public final class PEMUtilities +{ + private static final Map KEYSIZES = new HashMap(); + private static final Set PKCS5_SCHEME_1 = new HashSet(); + private static final Set PKCS5_SCHEME_2 = new HashSet(); + + static + { + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC); + + PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2); + PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC); + + KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256)); + } + + static int getKeySize(String algorithm) + { + if (!KEYSIZES.containsKey(algorithm)) + { + throw new IllegalStateException("no key size for algorithm: " + algorithm); + } + + return ((Integer)KEYSIZES.get(algorithm)).intValue(); + } + + static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid) + { + return PKCS5_SCHEME_1.contains(algOid); + } + + public static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid) + { + return PKCS5_SCHEME_2.contains(algOid); + } + + public static boolean isPKCS12(ASN1ObjectIdentifier algOid) + { + return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId()); + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java b/pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java new file mode 100644 index 00000000..379c014d --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java @@ -0,0 +1,69 @@ +package org.spongycastle.openssl; + +import java.io.IOException; +import java.io.Writer; + +import org.spongycastle.openssl.jcajce.JcaMiscPEMGenerator; +import org.spongycastle.util.io.pem.PemGenerationException; +import org.spongycastle.util.io.pem.PemObjectGenerator; +import org.spongycastle.util.io.pem.PemWriter; + +/** + * General purpose writer for OpenSSL PEM objects. + * @deprecated use JcaPEMWriter + */ +public class PEMWriter + extends PemWriter +{ + /** + * Base constructor. + * + * @param out output stream to use. + */ + public PEMWriter(Writer out) + { + super(out); + } + + /** + * @throws IOException + */ + public void writeObject( + Object obj) + throws IOException + { + writeObject(obj, null); + } + + /** + * @param obj + * @param encryptor + * @throws IOException + */ + public void writeObject( + Object obj, + PEMEncryptor encryptor) + throws IOException + { + try + { + super.writeObject(new JcaMiscPEMGenerator(obj, encryptor)); + } + catch (PemGenerationException e) + { + if (e.getCause() instanceof IOException) + { + throw (IOException)e.getCause(); + } + + throw e; + } + } + + public void writeObject( + PemObjectGenerator obj) + throws IOException + { + super.writeObject(obj); + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java b/pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java new file mode 100644 index 00000000..83130d36 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java @@ -0,0 +1,87 @@ +package org.spongycastle.openssl; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.operator.OutputEncryptor; +import org.spongycastle.util.io.pem.PemGenerationException; +import org.spongycastle.util.io.pem.PemObject; +import org.spongycastle.util.io.pem.PemObjectGenerator; + +public class PKCS8Generator + implements PemObjectGenerator +{ + public static final ASN1ObjectIdentifier AES_128_CBC = NISTObjectIdentifiers.id_aes128_CBC; + public static final ASN1ObjectIdentifier AES_192_CBC = NISTObjectIdentifiers.id_aes192_CBC; + public static final ASN1ObjectIdentifier AES_256_CBC = NISTObjectIdentifiers.id_aes256_CBC; + + public static final ASN1ObjectIdentifier DES3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC; + + public static final ASN1ObjectIdentifier PBE_SHA1_RC4_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4; + public static final ASN1ObjectIdentifier PBE_SHA1_RC4_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4; + public static final ASN1ObjectIdentifier PBE_SHA1_3DES = PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC; + public static final ASN1ObjectIdentifier PBE_SHA1_2DES = PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC; + public static final ASN1ObjectIdentifier PBE_SHA1_RC2_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC; + public static final ASN1ObjectIdentifier PBE_SHA1_RC2_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC; + + private PrivateKeyInfo key; + private OutputEncryptor outputEncryptor; + + /** + * Base constructor. + */ + public PKCS8Generator(PrivateKeyInfo key, OutputEncryptor outputEncryptor) + { + this.key = key; + this.outputEncryptor = outputEncryptor; + } + + public PemObject generate() + throws PemGenerationException + { + if (outputEncryptor != null) + { + return generate(key, outputEncryptor); + } + else + { + return generate(key, null); + } + } + + private PemObject generate(PrivateKeyInfo key, OutputEncryptor encryptor) + throws PemGenerationException + { + try + { + byte[] keyData = key.getEncoded(); + + if (encryptor == null) + { + return new PemObject("PRIVATE KEY", keyData); + } + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OutputStream cOut = encryptor.getOutputStream(bOut); + + cOut.write(key.getEncoded()); + + cOut.close(); + + EncryptedPrivateKeyInfo info = new EncryptedPrivateKeyInfo(encryptor.getAlgorithmIdentifier(), bOut.toByteArray()); + + return new PemObject("ENCRYPTED PRIVATE KEY", info.getEncoded()); + } + catch (IOException e) + { + throw new PemGenerationException("unable to process encoded key data: " + e.getMessage(), e); + } + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PasswordException.java b/pkix/src/main/java/org/spongycastle/openssl/PasswordException.java new file mode 100644 index 00000000..68de3213 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PasswordException.java @@ -0,0 +1,10 @@ +package org.spongycastle.openssl; + +public class PasswordException + extends PEMException +{ + public PasswordException(String msg) + { + super(msg); + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java b/pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java new file mode 100644 index 00000000..eb981fd5 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java @@ -0,0 +1,9 @@ +package org.spongycastle.openssl; + +/** + * call back to allow a password to be fetched when one is requested. + */ +public interface PasswordFinder +{ + public char[] getPassword(); +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java new file mode 100644 index 00000000..88f2a32e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java @@ -0,0 +1,98 @@ +package org.spongycastle.openssl.jcajce; + +import java.io.IOException; +import java.security.Key; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.cert.jcajce.JcaX509AttributeCertificateHolder; +import org.spongycastle.cert.jcajce.JcaX509CRLHolder; +import org.spongycastle.cert.jcajce.JcaX509CertificateHolder; +import org.spongycastle.jce.PKCS10CertificationRequest; +import org.spongycastle.openssl.MiscPEMGenerator; +import org.spongycastle.openssl.PEMEncryptor; +import org.spongycastle.x509.X509AttributeCertificate; +import org.spongycastle.x509.X509V2AttributeCertificate; + +/** + * PEM generator for the original set of PEM objects used in Open SSL. + */ +public class JcaMiscPEMGenerator + extends MiscPEMGenerator +{ + private Object obj; + private String algorithm; + private char[] password; + private SecureRandom random; + private Provider provider; + + public JcaMiscPEMGenerator(Object o) + throws IOException + { + super(convertObject(o)); + } + + public JcaMiscPEMGenerator(Object o, PEMEncryptor encryptor) + throws IOException + { + super(convertObject(o), encryptor); + } + + private static Object convertObject(Object o) + throws IOException + { + if (o instanceof X509Certificate) + { + try + { + return new JcaX509CertificateHolder((X509Certificate)o); + } + catch (CertificateEncodingException e) + { + throw new IllegalArgumentException("Cannot encode object: " + e.toString()); + } + } + else if (o instanceof X509CRL) + { + try + { + return new JcaX509CRLHolder((X509CRL)o); + } + catch (CRLException e) + { + throw new IllegalArgumentException("Cannot encode object: " + e.toString()); + } + } + else if (o instanceof KeyPair) + { + return convertObject(((KeyPair)o).getPrivate()); + } + else if (o instanceof PrivateKey) + { + return PrivateKeyInfo.getInstance(((Key)o).getEncoded()); + } + else if (o instanceof PublicKey) + { + return SubjectPublicKeyInfo.getInstance(((PublicKey)o).getEncoded()); + } + else if (o instanceof X509AttributeCertificate) + { + return new JcaX509AttributeCertificateHolder((X509V2AttributeCertificate)o); + } + else if (o instanceof PKCS10CertificationRequest) + { + return new org.spongycastle.pkcs.PKCS10CertificationRequest(((PKCS10CertificationRequest)o).getEncoded()); + } + + return o; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java new file mode 100644 index 00000000..1b15639a --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java @@ -0,0 +1,115 @@ +package org.spongycastle.openssl.jcajce; + +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.openssl.PEMException; +import org.spongycastle.openssl.PEMKeyPair; + +public class JcaPEMKeyConverter +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + + private static final Map algorithms = new HashMap(); + + static + { + algorithms.put(X9ObjectIdentifiers.id_ecPublicKey, "ECDSA"); + algorithms.put(PKCSObjectIdentifiers.rsaEncryption, "RSA"); + algorithms.put(X9ObjectIdentifiers.id_dsa, "DSA"); + } + + public JcaPEMKeyConverter setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcaPEMKeyConverter setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public KeyPair getKeyPair(PEMKeyPair keyPair) + throws PEMException + { + try + { + KeyFactory keyFactory = getKeyFactory(keyPair.getPrivateKeyInfo().getPrivateKeyAlgorithm()); + + return new KeyPair(keyFactory.generatePublic(new X509EncodedKeySpec(keyPair.getPublicKeyInfo().getEncoded())), + keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyPair.getPrivateKeyInfo().getEncoded()))); + } + catch (Exception e) + { + throw new PEMException("unable to convert key pair: " + e.getMessage(), e); + } + } + + public PublicKey getPublicKey(SubjectPublicKeyInfo publicKeyInfo) + throws PEMException + { + try + { + KeyFactory keyFactory = getKeyFactory(publicKeyInfo.getAlgorithm()); + + return keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded())); + } + catch (Exception e) + { + throw new PEMException("unable to convert key pair: " + e.getMessage(), e); + } + } + + public PrivateKey getPrivateKey(PrivateKeyInfo privateKeyInfo) + throws PEMException + { + try + { + KeyFactory keyFactory = getKeyFactory(privateKeyInfo.getPrivateKeyAlgorithm()); + + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded())); + } + catch (Exception e) + { + throw new PEMException("unable to convert key pair: " + e.getMessage(), e); + } + } + + private KeyFactory getKeyFactory(AlgorithmIdentifier algId) + throws NoSuchAlgorithmException, NoSuchProviderException + { + ASN1ObjectIdentifier algorithm = algId.getAlgorithm(); + + String algName = (String)algorithms.get(algorithm); + + if (algName == null) + { + algName = algorithm.getId(); + } + + return helper.createKeyFactory(algName); + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java new file mode 100644 index 00000000..0224b89e --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java @@ -0,0 +1,68 @@ +package org.spongycastle.openssl.jcajce; + +import java.io.IOException; +import java.io.Writer; + +import org.spongycastle.openssl.PEMEncryptor; +import org.spongycastle.util.io.pem.PemGenerationException; +import org.spongycastle.util.io.pem.PemObjectGenerator; +import org.spongycastle.util.io.pem.PemWriter; + +/** + * General purpose writer for OpenSSL PEM objects based on JCA/JCE classes. + */ +public class JcaPEMWriter + extends PemWriter +{ + /** + * Base constructor. + * + * @param out output stream to use. + */ + public JcaPEMWriter(Writer out) + { + super(out); + } + + /** + * @throws java.io.IOException + */ + public void writeObject( + Object obj) + throws IOException + { + writeObject(obj, null); + } + + /** + * @param obj + * @param encryptor + * @throws java.io.IOException + */ + public void writeObject( + Object obj, + PEMEncryptor encryptor) + throws IOException + { + try + { + super.writeObject(new JcaMiscPEMGenerator(obj, encryptor)); + } + catch (PemGenerationException e) + { + if (e.getCause() instanceof IOException) + { + throw (IOException)e.getCause(); + } + + throw e; + } + } + + public void writeObject( + PemObjectGenerator obj) + throws IOException + { + super.writeObject(obj); + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java new file mode 100644 index 00000000..9c4e4f46 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java @@ -0,0 +1,18 @@ +package org.spongycastle.openssl.jcajce; + +import java.security.PrivateKey; + +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.openssl.PKCS8Generator; +import org.spongycastle.operator.OutputEncryptor; +import org.spongycastle.util.io.pem.PemGenerationException; + +public class JcaPKCS8Generator + extends PKCS8Generator +{ + public JcaPKCS8Generator(PrivateKey key, OutputEncryptor encryptor) + throws PemGenerationException + { + super(PrivateKeyInfo.getInstance(key.getEncoded()), encryptor); + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java new file mode 100644 index 00000000..5050f4b2 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java @@ -0,0 +1,141 @@ +package org.spongycastle.openssl.jcajce; + +import java.io.IOException; +import java.io.InputStream; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.Provider; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.pkcs.EncryptionScheme; +import org.spongycastle.asn1.pkcs.KeyDerivationFunc; +import org.spongycastle.asn1.pkcs.PBEParameter; +import org.spongycastle.asn1.pkcs.PBES2Parameters; +import org.spongycastle.asn1.pkcs.PBKDF2Params; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.openssl.PEMException; +import org.spongycastle.operator.InputDecryptor; +import org.spongycastle.operator.InputDecryptorProvider; +import org.spongycastle.operator.OperatorCreationException; + +public class JceOpenSSLPKCS8DecryptorProviderBuilder +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + + public JceOpenSSLPKCS8DecryptorProviderBuilder() + { + helper = new DefaultJcaJceHelper(); + } + + public JceOpenSSLPKCS8DecryptorProviderBuilder setProvider(String providerName) + { + helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public JceOpenSSLPKCS8DecryptorProviderBuilder setProvider(Provider provider) + { + helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public InputDecryptorProvider build(final char[] password) + throws OperatorCreationException + { + return new InputDecryptorProvider() + { + public InputDecryptor get(final AlgorithmIdentifier algorithm) + throws OperatorCreationException + { + final Cipher cipher; + + try + { + if (PEMUtilities.isPKCS5Scheme2(algorithm.getAlgorithm())) + { + PBES2Parameters params = PBES2Parameters.getInstance(algorithm.getParameters()); + KeyDerivationFunc func = params.getKeyDerivationFunc(); + EncryptionScheme scheme = params.getEncryptionScheme(); + PBKDF2Params defParams = (PBKDF2Params)func.getParameters(); + + int iterationCount = defParams.getIterationCount().intValue(); + byte[] salt = defParams.getSalt(); + + String oid = scheme.getAlgorithm().getId(); + + SecretKey key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(oid, password, salt, iterationCount); + + cipher = helper.createCipher(oid); + AlgorithmParameters algParams = helper.createAlgorithmParameters(oid); + + algParams.init(scheme.getParameters().toASN1Primitive().getEncoded()); + + cipher.init(Cipher.DECRYPT_MODE, key, algParams); + } + else if (PEMUtilities.isPKCS12(algorithm.getAlgorithm())) + { + PKCS12PBEParams params = PKCS12PBEParams.getInstance(algorithm.getParameters()); + PBEKeySpec pbeSpec = new PBEKeySpec(password); + + SecretKeyFactory secKeyFact = helper.createSecretKeyFactory(algorithm.getAlgorithm().getId()); + PBEParameterSpec defParams = new PBEParameterSpec(params.getIV(), params.getIterations().intValue()); + + cipher = helper.createCipher(algorithm.getAlgorithm().getId()); + + cipher.init(Cipher.DECRYPT_MODE, secKeyFact.generateSecret(pbeSpec), defParams); + } + else if (PEMUtilities.isPKCS5Scheme1(algorithm.getAlgorithm())) + { + PBEParameter params = PBEParameter.getInstance(algorithm.getParameters()); + PBEKeySpec pbeSpec = new PBEKeySpec(password); + + SecretKeyFactory secKeyFact = helper.createSecretKeyFactory(algorithm.getAlgorithm().getId()); + PBEParameterSpec defParams = new PBEParameterSpec(params.getSalt(), params.getIterationCount().intValue()); + + cipher = helper.createCipher(algorithm.getAlgorithm().getId()); + + cipher.init(Cipher.DECRYPT_MODE, secKeyFact.generateSecret(pbeSpec), defParams); + } + else + { + throw new PEMException("Unknown algorithm: " + algorithm.getAlgorithm()); + } + + return new InputDecryptor() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algorithm; + } + + public InputStream getInputStream(InputStream encIn) + { + return new CipherInputStream(encIn, cipher); + } + }; + } + catch (IOException e) + { + throw new OperatorCreationException(algorithm.getAlgorithm() + " not available: " + e.getMessage(), e); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException(algorithm.getAlgorithm() + " not available: " + e.getMessage(), e); + } + }; + }; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java new file mode 100644 index 00000000..8404661f --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java @@ -0,0 +1,221 @@ +package org.spongycastle.openssl.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.AlgorithmParameterGenerator; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.KeyDerivationFunc; +import org.spongycastle.asn1.pkcs.PBES2Parameters; +import org.spongycastle.asn1.pkcs.PBKDF2Params; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.operator.GenericKey; +import org.spongycastle.operator.OperatorCreationException; +import org.spongycastle.operator.OutputEncryptor; +import org.spongycastle.operator.jcajce.JceGenericKey; + +public class JceOpenSSLPKCS8EncryptorBuilder +{ + public static final String AES_128_CBC = NISTObjectIdentifiers.id_aes128_CBC.getId(); + public static final String AES_192_CBC = NISTObjectIdentifiers.id_aes192_CBC.getId(); + public static final String AES_256_CBC = NISTObjectIdentifiers.id_aes256_CBC.getId(); + + public static final String DES3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC.getId(); + + public static final String PBE_SHA1_RC4_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4.getId(); + public static final String PBE_SHA1_RC4_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4.getId(); + public static final String PBE_SHA1_3DES = PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC.getId(); + public static final String PBE_SHA1_2DES = PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC.getId(); + public static final String PBE_SHA1_RC2_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC.getId(); + public static final String PBE_SHA1_RC2_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC.getId(); + + private JcaJceHelper helper = new DefaultJcaJceHelper(); + + private AlgorithmParameters params; + private ASN1ObjectIdentifier algOID; + byte[] salt; + int iterationCount; + private Cipher cipher; + private SecureRandom random; + private AlgorithmParameterGenerator paramGen; + private SecretKeyFactory secKeyFact; + private char[] password; + + private SecretKey key; + + public JceOpenSSLPKCS8EncryptorBuilder(ASN1ObjectIdentifier algorithm) + { + algOID = algorithm; + + this.iterationCount = 2048; + } + + public JceOpenSSLPKCS8EncryptorBuilder setRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public JceOpenSSLPKCS8EncryptorBuilder setPasssword(char[] password) + { + this.password = password; + + return this; + } + + public JceOpenSSLPKCS8EncryptorBuilder setIterationCount(int iterationCount) + { + this.iterationCount = iterationCount; + + return this; + } + + public JceOpenSSLPKCS8EncryptorBuilder setProvider(String providerName) + { + helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public JceOpenSSLPKCS8EncryptorBuilder setProvider(Provider provider) + { + helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public OutputEncryptor build() + throws OperatorCreationException + { + final AlgorithmIdentifier algID; + + salt = new byte[20]; + + if (random == null) + { + random = new SecureRandom(); + } + + random.nextBytes(salt); + + try + { + this.cipher = helper.createCipher(algOID.getId()); + + if (PEMUtilities.isPKCS5Scheme2(algOID)) + { + this.paramGen = helper.createAlgorithmParameterGenerator(algOID.getId()); + } + else + { + this.secKeyFact = helper.createSecretKeyFactory(algOID.getId()); + } + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException(algOID + " not available: " + e.getMessage(), e); + } + + if (PEMUtilities.isPKCS5Scheme2(algOID)) + { + params = paramGen.generateParameters(); + + try + { + KeyDerivationFunc scheme = new KeyDerivationFunc(algOID, ASN1Primitive.fromByteArray(params.getEncoded())); + KeyDerivationFunc func = new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount)); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(func); + v.add(scheme); + + algID = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, PBES2Parameters.getInstance(new DERSequence(v))); + } + catch (IOException e) + { + throw new OperatorCreationException(e.getMessage(), e); + } + + key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(algOID.getId(), password, salt, iterationCount); + + try + { + cipher.init(Cipher.ENCRYPT_MODE, key, params); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException(e.getMessage(), e); + } + } + else if (PEMUtilities.isPKCS12(algOID)) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new DEROctetString(salt)); + v.add(new ASN1Integer(iterationCount)); + + algID = new AlgorithmIdentifier(algOID, PKCS12PBEParams.getInstance(new DERSequence(v))); + + try + { + PBEKeySpec pbeSpec = new PBEKeySpec(password); + PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount); + + key = secKeyFact.generateSecret(pbeSpec); + + cipher.init(Cipher.ENCRYPT_MODE, key, defParams); + } + catch (GeneralSecurityException e) + { + throw new OperatorCreationException(e.getMessage(), e); + } + } + else + { + throw new OperatorCreationException("unknown algorithm: " + algOID, null); + } + + return new OutputEncryptor() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return algID; + } + + public OutputStream getOutputStream(OutputStream encOut) + { + return new CipherOutputStream(encOut, cipher); + } + + public GenericKey getKey() + { + return new JceGenericKey(algID, key); + } + }; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java new file mode 100644 index 00000000..b54d7fd3 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java @@ -0,0 +1,54 @@ +package org.spongycastle.openssl.jcajce; + +import java.security.Provider; + +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.openssl.PEMDecryptor; +import org.spongycastle.openssl.PEMDecryptorProvider; +import org.spongycastle.openssl.PEMException; +import org.spongycastle.openssl.PasswordException; + +public class JcePEMDecryptorProviderBuilder +{ + private JcaJceHelper helper = new DefaultJcaJceHelper(); + + public JcePEMDecryptorProviderBuilder setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcePEMDecryptorProviderBuilder setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public PEMDecryptorProvider build(final char[] password) + { + return new PEMDecryptorProvider() + { + public PEMDecryptor get(final String dekAlgName) + { + return new PEMDecryptor() + { + public byte[] decrypt(byte[] keyBytes, byte[] iv) + throws PEMException + { + if (password == null) + { + throw new PasswordException("Password is null, but a password is required"); + } + + return PEMUtilities.crypt(false, helper, keyBytes, password, dekAlgName, iv); + } + }; + } + }; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java new file mode 100644 index 00000000..3360f8a8 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java @@ -0,0 +1,78 @@ +package org.spongycastle.openssl.jcajce; + +import java.security.Provider; +import java.security.SecureRandom; + +import org.spongycastle.jcajce.util.DefaultJcaJceHelper; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.jcajce.util.NamedJcaJceHelper; +import org.spongycastle.jcajce.util.ProviderJcaJceHelper; +import org.spongycastle.openssl.PEMEncryptor; +import org.spongycastle.openssl.PEMException; + +public class JcePEMEncryptorBuilder +{ + private final String algorithm; + + private JcaJceHelper helper = new DefaultJcaJceHelper(); + private SecureRandom random; + + public JcePEMEncryptorBuilder(String algorithm) + { + this.algorithm = algorithm; + } + + public JcePEMEncryptorBuilder setProvider(Provider provider) + { + this.helper = new ProviderJcaJceHelper(provider); + + return this; + } + + public JcePEMEncryptorBuilder setProvider(String providerName) + { + this.helper = new NamedJcaJceHelper(providerName); + + return this; + } + + public JcePEMEncryptorBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public PEMEncryptor build(final char[] password) + { + if (random == null) + { + random = new SecureRandom(); + } + + int ivLength = algorithm.startsWith("AES-") ? 16 : 8; + + final byte[] iv = new byte[ivLength]; + + random.nextBytes(iv); + + return new PEMEncryptor() + { + public String getAlgorithm() + { + return algorithm; + } + + public byte[] getIV() + { + return iv; + } + + public byte[] encrypt(byte[] encoding) + throws PEMException + { + return PEMUtilities.crypt(true, helper, encoding, password, algorithm, iv); + } + }; + } +} diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java new file mode 100644 index 00000000..6bfa2a57 --- /dev/null +++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java @@ -0,0 +1,257 @@ +package org.spongycastle.openssl.jcajce; + +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.crypto.PBEParametersGenerator; +import org.spongycastle.crypto.generators.OpenSSLPBEParametersGenerator; +import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.jcajce.util.JcaJceHelper; +import org.spongycastle.openssl.EncryptionException; +import org.spongycastle.openssl.PEMException; +import org.spongycastle.util.Integers; + +class PEMUtilities +{ + private static final Map KEYSIZES = new HashMap(); + private static final Set PKCS5_SCHEME_1 = new HashSet(); + private static final Set PKCS5_SCHEME_2 = new HashSet(); + + static + { + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC); + + PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2); + PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC); + + KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256)); + } + + static int getKeySize(String algorithm) + { + if (!KEYSIZES.containsKey(algorithm)) + { + throw new IllegalStateException("no key size for algorithm: " + algorithm); + } + + return ((Integer)KEYSIZES.get(algorithm)).intValue(); + } + + static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid) + { + return PKCS5_SCHEME_1.contains(algOid); + } + + static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid) + { + return PKCS5_SCHEME_2.contains(algOid); + } + + public static boolean isPKCS12(ASN1ObjectIdentifier algOid) + { + return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId()); + } + + public static SecretKey generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount) + { + PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(); + + generator.init( + PBEParametersGenerator.PKCS5PasswordToBytes(password), + salt, + iterationCount); + + return new SecretKeySpec(((KeyParameter)generator.generateDerivedParameters(PEMUtilities.getKeySize(algorithm))).getKey(), algorithm); + } + + static byte[] crypt( + boolean encrypt, + JcaJceHelper helper, + byte[] bytes, + char[] password, + String dekAlgName, + byte[] iv) + throws PEMException + { + AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv); + String alg; + String blockMode = "CBC"; + String padding = "PKCS5Padding"; + Key sKey; + + // Figure out block mode and padding. + if (dekAlgName.endsWith("-CFB")) + { + blockMode = "CFB"; + padding = "NoPadding"; + } + if (dekAlgName.endsWith("-ECB") || + "DES-EDE".equals(dekAlgName) || + "DES-EDE3".equals(dekAlgName)) + { + // ECB is actually the default (though seldom used) when OpenSSL + // uses DES-EDE (des2) or DES-EDE3 (des3). + blockMode = "ECB"; + paramSpec = null; + } + if (dekAlgName.endsWith("-OFB")) + { + blockMode = "OFB"; + padding = "NoPadding"; + } + + + // Figure out algorithm and key size. + if (dekAlgName.startsWith("DES-EDE")) + { + alg = "DESede"; + // "DES-EDE" is actually des2 in OpenSSL-speak! + // "DES-EDE3" is des3. + boolean des2 = !dekAlgName.startsWith("DES-EDE3"); + sKey = getKey(password, alg, 24, iv, des2); + } + else if (dekAlgName.startsWith("DES-")) + { + alg = "DES"; + sKey = getKey(password, alg, 8, iv); + } + else if (dekAlgName.startsWith("BF-")) + { + alg = "Blowfish"; + sKey = getKey(password, alg, 16, iv); + } + else if (dekAlgName.startsWith("RC2-")) + { + alg = "RC2"; + int keyBits = 128; + if (dekAlgName.startsWith("RC2-40-")) + { + keyBits = 40; + } + else if (dekAlgName.startsWith("RC2-64-")) + { + keyBits = 64; + } + sKey = getKey(password, alg, keyBits / 8, iv); + if (paramSpec == null) // ECB block mode + { + paramSpec = new RC2ParameterSpec(keyBits); + } + else + { + paramSpec = new RC2ParameterSpec(keyBits, iv); + } + } + else if (dekAlgName.startsWith("AES-")) + { + alg = "AES"; + byte[] salt = iv; + if (salt.length > 8) + { + salt = new byte[8]; + System.arraycopy(iv, 0, salt, 0, 8); + } + + int keyBits; + if (dekAlgName.startsWith("AES-128-")) + { + keyBits = 128; + } + else if (dekAlgName.startsWith("AES-192-")) + { + keyBits = 192; + } + else if (dekAlgName.startsWith("AES-256-")) + { + keyBits = 256; + } + else + { + throw new EncryptionException("unknown AES encryption with private key"); + } + sKey = getKey(password, "AES", keyBits / 8, salt); + } + else + { + throw new EncryptionException("unknown encryption with private key"); + } + + String transformation = alg + "/" + blockMode + "/" + padding; + + try + { + Cipher c = helper.createCipher(transformation); + int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; + + if (paramSpec == null) // ECB block mode + { + c.init(mode, sKey); + } + else + { + c.init(mode, sKey, paramSpec); + } + return c.doFinal(bytes); + } + catch (Exception e) + { + throw new EncryptionException("exception using cipher - please check password and data.", e); + } + } + + private static SecretKey getKey( + char[] password, + String algorithm, + int keyLength, + byte[] salt) + { + return getKey(password, algorithm, keyLength, salt, false); + } + + private static SecretKey getKey( + char[] password, + String algorithm, + int keyLength, + byte[] salt, + boolean des2) + { + OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator(); + + pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt); + + KeyParameter keyParam; + keyParam = (KeyParameter) pGen.generateDerivedParameters(keyLength * 8); + byte[] key = keyParam.getKey(); + if (des2 && key.length >= 24) + { + // For DES2, we must copy first 8 bytes into the last 8 bytes. + System.arraycopy(key, 0, key, 16, 8); + } + return new SecretKeySpec(key, algorithm); + } +} |