aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public_key/doc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public_key/doc')
-rw-r--r--lib/public_key/doc/src/cert_records.xml45
-rw-r--r--lib/public_key/doc/src/introduction.xml20
-rw-r--r--lib/public_key/doc/src/part.xml8
-rw-r--r--lib/public_key/doc/src/public_key.xml164
-rw-r--r--lib/public_key/doc/src/public_key_records.xml12
-rw-r--r--lib/public_key/doc/src/using_public_key.xml8
6 files changed, 186 insertions, 71 deletions
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index b1d2a05200..f01f7dbaf5 100644
--- a/lib/public_key/doc/src/cert_records.xml
+++ b/lib/public_key/doc/src/cert_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,12 +34,11 @@
<file>cert_records.xml</file>
</header>
- <p>This chapter briefly describes erlang records derived from asn1
- specifications used to handle X509 certificates. The intent is to
- describe the data types and not to specify the meaning of each
+ <p>This chapter briefly describes erlang records derived from ASN1
+ specifications used to handle <c> X509 certificates</c> and <c>CertificationRequest</c>.
+ The intent is to describe the data types and not to specify the meaning of each
component for this we refer you to <url
- href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url>. Also
- descirbed is <p>CertificationRequest</p> that is defined by
+ href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url> and
<url href="http://www.rsa.com/rsalabs/node.asp?id=2124">PKCS-10</url>.
</p>
@@ -48,7 +47,7 @@
<code> -include_lib("public_key/include/public_key.hrl"). </code>
- <p>The used asn1 specifications are available <c>asn1</c> subdirectory
+ <p>The used ASN1 specifications are available <c>asn1</c> subdirectory
of the application <c>public_key</c>.
</p>
@@ -62,7 +61,7 @@
follows here.</p>
<p><c>oid() - a tuple of integers
- as generated by the asn1 compiler.</c></p>
+ as generated by the ASN1 compiler.</c></p>
<p><c>time() = uct_time() | general_time()</c></p>
@@ -101,7 +100,7 @@
#'Certificate'{
tbsCertificate, % #'TBSCertificate'{}
signatureAlgorithm, % #'AlgorithmIdentifier'{}
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}.
#'TBSCertificate'{
@@ -119,7 +118,7 @@
#'AlgorithmIdentifier'{
algorithm, % oid()
- parameters % asn1_der_encoded()
+ parameters % der_encoded()
}.
</code>
@@ -127,7 +126,7 @@
#'OTPCertificate'{
tbsCertificate, % #'OTPTBSCertificate'{}
signatureAlgorithm, % #'SignatureAlgorithm'
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}.
#'OTPTBSCertificate'{
@@ -290,7 +289,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'Extension'{
extnID, % id_extensions() | oid()
critical, % boolean()
- extnValue % asn1_der_encoded()
+ extnValue % der_encoded()
}.
</code>
@@ -372,7 +371,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
<row>
<cell align="left" valign="middle">id-ce-cRLDistributionPoints</cell>
- <cell align="left" valign="middle">#'DistributionPoint'{}</cell>
+ <cell align="left" valign="middle">[#'DistributionPoint'{}]</cell>
</row>
<row>
@@ -461,7 +460,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'Attribute'{
type, % oid()
- values % [asn1_der_encoded()]
+ values % [der_encoded()]
}).
#'BasicConstraints'{
@@ -486,9 +485,10 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
}).
#'DistributionPoint'{
- distributionPoint, % general_name() | [#AttributeTypeAndValue{}]
+ distributionPoint, % {fullName, [general_name()]} | {nameRelativeToCRLIssuer,
+ [#AttributeTypeAndValue{}]}
reasons, % [dist_reason()]
- cRLIssuer % general_name()
+ cRLIssuer % [general_name()]
}).
</code>
@@ -530,7 +530,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'CertificateList'{
tbsCertList, % #'TBSCertList{}
signatureAlgorithm, % #'AlgorithmIdentifier'{}
- signature % {0, binary()} - asn1 compact bitstring
+ signature % {0, binary()} - ASN1 compact bitstring
}).
#'TBSCertList'{
@@ -589,7 +589,8 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
<code>
#'IssuingDistributionPoint'{
- distributionPoint, % general_name() | [#AttributeTypeAndValue'{}]
+ distributionPoint, % {fullName, [general_name()]} | {nameRelativeToCRLIssuer,
+ [#AttributeTypeAndValue'{}]}
onlyContainsUserCerts, % boolean()
onlyContainsCACerts, % boolean()
onlySomeReasons, % [dist_reason()]
@@ -641,7 +642,7 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'CertificationRequest'{
certificationRequestInfo #'CertificationRequestInfo'{},
signatureAlgorithm #'CertificationRequest_signatureAlgorithm'{}}.
- signature {0, binary()} - asn1 compact bitstring
+ signature {0, binary()} - ASN1 compact bitstring
}
#'CertificationRequestInfo'{
@@ -653,17 +654,17 @@ oid names see table below. Ex: ?'id-dsa-with-sha1'</p>
#'CertificationRequestInfo_subjectPKInfo'{
algorithm #'CertificationRequestInfo_subjectPKInfo_algorithm'{}
- subjectPublicKey {0, binary()} - asn1 compact bitstring
+ subjectPublicKey {0, binary()} - ASN1 compact bitstring
}
#'CertificationRequestInfo_subjectPKInfo_algorithm'{
algorithm = oid(),
- parameters = asn1_der_encoded()
+ parameters = der_encoded()
}
#'CertificationRequest_signatureAlgorithm'{
algorithm = oid(),
- parameters = asn1_der_encoded()
+ parameters = der_encoded()
}
</code>
</section>
diff --git a/lib/public_key/doc/src/introduction.xml b/lib/public_key/doc/src/introduction.xml
index e0dd5603f7..4b59cc2245 100644
--- a/lib/public_key/doc/src/introduction.xml
+++ b/lib/public_key/doc/src/introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2012</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -36,11 +36,13 @@
<section>
<title>Purpose</title>
- <p> This application provides an API to public key infrastructure
- from <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC
- 5280</url> (X.509 certificates) and public key formats defined by
- the <url href="http://www.rsa.com/rsalabs/node.asp?id=2124">
- PKCS-standard</url></p>
+ <p> public_key deals with public key related file formats, digital
+ signatures and <url href="http://www.ietf.org/rfc/rfc5280.txt">
+ X-509 certificates</url>. It is a library application that
+ provides encode/decode, sign/verify, encrypt/decrypt and similar
+ functionality, it does not read or write files it expects or returns
+ file contents or partial file contents as binaries.
+ </p>
</section>
<section>
@@ -51,9 +53,9 @@
<section>
<title>Performance tips</title>
- <p>The public_key decode and encode functions will try to use the nifs
- which are in the asn1 compilers runtime modules if they can be found.
- So for the best performance you want to have the asn1 application in the
+ <p>The public_key decode and encode functions will try to use the NIFs
+ which are in the ASN1 compilers runtime modules if they can be found.
+ So for the best performance you want to have the ASN1 application in the
path of your system. </p>
</section>
diff --git a/lib/public_key/doc/src/part.xml b/lib/public_key/doc/src/part.xml
index ea3123b5bd..08fa4eec58 100644
--- a/lib/public_key/doc/src/part.xml
+++ b/lib/public_key/doc/src/part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,8 +32,10 @@
</header>
<description>
<p> This application provides an API to public key infrastructure
- from RFC 3280 (X.509 certificates) and some public key formats defined
- by the PKCS-standard. </p>
+ from <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC
+ 5280</url> (X.509 certificates) and public key formats defined by
+ the <url href="http://www.rsa.com/rsalabs/node.asp?id=2124">
+ PKCS-standard</url></p>
</description>
<xi:include href="introduction.xml"/>
<xi:include href="public_key_records.xml"/>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index b240d53571..66c9217579 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -33,12 +33,30 @@
<module>public_key</module>
<modulesummary> API module for public key infrastructure.</modulesummary>
<description>
- <p>This module provides functions to handle public key infrastructure
- from <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url>- X.509 certificates and some parts of the PKCS-standard.
+ <p>This module provides functions to handle public key infrastructure. It can
+ encode/decode different file formats (PEM, openssh), sign and verify digital signatures and vlidate
+ certificate paths and certificate revokation lists.
</p>
</description>
<section>
+ <title>public_key</title>
+
+ <list type="bulleted">
+ <item>public_key requires the crypto application.</item>
+
+ <item>Supports <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> -
+ Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile </item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2125"> PKCS-1 </url> - RSA Cryptography Standard </item>
+ <item>Supports <url href="http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf"> DSA</url>- Digital Signature Algorithm</item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2126"> PKCS-3 </url> - Diffie-Hellman Key Agreement Standard </item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2127"> PKCS-5</url> - Password-Based Cryptography Standard </item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2130"> PKCS-8</url> - Private-Key Information Syntax Standard</item>
+ <item>Supports <url href="http://www.rsa.com/rsalabs/node.asp?id=2132"> PKCS-10</url> - Certification Request Syntax Standard</item>
+ </list>
+ </section>
+
+ <section>
<title>COMMON DATA TYPES </title>
<note><p>All records used in this manual
@@ -58,7 +76,9 @@
<p><code>boolean() = true | false</code></p>
- <p><code>string = [bytes()]</code></p>
+ <p><code>string() = [bytes()]</code></p>
+
+ <p><code>der_encoded() = binary()</code></p>
<p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' |
'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo' |
@@ -87,6 +107,9 @@
<p><code> dss_digest_type() = 'sha' </code></p>
+ <p><code> crl_reason() = unspecified | keyCompromise | cACompromise | affiliationChanged | superseded | cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise
+ </code></p>
+
<p><code> ssh_file() = openssh_public_key | rfc4716_public_key |
known_hosts | auth_keys </code></p>
@@ -151,7 +174,7 @@
<func>
<name>der_decode(Asn1type, Der) -> term()</name>
- <fsummary> Decodes a public key asn1 der encoded entity.</fsummary>
+ <fsummary> Decodes a public key ASN.1 DER encoded entity.</fsummary>
<type>
<v>Asn1Type = atom()</v>
<d> ASN.1 type present in the public_key applications
@@ -159,7 +182,7 @@
<v>Der = der_encoded()</v>
</type>
<desc>
- <p> Decodes a public key ASN.1 der encoded entity.</p>
+ <p> Decodes a public key ASN.1 DER encoded entity.</p>
</desc>
</func>
@@ -181,14 +204,14 @@
<func>
<name>pem_decode(PemBin) -> [pem_entry()]</name>
<fsummary>Decode PEM binary data and return
- entries as ASN.1 der encoded entities. </fsummary>
+ entries as ASN.1 DER encoded entities. </fsummary>
<type>
<v>PemBin = binary()</v>
<d>Example {ok, PemBin} = file:read_file("cert.pem").</d>
</type>
<desc>
<p>Decode PEM binary data and return
- entries as ASN.1 der encoded entities.</p>
+ entries as ASN.1 DER encoded entities.</p>
</desc>
</func>
@@ -212,8 +235,8 @@
<v> Password = string() </v>
</type>
<desc>
- <p>Decodes a pem entry. pem_decode/1 returns a list of pem
- entries. Note that if the pem entry is of type
+ <p>Decodes a PEM entry. pem_decode/1 returns a list of PEM
+ entries. Note that if the PEM entry is of type
'SubjectPublickeyInfo' it will be further decoded to an
rsa_public_key() or dsa_public_key().</p>
</desc>
@@ -222,7 +245,7 @@
<func>
<name>pem_entry_encode(Asn1Type, Entity) -> pem_entry()</name>
<name>pem_entry_encode(Asn1Type, Entity, {CipherInfo, Password}) -> pem_entry()</name>
- <fsummary> Creates a pem entry that can be fed to pem_encode/1.</fsummary>
+ <fsummary> Creates a PEM entry that can be fed to pem_encode/1.</fsummary>
<type>
<v>Asn1Type = pki_asn1_type()</v>
<v>Entity = term()</v>
@@ -236,7 +259,7 @@
<v>Password = string()</v>
</type>
<desc>
- <p> Creates a pem entry that can be feed to pem_encode/1.</p>
+ <p> Creates a PEM entry that can be feed to pem_encode/1.</p>
</desc>
</func>
@@ -266,12 +289,12 @@
<func>
<name>pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name>
- <fsummary> Decodes an ASN.1 der encoded pkix x509 certificate.</fsummary>
+ <fsummary> Decodes an ASN.1 DER encoded PKIX x509 certificate.</fsummary>
<type>
<v>Cert = der_encoded()</v>
</type>
<desc>
- <p>Decodes an ASN.1 der encoded pkix certificate. The otp option
+ <p>Decodes an ASN.1 DER encoded PKIX certificate. The otp option
will use the customized ASN.1 specification OTP-PKIX.asn1 for
decoding and also recursively decode most of the standard
parts.</p>
@@ -280,14 +303,15 @@
<func>
<name>pkix_encode(Asn1Type, Entity, otp | plain) -> der_encoded()</name>
- <fsummary>Der encodes a pkix x509 certificate or part of such a
+ <fsummary>DER encodes a PKIX x509 certificate or part of such a
certificate.</fsummary>
<type>
<v>Asn1Type = atom()</v>
<d>The ASN.1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .</d>
+ <v>Entity = #'Certificate'{} | #'OTPCertificate'{} | a valid subtype</v>
</type>
<desc>
- <p>Der encodes a pkix x509 certificate or part of such a
+ <p>DER encodes a PKIX x509 certificate or part of such a
certificate. This function must be used for encoding certificates or parts of certificates
that are decoded/created in the otp format, whereas for the plain format this
function will directly call der_encode/2. </p>
@@ -357,18 +381,104 @@
</desc>
</func>
- <!-- <func> -->
- <!-- <name>pkix_path_validation()</name> -->
- <!-- <fsummary> Performs a basic path validation according to RFC 5280.</fsummary> -->
- <!-- <type> -->
- <!-- <v></v> -->
- <!-- </type> -->
- <!-- <desc> -->
- <!-- <p> Performs a basic path validation according to RFC 5280.</p> -->
- <!-- </desc> -->
- <!-- </func> -->
+ <func>
+ <name>pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, {PublicKeyInfo, PolicyTree}} | {error, {bad_cert, Reason}} </name>
+ <fsummary> Performs a basic path validation according to RFC 5280.</fsummary>
+ <type>
+ <v> TrustedCert = #'OTPCertificate'{} | der_encode() | unknown_ca | selfsigned_peer </v>
+ <d>Normally a trusted certificate but it can also be one of the path validation
+ errors <c>unknown_ca </c> or <c>selfsigned_peer </c> that can be discovered while
+ constructing the input to this function and that should be run through the <c>verify_fun</c>.</d>
+ <v> CertChain = [der_encode()]</v>
+ <d>A list of DER encoded certificates in trust order ending with the peer certificate.</d>
+ <v> Options = proplists:proplists()</v>
+ <v>PublicKeyInfo = {?'rsaEncryption' | ?'id-dsa',
+ rsa_public_key() | integer(), 'NULL' | 'Dss-Parms'{}}</v>
+ <v> PolicyTree = term() </v>
+ <d>At the moment this will always be an empty list as Policies are not currently supported</d>
+ <v> Reason = cert_expired | invalid_issuer | invalid_signature | unknown_ca |
+ selfsigned_peer | name_not_permitted | missing_basic_constraint | invalid_key_usage | crl_reason()
+ </v>
+ </type>
+ <desc>
+ <p>
+ Performs a basic path validation according to
+ <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280.</url>
+ However CRL validation is done separately by <seealso
+ marker="public_key#pkix_crls_validate-3">pkix_crls_validate/3 </seealso> and should be called
+ from the supplied <c>verify_fun</c>
+ </p>
+
+ <taglist>
+ <p> Available options are: </p>
+
+ <tag>{verify_fun, fun()}</tag>
+ <item>
+ <p>The fun should be defined as:</p>
+
+ <code>
+fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
+ {extension, #'Extension'{}},
+ InitialUserState :: term()) ->
+ {valid, UserState :: term()} | {valid_peer, UserState :: term()} |
+ {fail, Reason :: term()} | {unknown, UserState :: term()}.
+ </code>
+
+ <p>If the verify callback fun returns {fail, Reason}, the
+ verification process is immediately stopped. If the verify
+ callback fun returns {valid, UserState}, the verification
+ process is continued, this can be used to accept specific path
+ validation errors such as <c>selfsigned_peer</c> as well as
+ verifying application specific extensions. If called with an
+ extension unknown to the user application the return value
+ {unknown, UserState} should be used.</p>
+
+ </item>
+ <tag>{max_path_length, integer()}</tag>
+ <item>
+ The <c>max_path_length</c> is the maximum number of non-self-issued
+ intermediate certificates that may follow the peer certificate
+ in a valid certification path. So if <c>max_path_length</c> is 0 the PEER must
+ be signed by the trusted ROOT-CA directly, if 1 the path can
+ be PEER, CA, ROOT-CA, if it is 2 PEER, CA, CA, ROOT-CA and so
+ on.
+ </item>
+ </taglist>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_crls_validate(OTPCertificate, DPAndCRLs, Options) -> CRLStatus()</name>
+ <fsummary> Performs CRL validation.</fsummary>
+ <type>
+ <v> OTPCertificate = #'OTPCertificate'{}</v>
+ <v> DPAndCRLs = [{DP::#'DistributionPoint'{} ,CRL::#'CertificateList'{}}] </v>
+ <v> Options = proplists:proplists()</v>
+ <v> CRLStatus() = valid | {bad_cert, revocation_status_undetermined} |
+ {bad_cert, {revoked, crl_reason()}}</v>
+ </type>
+ <desc>
+ <p> Performs CRL validation. It is intended to be called from
+ the verify fun of <seealso marker="public_key#pkix_path_validation-3"> pkix_path_validation/3
+ </seealso></p>
+ <taglist>
+ <p> Available options are: </p>
+ <tag>{update_crl, fun()}</tag>
+ <item>
+ <p>The fun has the following type spec:</p>
+
+ <code> fun(#'DistributionPoint'{}, #'CertificateList'{}) -> #'CertificateList'{}</code>
+
+ <p>The fun should use the information in the distribution point to acesses
+ the lates possible version of the CRL. If this fun is not specified
+ public_key will use the default implementation:
+ </p>
+ <code> fun(_DP, CRL) -> CRL end</code>
+ </item>
+ </taglist>
+ </desc>
+ </func>
-
<func>
<name>pkix_sign(#'OTPTBSCertificate'{}, Key) -> der_encode()</name>
<fsummary>Signs certificate.</fsummary>
@@ -389,7 +499,7 @@
<v>Key = rsa_public_key() | dsa_public_key()</v>
</type>
<desc>
- <p> Verify pkix x.509 certificate signature.</p>
+ <p> Verify PKIX x.509 certificate signature.</p>
</desc>
</func>
diff --git a/lib/public_key/doc/src/public_key_records.xml b/lib/public_key/doc/src/public_key_records.xml
index bb90290266..e39ad0ec64 100644
--- a/lib/public_key/doc/src/public_key_records.xml
+++ b/lib/public_key/doc/src/public_key_records.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2008</year>
- <year>2011</year>
+ <year>2013</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,7 +34,7 @@
<file>public_key_records.xml</file>
</header>
- <p>This chapter briefly describes Erlang records derived from asn1
+ <p>This chapter briefly describes Erlang records derived from ASN1
specifications used to handle public and private keys. The intent
is to describe the data types and not to specify the meaning of
each component for this we refer you to the relevant standards and RFCs.</p>
@@ -67,9 +67,9 @@
}.
#'OtherPrimeInfo'{
- prime, % integer()
- exponent, % integer()
- coefficient % integer()
+ prime, % integer()
+ exponent, % integer()
+ coefficient % integer()
}.
</code>
diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml
index f0eaeb8654..5d9f1536d9 100644
--- a/lib/public_key/doc/src/using_public_key.xml
+++ b/lib/public_key/doc/src/using_public_key.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2011</year><year>2011</year>
+ <year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,7 +21,7 @@
</legalnotice>
- <title>Using the public_key API</title>
+ <title>Getting Started</title>
<file>using_public_key.xml</file>
</header>
@@ -40,7 +40,7 @@
<section>
<title>PEM files</title>
- <p> Pulic key data (keys, certificates etc) may be stored in PEM format. PEM files
+ <p> Public key data (keys, certificates etc) may be stored in PEM format. PEM files
comes from the Private Enhanced Mail Internet standard and has a
structure that looks like this:</p>
@@ -63,7 +63,7 @@
<code>1> {ok, PemBin} = file:read_file("dsa.pem").
{ok,&lt;&lt;"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...&gt;&gt;}</code>
- <p>This PEM file only has one entry a private DSA key.</p>
+ <p>This PEM file only has one entry, a private DSA key.</p>
<code>2> [DSAEntry] = public_key:pem_decode(PemBin).
[{'DSAPrivateKey',&lt;&lt;48,130,1,187,2,1,0,2,129,129,0,183,
179,230,217,37,99,144,157,21,228,204,