summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2018-09-14 17:54:02 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2018-09-14 17:54:02 +0700
commit297ccc3cc304defd9ecb0ba944bede4f89696b3b (patch)
treecd76cba6a46d74fa58a4b07af0221040840a3ce7
parentRFC 8032: Implement Ed25519ctx, Ed25519ph, Ed448ph variants (diff)
downloadBouncyCastle.NET-ed25519-297ccc3cc304defd9ecb0ba944bede4f89696b3b.tar.xz
RFC 5958: Update PrivateKeyInfo
- now supports optional 'publicKey' field
-rw-r--r--crypto/src/asn1/pkcs/PrivateKeyInfo.cs193
-rw-r--r--crypto/src/util/collections/CollectionUtilities.cs8
2 files changed, 139 insertions, 62 deletions
diff --git a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
index c5be7a315..dfb332fdd 100644
--- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
+++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
@@ -4,15 +4,55 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Asn1.Pkcs
 {
+    /**
+     *  RFC 5958
+     *
+     *  <pre>
+     *  [IMPLICIT TAGS]
+     *
+     *  OneAsymmetricKey ::= SEQUENCE {
+     *      version                   Version,
+     *      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
+     *      privateKey                PrivateKey,
+     *      attributes            [0] Attributes OPTIONAL,
+     *      ...,
+     *      [[2: publicKey        [1] PublicKey OPTIONAL ]],
+     *      ...
+     *  }
+     *
+     *  PrivateKeyInfo ::= OneAsymmetricKey
+     *
+     *  Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2)
+     *
+     *  PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+     *                                     { PUBLIC-KEY,
+     *                                       { PrivateKeyAlgorithms } }
+     *
+     *  PrivateKey ::= OCTET STRING
+     *                     -- Content varies based on type of key.  The
+     *                     -- algorithm identifier dictates the format of
+     *                     -- the key.
+     *
+     *  PublicKey ::= BIT STRING
+     *                     -- Content varies based on type of key.  The
+     *                     -- algorithm identifier dictates the format of
+     *                     -- the key.
+     *
+     *  Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+     *  </pre>
+     */
     public class PrivateKeyInfo
         : Asn1Encodable
     {
-        private readonly Asn1OctetString        privKey;
-        private readonly AlgorithmIdentifier	algID;
-        private readonly Asn1Set				attributes;
+        private readonly DerInteger version;
+        private readonly AlgorithmIdentifier privateKeyAlgorithm;
+        private readonly Asn1OctetString privateKey;
+        private readonly Asn1Set attributes;
+        private readonly DerBitString publicKey;
 
         public static PrivateKeyInfo GetInstance(Asn1TaggedObject obj, bool explicitly)
         {
@@ -25,110 +65,139 @@ namespace Org.BouncyCastle.Asn1.Pkcs
             if (obj == null)
                 return null;
             if (obj is PrivateKeyInfo)
-                return (PrivateKeyInfo) obj;
+                return (PrivateKeyInfo)obj;
             return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj));
         }
 
-        public PrivateKeyInfo(AlgorithmIdentifier algID, Asn1Encodable privateKey)
-            : this(algID, privateKey, null)
+        private static int GetVersionValue(DerInteger version)
+        {
+            BigInteger bigValue = version.Value;
+            if (bigValue.CompareTo(BigInteger.Zero) < 0 || bigValue.CompareTo(BigInteger.One) > 0)
+                throw new ArgumentException("invalid version for private key info", "version");
+
+            return bigValue.IntValue;
+        }
+
+        public PrivateKeyInfo(
+            AlgorithmIdentifier privateKeyAlgorithm,
+            Asn1Encodable privateKey)
+            : this(privateKeyAlgorithm, privateKey, null, null)
         {
         }
 
         public PrivateKeyInfo(
-            AlgorithmIdentifier	algID,
-            Asn1Encodable       privateKey,
-            Asn1Set				attributes)
+            AlgorithmIdentifier privateKeyAlgorithm,
+            Asn1Encodable privateKey,
+            Asn1Set attributes)
+            : this(privateKeyAlgorithm, privateKey, attributes, null)
         {
-            this.algID = algID;
-            this.privKey = new DerOctetString(privateKey.GetEncoded(Asn1Encodable.Der));
+        }
+
+        public PrivateKeyInfo(
+            AlgorithmIdentifier privateKeyAlgorithm,
+            Asn1Encodable privateKey,
+            Asn1Set attributes,
+            byte[] publicKey)
+        {
+            this.version = new DerInteger(publicKey != null ? BigInteger.One : BigInteger.Zero);
+            this.privateKeyAlgorithm = privateKeyAlgorithm;
+            this.privateKey = new DerOctetString(privateKey);
             this.attributes = attributes;
+            this.publicKey = publicKey == null ? null : new DerBitString(publicKey);
         }
 
         private PrivateKeyInfo(Asn1Sequence seq)
         {
             IEnumerator e = seq.GetEnumerator();
 
-            e.MoveNext();
-            BigInteger version = ((DerInteger)e.Current).Value;
-            if (version.IntValue != 0)
-            {
-                throw new ArgumentException("wrong version for private key info: " + version.IntValue);
-            }
+            this.version = DerInteger.GetInstance(CollectionUtilities.RequireNext(e));
 
-            e.MoveNext();
-            algID = AlgorithmIdentifier.GetInstance(e.Current);
-            e.MoveNext();
-            privKey = Asn1OctetString.GetInstance(e.Current);
+            int versionValue = GetVersionValue(version);
 
-            if (e.MoveNext())
+            this.privateKeyAlgorithm = AlgorithmIdentifier.GetInstance(CollectionUtilities.RequireNext(e));
+            this.privateKey = Asn1OctetString.GetInstance(CollectionUtilities.RequireNext(e));
+
+            int lastTag = -1;
+            while (e.MoveNext())
             {
-                attributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false);
+                Asn1TaggedObject tagged = (Asn1TaggedObject)e.Current;
+
+                int tag = tagged.TagNo;
+                if (tag <= lastTag)
+                    throw new ArgumentException("invalid optional field in private key info", "seq");
+
+                lastTag = tag;
+
+                switch (tag)
+                {
+                case 0:
+                {
+                    this.attributes = Asn1Set.GetInstance(tagged, false);
+                    break;
+                }
+                case 1:
+                {
+                    if (versionValue < 1)
+                        throw new ArgumentException("'publicKey' requires version v2(1) or later", "seq");
+
+                    this.publicKey = DerBitString.GetInstance(tagged, false);
+                    break;
+                }
+                default:
+                {
+                    throw new ArgumentException("unknown optional field in private key info", "seq");
+                }
+                }
             }
         }
 
-        public virtual AlgorithmIdentifier PrivateKeyAlgorithm
+        public virtual Asn1Set Attributes
         {
-            get { return algID; }
+            get { return attributes; }
         }
 
-        [Obsolete("Use 'PrivateKeyAlgorithm' property instead")]
-        public virtual AlgorithmIdentifier AlgorithmID
+        /// <summary>Return true if a public key is present, false otherwise.</summary>
+        public virtual bool HasPublicKey
         {
-            get { return algID; }
+            get { return publicKey != null; }
+        }
+
+        public virtual AlgorithmIdentifier PrivateKeyAlgorithm
+        {
+            get { return privateKeyAlgorithm; }
         }
 
         public virtual Asn1Object ParsePrivateKey()
         {
-            return Asn1Object.FromByteArray(privKey.GetOctets());
+            return Asn1Object.FromByteArray(privateKey.GetOctets());
         }
 
-        [Obsolete("Use 'ParsePrivateKey' instead")]
-        public virtual Asn1Object PrivateKey
+        /// <summary>For when the public key is an ASN.1 encoding.</summary>
+        public virtual Asn1Object ParsePublicKey()
         {
-            get
-            {
-                try
-                {
-                    return ParsePrivateKey();
-                }
-                catch (IOException)
-                {
-                    throw new InvalidOperationException("unable to parse private key");
-                }
-            }
+            return publicKey == null ? null : Asn1Object.FromByteArray(publicKey.GetOctets());
         }
 
-        public virtual Asn1Set Attributes
+        /// <summary>Return the public key as a raw bit string.</summary>
+        public virtual DerBitString PublicKeyData
         {
-            get { return attributes; }
+            get { return publicKey; }
         }
 
-        /**
-         * write out an RSA private key with its associated information
-         * as described in Pkcs8.
-         * <pre>
-         *      PrivateKeyInfo ::= Sequence {
-         *                              version Version,
-         *                              privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
-         *                              privateKey PrivateKey,
-         *                              attributes [0] IMPLICIT Attributes OPTIONAL
-         *                          }
-         *      Version ::= Integer {v1(0)} (v1,...)
-         *
-         *      PrivateKey ::= OCTET STRING
-         *
-         *      Attributes ::= Set OF Attr
-         * </pre>
-         */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(0), algID, privKey);
+            Asn1EncodableVector v = new Asn1EncodableVector(version, privateKeyAlgorithm, privateKey);
 
             if (attributes != null)
             {
                 v.Add(new DerTaggedObject(false, 0, attributes));
             }
 
+            if (publicKey != null)
+            {
+                v.Add(new DerTaggedObject(false, 1, publicKey));
+            }
+
             return new DerSequence(v);
         }
     }
diff --git a/crypto/src/util/collections/CollectionUtilities.cs b/crypto/src/util/collections/CollectionUtilities.cs
index 18fcb6774..cac158226 100644
--- a/crypto/src/util/collections/CollectionUtilities.cs
+++ b/crypto/src/util/collections/CollectionUtilities.cs
@@ -39,6 +39,14 @@ namespace Org.BouncyCastle.Utilities.Collections
             return new UnmodifiableSetProxy(s);
         }
 
+        public static object RequireNext(IEnumerator e)
+        {
+            if (!e.MoveNext())
+                throw new InvalidOperationException();
+
+            return e.Current;
+        }
+
         public static string ToString(IEnumerable c)
         {
             StringBuilder sb = new StringBuilder("[");